进程间通信的方式(三):消息队列
? ? ? ? ?消息隊(duì)列是在兩個(gè)不相關(guān)進(jìn)程間傳遞數(shù)據(jù)的一種簡(jiǎn)單、高效方式,她獨(dú)立于發(fā)送進(jìn)程、接受進(jìn)程而存在。消息隊(duì)列提供了一種從一個(gè)進(jìn)程向另一個(gè)進(jìn)程發(fā)送一個(gè)數(shù)據(jù)塊的方法。每個(gè)數(shù)據(jù)塊都被認(rèn)為是一個(gè)管道,接收進(jìn)程可以獨(dú)立地接收含有不同管道的數(shù)據(jù)結(jié)構(gòu)。我們可以通過(guò)發(fā)送消息來(lái)避免命名管道的同步和阻塞問(wèn)題。消息隊(duì)列與命名管道一樣,每個(gè)數(shù)據(jù)塊都有一個(gè)最大長(zhǎng)度的限制。我們可以將每個(gè)數(shù)據(jù)塊當(dāng)作是一種消息類型(頻道),發(fā)送和接收的內(nèi)容就是這個(gè)類型(頻道)對(duì)應(yīng)的消息(節(jié)目),每個(gè)類型(頻道)相當(dāng)于一個(gè)獨(dú)立的管道,相互之間互不影響。
1.概述
? ? ? ? 消息隊(duì)列可以是一種消息鏈表。有足夠的權(quán)限的線程可以往隊(duì)列中放置消息,有足夠讀權(quán)限的線程可以從隊(duì)列中取走消息。每個(gè)消息都是一個(gè)記錄,它由發(fā)送者賦予一個(gè)優(yōu)先級(jí)。在某個(gè)進(jìn)程往一個(gè)隊(duì)列寫(xiě)入消息之前,并不需要另外某個(gè)進(jìn)程在該隊(duì)列上等待消息的到達(dá)。這根管道和FIFO是相反的,對(duì)于后者來(lái)說(shuō),除非讀出者已經(jīng)存在,否則現(xiàn)有 寫(xiě)入者是沒(méi)有意義的。
? ? ? ? 一個(gè)進(jìn)程可以往某個(gè)隊(duì)列中寫(xiě)入一些消息,然后終止,再讓另外一個(gè)進(jìn)程在以后某個(gè)時(shí)刻讀出這些消息。我沒(méi)說(shuō)過(guò)消息隊(duì)列具有隨內(nèi)核的持續(xù)性,這跟管道和FIFO不一樣,當(dāng)一個(gè)管道和FIFO的最后一次關(guān)閉發(fā)生時(shí),仍然在該管或FIFO上的數(shù)據(jù)將被丟棄。
?
?
1.1 消息隊(duì)列結(jié)構(gòu)
? ? ? ?我們將內(nèi)核中的某個(gè)特定的消息隊(duì)列畫(huà)為一個(gè)消息鏈表。假設(shè)有一個(gè)具有三個(gè)消息的隊(duì)列,消息長(zhǎng)度分別為1字節(jié)、2字節(jié)和三字節(jié),而且這些消息就是以這樣的順序?qū)懭朐撽?duì)列的。再假設(shè)這三個(gè)消息的類型(type)分別是100、200、300。
?
?
?
1.2 消息隊(duì)列和管道的對(duì)比
1.匿名管道是跟隨進(jìn)程的,消息隊(duì)列是跟隨內(nèi)核的,也就是說(shuō)進(jìn)程結(jié)束之后,匿名管道就死了,但是消息隊(duì)列還會(huì)存在(除非顯示調(diào)用函數(shù)銷毀)
2.管道是文件,存放在磁盤上,訪問(wèn)速度慢,消息隊(duì)列是數(shù)據(jù)結(jié)構(gòu),存放在內(nèi)存,訪問(wèn)速度快
3.管道是數(shù)據(jù)流式存取,消息隊(duì)列是數(shù)據(jù)塊式存取
?
?
2 代碼實(shí)現(xiàn)消息隊(duì)列的通信
?
? ? ? ? 首先從宏觀的角度了解一下消息隊(duì)列的工作機(jī)制。因?yàn)橄㈥?duì)列獨(dú)立于進(jìn)程而存在,為了區(qū)別不同的消息隊(duì)列,需要以key值標(biāo)記消息隊(duì)列,這樣兩個(gè)不相關(guān)進(jìn)程可以通過(guò)事先約定的key值通過(guò)消息隊(duì)列進(jìn)行消息收發(fā)。例如進(jìn)程A向key消息隊(duì)列發(fā)送消息,進(jìn)程B從Key消息隊(duì)列讀取消息。在這一過(guò)程中主要涉及到四個(gè)函數(shù):
#include <sys/msg.h> # 消息隊(duì)列相關(guān)函數(shù)及數(shù)據(jù)結(jié)構(gòu)頭文件int msgctl(int msqid, int cmd, struct msqid_ds *buf);# 控制消息隊(duì)列函數(shù)int msgget(key_t key, int msgflg); # 創(chuàng)建消息隊(duì)列,key值唯一標(biāo)識(shí)該消息隊(duì)列int msgrcv(int msqid, void *msg_ptr, size_t msg_sz, long int msgtype, int msgflg);# 接收消息int msgsnd(int msqid, const void *msg_ptr, size_t msg_sz, int msgflg);# 發(fā)送消息server 消息接收方
?
# msg1.c 接收端#include <stdlib.h> #include <stdio.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <sys/msg.h> # 包含消息隊(duì)列相關(guān)函數(shù)及數(shù)據(jù)結(jié)構(gòu)的頭文件 struct my_msg_st {long int my_msg_type;char some_text[BUFSIZ]; };# 消息格式 int main() {int running = 1;int msgid;struct my_msg_st some_data;long int msg_to_receive = 0;msgid = msgget((key_t)1234, 0666 | IPC_CREAT);# 創(chuàng)建標(biāo)識(shí)符為key = 1234 的消息隊(duì)列,注意發(fā)送端與接收端該值的一致性if (msgid == -1) {fprintf(stderr, “msgget failed with error: %d\n”, errno);exit(EXIT_FAILURE);}# 錯(cuò)誤處理:msgget調(diào)用成功返回消息隊(duì)列標(biāo)識(shí)符,調(diào)用失敗返回-1while(running) {if (msgrcv(msgid, (void *)&some_data, BUFSIZ,msg_to_receive, 0) == -1) { # 從消息隊(duì)列接收消息,如果接收失敗執(zhí)行if語(yǔ)句并退出fprintf(stderr, “msgrcv failed with error: %d\n”, errno);exit(EXIT_FAILURE);}printf(“You wrote: %s”, some_data.some_text);if (strncmp(some_data.some_text, “end”, 3) == 0) { # 如果接收到文本含有“end”,將running設(shè)置為0,效果是:退出while循環(huán)running = 0;}}if (msgctl(msgid, IPC_RMID, 0) == -1) { # 刪除消息隊(duì)列,如果刪除失敗執(zhí)行if語(yǔ)句并退出fprintf(stderr, “msgctl(IPC_RMID) failed\n”);exit(EXIT_FAILURE);}exit(EXIT_SUCCESS); }client 客戶端消息發(fā)送方
?
# msg2.c 發(fā)送端#include <stdlib.h> #include <stdio.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <sys/msg.h> #define MAX_TEXT 512 struct my_msg_st {long int my_msg_type;char some_text[MAX_TEXT]; };# 消息格式,與接收端一致 int main() {int running = 1;struct my_msg_st some_data;int msgid;char buffer[BUFSIZ];msgid = msgget((key_t)1234, 0666 | IPC_CREAT);# 創(chuàng)建消息標(biāo)識(shí)符key = 1234的消息隊(duì)列。如果該隊(duì)列已經(jīng)存在,則直接返回該隊(duì)列的標(biāo)識(shí)符,以便向該消息隊(duì)列收發(fā)消息if (msgid == -1) {fprintf(stderr, “msgget failed with error: %d\n”, errno);exit(EXIT_FAILURE);}# 錯(cuò)誤處理,同接收者msg1while(running) {printf(“Enter some text: “);fgets(buffer, BUFSIZ, stdin);# 由控制臺(tái)輸入文本,并將其存放在buffer之中some_data.my_msg_type = 1;# 類型填充,在本例中沒(méi)有特別含義strcpy(some_data.some_text, buffer);# 將buffer數(shù)據(jù)復(fù)制到some_text之中if (msgsnd(msgid, (void *)&some_data, MAX_TEXT, 0) == -1) { # 向消息隊(duì)列發(fā)送消息,如果發(fā)送失敗執(zhí)行if語(yǔ)句并退出fprintf(stderr, “msgsnd failed\n”);exit(EXIT_FAILURE);}if (strncmp(buffer, “end”, 3) == 0) {# 如果發(fā)送的“end”,則在發(fā)送“end”之后,退出while,結(jié)束程序running = 0;}}exit(EXIT_SUCCESS); }
?
總結(jié)
以上是生活随笔為你收集整理的进程间通信的方式(三):消息队列的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 进程间的通信方式(二):管道Pipe和命
- 下一篇: 进程间通信的方式(四):信号量