又到了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
期待下一篇~
留言列表