又到了IPC的時間了

今天要看的是msg queue

 

其實呢msg queue就如同他的名字一樣,是一個存放msg的queue

可以參考這裡:http://tldp.org/LDP/lpg/node27.html

基本上可以看做一個存在kernel中的list,然後每個list都會有一個特別的ID來辨識

有一點要特別注意的是,msgqueue建出來之後就會一直存在kernel中,就算建立它的process結束了也不會消失,除非把它移除或是重啟系統

 

那麼要如何建立一個msg queue呢

要使用msgget()這個API:

int msgget ( key_t key, int msgflg );

如果建立成功的話,就會回傳msgqueue的ID,失敗則回傳-1

參數部分嘛,第一個參數是key,前面提過每個msgqueue都是有一個特別的ID,是一個獨立的存在

這個key值就是系統用來辨認現在kernel中是否存在使用相同key值的msgqueue,當然是不能重複的

而這個key值可以用ftok()這個函式來取得,也可以用IPC_PRIVATE來保證一定會建出新的msgqueue

第二個參數是一個flag,主要有兩種:

1. IPC_CREAT

    大部分情況就是用這個,如果key值檢查沒問題,就會建出一個新的msgqueue,否則不會建立,然後如果msgqueue已經存在的話,並不會回傳失敗,會回傳已存在那個msgqueue的ID

2. IPC_EXCL

    比較少用到,要和IPC_CREAT一起使用,如果msqueueu已經存在的話會回傳失敗

 

再來看看怎麼傳資料到msgqueue裡面,主要要使用msgsnd()這個API:

int msgsnd ( int msqid, struct msgbuf *msgp, int msgsz, int msgflg );

成功回傳0,失敗回傳-1

第一個參數就是msgqueue的ID嘛,第二個就比較有趣了,就是要傳進去的struct

這是一個可以自己定義的struct,可以自己決定要存進去的資訊是什麼,比如說陣列或是數值之類的

例如說像這樣:

struct msgbuf {
    long mtype;         /* type of message */
    char mtext[1];      /* message text */
};

第一個mtype一定要是一個大於0的正數,也是個ㄧ定要存在的一個數值,其他內容就可以自訂了

但是它是有限定大小的,詳情可以到文件那邊看一看

第三個參數msgsz就是msgbuf的大小

最後一個也是個flag,可以設0表示什麼都不做

或是設成IPC_NOWAIT,表示如果queue的空間滿了,就會繼續執行下去;否則就會block在這邊直到可以寫資料進去為止

 

然後是接收,是用msgrcv()

int msgrcv ( int msqid, struct msgbuf *msgp, int msgsz, long mtype, int msgflg );

回傳值是寫進*msgp的bytes數

其他參數嘛,msgsz表示要接收的資料大小,注意這個大小是不包含mtype的,但msgsnd中的msgsz是有包含的

mtype的話是以下這個規則:

mtype == 0,回傳msgqueue中的第一個msg

mtype > 0,回傳msgqueue中msg type為mtype的第一個msg

mtype < 0,回傳msgqueue中msg type值小於或等於mtype絕對值的msg,如果有多個,則取type值最小的msg

最後msgflg一樣可以為0或是IPC_NOWAIT

如果msgflg為0,msgqueue中沒有msg可以讀取的時候,就會block在這裡直到有msg可以讀取為止

反之如果msflag為IPC_NOWAIT,就會出現ENOMSG的錯誤訊息,然後繼續執行下去

 

再來我們可以透過msgctl()來控制msgqueue:

int msgctl ( int msgqid, int cmd, struct msqid_ds *buf );

成功回傳0,失敗回傳-1

關於msgid_ds可以看一下http://tldp.org/LDP/lpg/node32.html#SECTION00742230000000000000,他是帶有msgqueue資訊的一個struct

第一個參數明顯是ID,第二個cmd是要執行的操作,然後會依據cmd把結果或是取得的資訊存在buf中

cmd大概有三種:

1. IPC_STAT:獲取msgqueue的狀態和資訊並存到buf中

2. IPC_SET:設定msgqueue裡的ipc_perm內容,關於ipc_perm可以看一下 http://tldp.org/LDP/lpg/node33.html#SECTION00742240000000000000,當然要設定的數值就是從buf去拿了

3. IPC_RMID:從kernel中移除這個msgqueue

嗯... 實際上一些使用範例都在文件裡有提到,這邊就不多寫了

依照慣例提供一個簡易的範例,然後我之後再來做實驗XD

範例來自 https://songlee24.github.io/2015/04/21/linux-IPC/

  • msg_server.c

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/msg.h>
    
    // 用于创建一个唯一的key
    #define MSG_FILE "/etc/passwd"
    
    // 消息结构
    struct msg_form {
    	long mtype;
    	char mtext[256];
    };
    
    int main()
    {
    	int msqid;
    	key_t key;
    	struct msg_form msg;
    	
    	// 获取key值
    	if((key = ftok(MSG_FILE,'z')) < 0)
    	{
    		perror("ftok error");
    		exit(1);
    	}
    
    	// 打印key值
    	printf("Message Queue - Server key is: %d.\n", key);
    
    	// 创建消息队列
    	if ((msqid = msgget(key, IPC_CREAT|0777)) == -1)
    	{
    		perror("msgget error");
    		exit(1);
    	}
    
    	// 打印消息队列ID及进程ID
    	printf("My msqid is: %d.\n", msqid);
    	printf("My pid is: %d.\n", getpid());
    
    	// 循环读取消息
    	for(;;) 
    	{
    		msgrcv(msqid, &msg, 256, 888, 0);// 返回类型为888的第一个消息
    		printf("Server: receive msg.mtext is: %s.\n", msg.mtext);
    		printf("Server: receive msg.mtype is: %d.\n", msg.mtype);
    
    		msg.mtype = 999; // 客户端接收的消息类型
    		sprintf(msg.mtext, "hello, I'm server %d", getpid());
    		msgsnd(msqid, &msg, sizeof(msg.mtext), 0);
    	}
    	return 0;
    }
    
  • msg_client.c

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/msg.h>
    
    // 用于创建一个唯一的key
    #define MSG_FILE "/etc/passwd"
    
    // 消息结构
    struct msg_form {
    	long mtype;
    	char mtext[256];
    };
    
    int main()
    {
    	int msqid;
    	key_t key;
    	struct msg_form msg;
    
    	// 获取key值
    	if ((key = ftok(MSG_FILE, 'z')) < 0) 
    	{
    		perror("ftok error");
    		exit(1);
    	}
    
    	// 打印key值
    	printf("Message Queue - Client key is: %d.\n", key);
    
    	// 打开消息队列
    	if ((msqid = msgget(key, IPC_CREAT|0777)) == -1) 
    	{
    		perror("msgget error");
    		exit(1);
    	}
    
    	// 打印消息队列ID及进程ID
        printf("My msqid is: %d.\n", msqid);
    	printf("My pid is: %d.\n", getpid());
    
    	// 添加消息,类型为888
    	msg.mtype = 888;
    	sprintf(msg.mtext, "hello, I'm client %d", getpid());
    	msgsnd(msqid, &msg, sizeof(msg.mtext), 0);
    
    	// 读取类型为777的消息
    	msgrcv(msqid, &msg, 256, 999, 0);
    	printf("Client: receive msg.mtext is: %s.\n", msg.mtext);
    	printf("Client: receive msg.mtype is: %d.\n", msg.mtype);
    	return 0;
    }
    

 

這個範例... 簡單說就是client傳過來然後server接收這樣

這種方法的好處嘛,我覺得就是可以自訂傳輸的型態和內容吧

相對於pipe(只能用於parent process)和FIFO(透過檔案的方式來傳輸,基本上內容就是char)

msgqueue可以自訂一些struct之類的型態去傳輸,在需要傳較複雜的資訊時會比較好用,可讀性也會相對高一點

 

好啦~ 下一篇應該會再介紹另外幾種方式:shared memory或是socket

期待下一篇~

arrow
arrow
    全站熱搜

    頁頁頁六滴 發表在 痞客邦 留言(0) 人氣()