概念: 進程彼此之間可以通過IPC消息進行通信。進程產生的每條消息都被發送到一個IPC消息隊列中,這條消息一直存放在隊列中,直到另一個進程將其讀走為止。
優點:可以通過發送消息來幾乎完全避免命名管道的同步和阻塞問題;消息隊列提供了一種從一個進程向另一個進程發送一個數據塊的方法。而且,每個數據塊被認為含有一個類型,接收進程可以獨立地接收含有不同類型值的數據塊。
缺點:每個數據塊有一個最大長度的限制;系統中所有消息隊列所包含的全部數據塊的總長度也有一個上限。
基本原理: 消息是由固定大小的首部和可變長度的正文組成的;可以使用一個整數值標識消息,這就允許進程有選擇的從消息隊列中獲取消息。只要進程從IPC消息隊列中讀出一條消息,內核就把這條消息刪除;因此,一個進程只能接收一條給定的消息。當消息隊列滿時,則試圖讓新消息入隊的進程可能被阻塞。當消息隊列為空時,則接收進程甚至會被阻塞。
代碼示例:
發送信息的程序的源文件msgsend.c的源代碼為:
#include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <sys/msg.h> #include <errno.h> #define MAX_TEXT 512 struct msg_st { long int msg_type; char text[MAX_TEXT]; }; int main() { int running = 1; struct msg_st data; char buffer[BUFSIZ]; int msgid = -1; //建立消息隊列 msgid = msgget((key_t)1234, 0666 | IPC_CREAT); if(msgid == -1) { fprintf(stderr, "msgget failed with error: %d\n", errno); exit(EXIT_FAILURE); } //向消息隊列中寫消息,直到寫入end while(running) { //輸入數據 printf("Enter some text: "); fgets(buffer, BUFSIZ, stdin); data.msg_type = 1; //注意2, 用來設置發送的信息的信息類型,即其發送的信息的類型為1 strcpy(data.text, buffer); //向隊列發送數據 if(msgsnd(msgid, (void*)&data, MAX_TEXT, 0) == -1) { fprintf(stderr, "msgsnd failed\n"); exit(EXIT_FAILURE); } //輸入end結束輸入 if(strncmp(buffer, "end", 3) == 0) running = 0; sleep(1); } exit(EXIT_SUCCESS); }
接收信息的程序源文件為msgreceive.c的源代碼為:
#include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <errno.h> #include <sys/msg.h> struct msg_st { long int msg_type; char text[BUFSIZ]; }; int main() { int running = 1; int msgid = -1; struct msg_st data; long int msgtype = 0; //注意1,值為0表示獲取隊列中第一個可用的消息 //建立消息隊列 msgid = msgget((key_t)1234, 0666 | IPC_CREAT); if(msgid == -1) { fprintf(stderr, "msgget failed with error: %d\n", errno); exit(EXIT_FAILURE); } //從隊列中獲取消息,直到遇到end消息為止 while(running) { if(msgrcv(msgid, (void*)&data, BUFSIZ, msgtype, 0) == -1) { fprintf(stderr, "msgrcv failed with errno: %d\n", errno); exit(EXIT_FAILURE); } printf("You wrote: %s\n",data.text); //遇到end結束 if(strncmp(data.text, "end", 3) == 0) running = 0; } //刪除消息隊列 if(msgctl(msgid, IPC_RMID, 0) == -1) { fprintf(stderr, "msgctl(IPC_RMID) failed\n"); exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); }
如果把注意1,即msgreceive.c文件main函數中的語句由long int msgtype = 0;改變為long int msgtype = 2;會發生什么情況,msgreceive將不能接收到程序msgsend發送的信息。因為在調用msgrcv函數時,如果msgtype(第四個參數)大於零,則將只獲取具有相同消息類型的第一個消息,修改后獲取的消息類型為2,而msgsend發送的消息類型為1,所以不能被msgreceive程序接收。
msgreceive如果沒有接收到信息和輸出,而且當msgsend輸入end結束后,msgreceive也不會結束,通過jobs命令我們可以看到它還在后台運行着。