一.概述 System V三種IPC:消息隊列,信號量,共用記憶體。這三種IPC最先出現在AT&T System v UNIX上面,並遵循XSI標準,有時候也被稱為XSI IPC。本文先探討消息隊列:1.消息隊列允許進程以消息的形式交換數據。讀寫都是針對...
一.概述
System V三種IPC:消息隊列,信號量,共用記憶體。這三種IPC最先出現在AT&T System v UNIX上面,並遵循XSI標準,有時候也被稱為XSI IPC。
本文先探討消息隊列:
1.消息隊列允許進程以消息的形式交換數據。讀寫都是針對整條消息,不能讀寫消息的一部分,不像管道那樣可以以流的形式讀寫任意位元組。
2.消息隊列除了包含數據外,還有一個整數來表示該消息的類型。讀取消息的時候即可以按照先進先出方式讀取,也可以按照消息類型來讀取。
二.函數介面
1.創建一個消息隊列
1 #include <sys/msg.h> 2 3 int msgget(key_t key, int msgflg);
key:是一個整數,該函數會將key轉換成一個IPC標識符。key有3種方法定義:1.手動隨意指定一個整數。2.把IPC_PRIVATE當作key傳入,系統會自動生成。3.用ftok()函數。
msgflg:指定該消息的許可權,跟文件的許可權控制類似。
2.發送消息
1 #include <sys/msg.h> 2 3 int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
msqid:用msget()獲取的id。
mgsp:存儲消息的結構指針,下麵的mtype就是自定義的消息類型,mtext是消息數據。
1 struct msgbuf { 2 long mtype; /* message type, must be > 0 */ 3 char mtext[1]; /* message data */ 4 };
msgsz:消息的大小,對應上面msgbuf裡面的mtext。
msgflg:控制消息發送時異常狀況,如消息隊列滿。
3.接收消息
1 #include <sys/msg.h> 2 3 ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
msqid:用msget()獲取的id,或者已知的消息ID。
msgp,msgsz:同msgsnd()。
msgtyp:接收消息的類型,即msgbuf裡面的mtype。但還有別的用法:
如果為0,就獲取隊列中第一個可用消息。
大於0,獲取相同類型消息的第一個,即mtype。
小於0,獲取等於或小於mtype的絕對值第一個消息。等會我們一一做實驗。
msgflg:同msgsnd()。
4.消息控制
1 #include <sys/msg.h> 2 3 int msgctl(int msqid, int cmd, struct msqid_ds *buf);
cmd:有3個選項,IPC_STAT,IPC_SET,IPC_RMID。前2個是獲取和設置msgid對應的消息結構體,最後一個是刪除消息隊列。
三.簡單的例子
1.創建消息隊列
1 /** 2 * @file msg_create.c 3 */ 4 5 #include <stdio.h> 6 #include <stdlib.h> 7 #include <string.h> 8 #include <sys/msg.h> 9 10 void err_exit(const char *err_msg) 11 { 12 printf("%s error\n", err_msg); 13 exit(1); 14 } 15 16 int main(void) 17 { 18 int msg_id = msgget(IPC_PRIVATE, 0666 | IPC_CREAT); 19 if (msg_id == -1) 20 err_exit("msgget()"); 21 22 printf("create msg_id:%d\n", msg_id); 23 24 return 0; 25 }
2.發送消息
1 /** 2 * @file msg_send.c 3 */ 4 5 #include <stdio.h> 6 #include <stdlib.h> 7 #include <string.h> 8 #include <sys/msg.h> 9 10 #define MAX_BUFFER 1024 11 12 typedef struct 13 { 14 long msg_type; 15 char msg_text[MAX_BUFFER]; 16 } msg_t; 17 18 void err_exit(const char *err_msg) 19 { 20 printf("%s error\n", err_msg); 21 exit(1); 22 } 23 24 int main(int argc, char *argv[]) 25 { 26 if (argc < 4) 27 { 28 printf("usage: %s msg_id msg_type msg_text\n", argv[0]); 29 exit(1); 30 } 31 32 int msg_id = atoi(argv[1]); 33 msg_t send_msg; 34 char *text = argv[3]; 35 int text_len = strlen(text); 36 37 send_msg.msg_type = atoi(argv[2]); 38 memcpy((void *)send_msg.msg_text, text, text_len); 39 40 if (msgsnd(msg_id, &send_msg, text_len, 0) == -1) 41 err_exit("msgsnd()"); 42 43 return 0; 44 }
3.接收消息
1 /** 2 * @file msg_recv.c 3 */ 4 5 #include <stdio.h> 6 #include <stdlib.h> 7 #include <string.h> 8 #include <sys/msg.h> 9 10 #define MAX_BUFFER 1024 11 12 typedef struct 13 { 14 long msg_type; 15 char msg_text[MAX_BUFFER]; 16 } msg_t; 17 18 void err_exit(const char *err_msg) 19 { 20 printf("%s error\n", err_msg); 21 exit(1); 22 } 23 24 int main(int argc, const char *argv[]) 25 { 26 if (argc < 3) 27 { 28 printf("usage: %s msg_id msg_type\n", argv[0]); 29 exit(1); 30 } 31 32 int msg_id = atoi(argv[1]); 33 msg_t recv_msg; 34 long msg_type = atoi(argv[2]); 35 36 if (msgrcv(msg_id, &recv_msg, MAX_BUFFER, msg_type, 0) == -1) 37 err_exit("msgrcv()"); 38 39 printf("receive:%s\n", recv_msg.msg_text); 40 41 //if (msgctl(msg_id, IPC_RMID, 0) == -1) 42 // err_exit("msgctl()"); 43 44 return 0; 45 }
四.實驗
1.創建消息,編譯執行msg_create.c,用ipcs -q查看消息:
可以看到:msqid就是用IPC_PRIVATE當作key傳入,系統會自動生成的,msqid=262144等會接收消息要用。perms是我們代碼設置的許可權,此時的消息位元組和消息數都是0。
2.發送消息,編譯執行msg_send.c,併發消息,用ipcs -q查看消息:
上面./mes_send後面依次是:剛剛創建的消息隊列id,消息類型,消息數據。
接下來,我們再繼續發送1條1類型消息,2條2類型消息,2條3類型消息,等會接收消息做實驗。
現在我們有6條消息了。
3.接收消息,編譯msg_recv.c。我們主要來實驗msgrcv()裡面的msg_type參數。即該文件的第36行代碼。
3.1:當msg_type等於0時,獲取隊列中第一個可用消息。
可以看到,1234就是我剛剛第一次發送到該隊列的消息。
3.2:當msg_type大於0,獲取具有相同類型的第一個消息:
上面我們獲取的是3類型的消息,接收的剛好是第一次發送3號消息的haha。
3.3:當msg_type小於0,獲取等於或小於msg_type絕對值的第一個消息:
上面,-3的絕對值是3,而隊列中存在最先放進去的消息是1號消息22222(本來是1號的1234,剛剛我們做實驗時讀取走了,所以剩下它第一)。1小於3,所以1號消息被讀取。
4.消息隊列的刪除,msg_recv.c裡面第41行代碼,如果放開後編譯執行,收到一條消息後整個隊列全部刪除。