进程间通信:消息队列概念及代码
前言
接下討論的IPC機制,它們最初由System V版本的Unix引入。由于這些機制都出現在同一個版本中并且有著相似的編程接口,所以它們被稱為System V IPC機制。接下來的內容包括:
信號量:用于管理對資源的訪問。
共享內存:用于在程序之間高效地共享數據。
消息隊列:在程序之間傳遞數據。
操作系統中的同步和異步:https://blog.csdn.net/qq_38289815/article/details/81012826
進程間通信:管道和命名管道(FIFO) ?https://blog.csdn.net/qq_38289815/article/details/104742682
進程間通信:信號量 ?https://blog.csdn.net/qq_38289815/article/details/104762940
進程間通信:共享內存 ?https://blog.csdn.net/qq_38289815/article/details/104776076
?
消息隊列
消息隊列與命名管道有許多相似之處,但少了在打開和關閉管道方面的復雜性。但使用消息隊列并未解決我們在使用命名管道時遇到的一些問題,如管道滿時的阻塞問題。消息隊列提供了一種在兩個不相關的進程之間傳遞數據的簡單且有效的方法。與命名管道相比,消息隊列的優勢在于,它獨立于發送和接收進程而存在,這消除了在同步命名管道的打開和關閉時可能產生的一些困難。
消息隊列提供了一種從一個進程向另一個進程發送一個數據塊的方法。?每個數據塊都被認為含有一個類型,接收進程可以獨立地接收含有不同類型的數據結構。優點:我們可以通過發送消息來避免命名管道的同步和阻塞問題;可以用一些方法來提前查看緊急消息。缺點:消息隊列與命名管道一樣,每個數據塊都有一個最大長度的限制,系統中所有隊列所包含的全部數據塊的總長度也有一個上限。Linux系統用宏定義MSGMAX和MSGMNB來表示一條消息的最大長度和一個隊列的最大長度。
?
消息隊列函數的定義如下:
#include <sys/msg.h>int msgctl(int msgid, int cmd, struct msgid_ds *buf); int msgget(key_t key, int msgflg); int msgrcv(int msgid, void *msg_ptr, size_t msg_sz, long int msgtype, int msgflg); int msgsnd(int msgid, const void *msg_ptr, size_t msg_sz, int msgflg);?
msgget()函數
msgget函數用來創建和訪問一個消息隊列。它的原型為:
int?msgget(key_t, key, int msgflg); 成功時返回一個以key命名的消息隊列的標識符(非零整數),失敗時返回-1。與其他的IPC機制一樣,程序必須提供一個鍵來命名某個特定的消息隊列。msgflg是一個權限標志,表示消息隊列的訪問權限,它與文件的訪問權限一樣。msgflg可以與IPC_CREAT做或操作,表示當key所命名的消息隊列不存在時創建一個消息隊列,如果key所命名的消息隊列存在時,IPC_CREAT標志會被忽略,而只返回一個標識符。
?
msgsnd()函數
msgsnd函數用來把消息添加到消息隊列中。它的原型為:
int?msgsend(int msgid, const void *msg_ptr, size_t msg_sz, int msgflg); 成功時函數返回0,失敗時返回-1。如果調用成功,消息數據的一份副本將被放到消息隊列中。msgid是由msgget函數返回的消息隊列標識符。
msg_ptr是一個指向準備發送消息的指針,但是消息的數據結構卻有一定的要求,指針msg_ptr所指向的消息結構一定要是以一個長整型成員變量開始的結構體(還有長度必須小于系統規定的上限),接收函數將用這個成員來確定消息的類型。所以消息結構要定義成這樣:?
struct my_message {long int message_type;/* The data you wish to transfer */ };msg_sz是msg_ptr指向的消息的長度,注意是消息的長度,而不是整個結構體的長度,也就是說msg_sz是不包括長整型消息類型成員變量的長度。
msgflg 用于控制在消息隊列滿或隊列消息達到系統范圍的限制時將要發生的事情。如果msgflg中設置IPC_NOWAIT狀態,函數將立刻返回,不發送消息并且返回值為-1。如果msgflg中IPC_NOWAIT標志被清除,則發送進程將掛起以等待隊列中騰出可用空間。
?
msgrcv()
msgrcv函數用來從一個消息隊列獲取消息,它的原型為:
int?msgrcv(int msgid, void *msg_ptr, size_t msg_st, long int msgtype, int msgflg); 調用成功時,該函數返回放到接收緩存區中的字節數,消息被復制到由msg_ptr指向的用戶分配的緩存區中,然后刪除消息隊列中的對應消息。失敗時返回-1。msgid, msg_ptr, msg_st 的作用和函數msgsnd函數的一樣。
msgtype?可以實現一種簡單的接收優先級。如果msgtype的值為0,就獲取隊列中的第一個可用消息。如果它的值大于零,將獲取具有相同消息類型的第一個信息。如果它小于零,就獲取類型等于或小于msgtype的絕對值的第一個消息。
msgflg 用于控制當隊列中沒有相應類型的消息可以接收時將發生的事情。如果msgflg中設置IPC_NOWAIT狀態,函數將立刻返回,返回值為-1。如果msgflg中IPC_NOWAIT標志被清除,則發送進程將掛起以等待隊列中騰出可用空間。
?
msgctl()
msgctl函數用來控制消息隊列,它與共享內存的shmctl函數相似,它的原型為:
int?msgctl(int msgid, int command, struct msgid_ds *buf); 成功時返回0,失敗時返回-1。msgid是由msgget返回的消息隊列標識符。
command是將要采取的動作,它可以取3個值:
? ? ? ? IPC_STAT:把msgid_ds結構中的數據設置為消息隊列的當前關聯值,即用消息隊列的當前關聯值覆蓋msgid_ds的值。
? ? ? ? IPC_SET:如果進程有足夠的權限,就把消息列隊的當前關聯值設置為msgid_ds結構中給出的值
? ? ? ? IPC_RMID:刪除消息隊列
buf是指向msgid_ds結構的指針,它指向消息隊列模式和訪問權限的結構。msgid_ds結構至少包括以下成員:?
struct?msgid_ds {uid_t shm_perm.uid;uid_t shm_perm.gid;mode_t shm_perm.mode; };?
使用消息隊列完成進程間通信
由于可以讓不相關的進程完成通信,所以我們在這里將編寫兩個程序,msg1.c用于接收消息,msg2.c用于發送消息。允許兩個程序都可以創建消息隊列,但只有接收者在接收完最后一個消息之后刪除它。
#include <stdio.h> //msg1.c #include <stdlib.h> #include <string.h> #include <sys/msg.h> #include <errno.h>struct msg_st {long int my_msg_type;char text[BUFSIZ]; };int main(int argc, char **argv) {int msgid;struct msg_st data;long int msg_to_receive = 0; // 建立消息隊列msgid = msgget((key_t)1234, 0666 | IPC_CREAT);if (msgid == -1){fprintf(stderr, "msgget failed width error: %d\n", errno);exit(EXIT_FAILURE);}// 從隊列中獲取消息,直到遇到end消息為止while (1){if (msgrcv(msgid, (void *)&data, BUFSIZ, msg_to_receive, 0) == -1){fprintf(stderr, "msgrcv failed width erro: %d", errno);}printf("You wrote: %s", data.text);// 遇到end結束if (strncmp(data.text, "end", 3) == 0){break;}}// 刪除消息隊列if (msgctl(msgid, IPC_RMID, 0) == -1){fprintf(stderr, "msgctl(IPC_RMID) failed\n");}exit(EXIT_SUCCESS); } #include <stdlib.h> //msg2.c #include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/msg.h> #include <errno.h>#define MAX_TEXT 512struct msg_st {long int my_msg_type;char text[MAX_TEXT]; };int main(int argc, char **argv) {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 error: %d\n", errno);exit(EXIT_FAILURE);}// 向消息隊里中寫消息,直到寫入endwhile (1){printf("Enter some text: \n");fgets(buffer, BUFSIZ, stdin);data.my_msg_type = 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){break;}sleep(1);}exit(EXIT_SUCCESS); }實驗解析
發送者程序通過msgget來創建一個消息隊列,然后用msgsnd向隊列中增加消息。接收者用msgget獲得消息隊列標識符,然后開始接收消息,直到接收到特殊的“end”為止。然后它用msgctl來刪除消息隊列以完成清理工作。
總結
以上是生活随笔為你收集整理的进程间通信:消息队列概念及代码的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 进程间通信:共享内存概念及代码
- 下一篇: 自信和优越感的区别