1. 程式人生 > 其它 >linux c ipc機制

linux c ipc機制

ipc

ipc 意思就是 程序間通訊機制的簡稱
在linux(centos)環境下 使用 ipcs(資訊檢視),ipcrm(刪除), ipcmk(建立)
通過指令 ipcs 檢視, linux 支援的IPC機制有三種

  1. Message Queues: 訊息佇列
  2. Shared Memory Segments: 共享記憶體段
  3. Semaphore Arrays: 訊號量陣列
[root@process_comm#] ipcs 

------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages    
0x4201032a 0          root       666        184          2           

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status      

------ Semaphore Arrays --------
key        semid      owner      perms      nsems  

訊息佇列

程式碼示例

程式碼如下: 使用 gcc -o recv ipc_que_recv.c, gcc -o send ipc_que_send.c 編譯各自程式碼得到 recvsend,分別執行
ipc_que_recv.c

#include <stdio.h>
#include<errno.h>
#include<string.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>

int create_msg_queue() {
    /**
     * 該方法通過指定引數, 生成對應的 唯一key id標識
     * extern key_t ftok (const char *__pathname, int __proj_id) __THROW;
     * __pathname: 存在的路徑名或者檔案,必須存在,否則返回錯誤
     * __proj_id: 自己約定, 在unix中取值 1~255
     */
    key_t _key = ftok("temp113", 66);
    if(_key < 0) {
        printf("error to ftok \n");
        return;
    }
    printf("ftok _key: 0x%0x \n", _key);
    /**
     * 該方法主要是為了根據 __key 查詢訊息佇列是否已經建立
     * extern int msgget (key_t __key, int __msgflg) __THROW;
     * __key: 訊息佇列的物件的關鍵字(key)
     * __msgflg: 對條件進行判斷
     * 0666: 第一位表示 條件判斷的處理方法
     *       第二位表示 當前使用者的許可權, 讀.寫.可執行
     *       第三位表示 group使用者組許可權
     *       第四位表示 其它使用者的許可權
     * ret: -1 失敗, 許可權被拒絕
     *       0 成功  
     *  詳見: rttno-base.h
     * /proc/sys/kernel/msgmax  一個訊息的位元組大小
     * /proc/sys/kernel/msgmnb  存放訊息佇列的總位元組數
     * /proc/sys/kernel/msgmni  系統的訊息佇列數目
     * 檢視系統支援訊息佇列數量, 
    */  
    // int msgid = msgget(_key, IPC_CREAT | IPC_EXCL | 0666);
    int msgid = msgget(_key, IPC_CREAT | 0666);
    if(msgid < 0)
	{
		printf("error msgid %d \n", msgid);
        // errno 是記錄系統的最後一次錯誤程式碼
		printf("%d : %s\n", errno, strerror(errno));
		return 1;
	}
    printf("msgget msgid: 0x%0x \n", msgid);
    return msgid;
}
struct msgbuf
{
	long mtype;
	unsigned char buff[92];
};
int main(int argc, char const *argv[])
{
    int msg_id = create_msg_queue();
    struct msgbuf sbuf;
    sbuf.mtype = 0x01;
    int cnt = 0;
    while(1) {
        cnt ++;
        memset(sbuf.buff, 0x0, sizeof(sbuf.buff));
        /**
         * 該函式為接收佇列訊息
         * extern ssize_t msgrcv (int __msqid, void *__msgp, size_t __msgsz, long int __msgtyp, int __msgflg);
         * __msqid: msg_id
         * __msgp: 接收資料的緩衝區
         * __msgsz: 接收資料緩衝區的大小
         * __msgtyp: 接收資料的型別, 就是可以通過三四個以上的程序之間的通訊,用型別表示要傳送給指定的消費者
         *   0, 函式不做型別檢查返回佇列中最舊的內容
         *  >0, 接收型別等於 __msgtyp 的第一個訊息
         *  <0, 接收型別等於或者小於msgtyp絕對值的第一個訊息
         * __msgflg: 控制函式行為
         *  IPC_NOWAIT: 佇列為空,返回 ENOMSG( No message of desired type), 並將控制權交回呼叫函式的程序, 如果不指定, 那函式將一直處於阻塞狀態
         *  MSG_NOERROR: 如何函式取得的訊息長度大於 __msgsz, 將只返回 msgsz長度的資訊, 剩下的會被丟棄, 如果不指定這個函式, 將被返回狀態碼E2BIG(Argument list too long)
         *  IPC_EXCEPT: 與msgtype配合使用返回佇列中第一個型別不為msgtype的訊息
         * ret: -1, 讀取錯誤, 錯誤存在 error 中
         *       否則返回實際讀取的資料長度
         */
        int rlen = msgrcv(msg_id, &sbuf, sizeof(sbuf.buff), 0x02, 0);
        if(rlen < 0) {
            printf("%d : %s", errno, strerror(errno));
            return -1;
        } else {
            printf("[%d]%d:%0x %0x %0x %0x\n", cnt, rlen, sbuf.buff[0],sbuf.buff[20],sbuf.buff[90],sbuf.buff[91]);
        }

        usleep(1000*100);
    }
    return 0;
}

ipc_que_send.c

#include <stdio.h>
#include<errno.h>
#include<string.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>

int get_msg_queue() {
    key_t _key = ftok("temp113", 66); 
	if(_key < 0)
	{
		printf("%d : %s",errno,strerror(errno));
		return -1;
	}

	int msg_id = msgget(_key, IPC_CREAT);
    printf("msgid: %d \n", msg_id);
	return msg_id;
}
struct msgbuf
{
	long mtype;
	unsigned char buff[92];
};
int main(int argc, char const *argv[])
{
    int msg_id = get_msg_queue();
    struct msgbuf sbuf;
    memset(sbuf.buff, 'c', sizeof(sbuf.buff));
    unsigned char cnt = 0;
    sbuf.mtype = 0x02;
    sbuf.buff[0] = 0x0;
    sbuf.buff[91] = 'a';
    while (1)
    {
        sbuf.buff[0] = cnt;
        cnt++;
        /**
         * 該函式為向佇列傳送資料
         * extern int msgsnd (int __msqid, const void *__msgp, size_t __msgsz, int __msgflg);
         * __msgflg: 控制傳送函式的行為, IPC_NOWAIT,如果訊息佇列已滿,訊息將不被寫入佇列,控制權返回呼叫函式的執行緒。如果不指定這個引數,執行緒將被阻塞直到訊息被可以被寫入。
         * ret: 0 成功
         *     -1 errno
        */
        int res = msgsnd(msg_id, &sbuf, sizeof(sbuf.buff), 0);
        printf("[%d] res %d, rlen %d res: %0x %0x %0x %0x \n", cnt, res, sizeof(sbuf.buff),sbuf.buff[0], sbuf.buff[20], sbuf.buff[60], sbuf.buff[91]);
        sleep(1);
    }
    
    return 0;
}

實驗探索結論

1.當訊息佇列一旦建立之後, 他是存在於核心系統的, 不限於使用者態的程式, 也就是說使用者態的程式不管是java還是c, 不管是執行還是不執行, 不管是兩個程序還是更多的程序, 他都可以對訊息佇列進行收發資料的操作, 異或同一程序自發自收
2.訊息佇列在核心中的原始碼