linux和類linux系統下程序間通訊(inter-process communication, ipc)有很多種方式,包括套接字(socket),共享記憶體(shared memory),管道(pipe),訊息佇列(message queue)等,各自有各自的一些應用場景和用途,這次就來聊一聊訊息佇列這個方式。
訊息佇列的機制如下圖所示,linux系統會維護乙個佇列,訊息傳送者通過系統api向這個佇列傳送訊息,存入佇列中,然後訊息接收者通過系統api從中取出訊息,訊息佇列具有一定的fifo特性,但它同時也具有根據優先順序來出隊的功能。
目前個人覺得訊息佇列的好處在於:實現程序間通訊時,可以直接利用系統直接包裝好的同步機制和簡單協議,而不需要再去設計通訊協議和同步的各種鎖。其缺點在於:每個佇列長度的大小都有系統的固定限制,而且由於佇列是單向設計的,乙個傳送乙個接收,若傳送者想收到接收者的返回,就比較困難。
這裡我們用c語言利用系統api來寫乙個簡單的例子,乙個生產者程序(producer)來生產訊息,乙個消費者程序(consumer)來消費訊息,就是將訊息列印出來,利用訊息佇列來實現這兩個程序之間的通訊。**共包含3個檔案,msg_queue_common.h,msg_queue_consumer.c,msg_queue_producer.c。
公共標頭檔案 msg_queue_common.h
#include #include #define msg_str_length 128
#define msg_key 1234
//user-defined message struct
struct my_msg;
消費者程序 msg_queue_consumer.c#include #include "msg_queue_common.h"
int main()
struct my_msg msg;
int ifexit = 1;
while(ifexit!=0)
msg.message[msg_str_length-1]='\0';
printf("received: %s\n",msg.message);
// check the command, decide if it exits while loop
if(msg.command==0)
}// delete the message queue
if(msgctl(msgid,ipc_rmid,0) == -1)
return 0;
}
生產者程序 msg_queue_producer.c#include #include #include "msg_queue_common.h"
int main()
struct my_msg msg;
printf("input information to consumer, enter \"bye\" to end both consumer and producer.\n");
int ifexit = 1;
while(ifexit!=0)else
int ret = msgsnd(msgid,(void*)&msg,sizeof(struct my_msg)-sizeof(long int),0);
if(ret == -1)
}printf("producer exit.\n");
return 0;
}
編譯生成gcc msg_queue_consumer.c -o consumer
gcc msg_queue_producer.c -o producer
執行
啟動兩個shell,先啟動./consumer,再啟動./producer,在producer下輸入資訊,將在consumer中接收並顯示,輸入bye,則將二者均結束。
$ ./producer
input information to consumer, enter "bye" to end both consumer and producer.
enter information to consumer: hello! allen junyu
enter information to consumer: you are such a nice guy
enter information to consumer: bye
producer exit.
$ ./consumer
receiving ...
received: hello! allen junyu
receiving ...
received: you are such a nice guy
receiving ...
received: bye
received command=0 to exit.
正如上面提到過的,訊息佇列在程式程序退出時,系統並不會自動**,那麼除了寫出很魯棒的程式外,有時候也不可避免存在訊息佇列洩露的情況。並且,有時候我們的確需要看到系統當前存在訊息佇列的情況,於是,利用系統命令來顯示,刪除訊息佇列就很有用了。
顯示當前訊息佇列命令為:
ipcs -q
實際上,ipcs是顯示各種程序間通訊狀態的命令,-q只不過讓它顯示訊息佇列(queue)的情況。還可以進行ipcs -qa來顯示訊息佇列的詳細情況:
$ ipcs -qa
ipc status from as of mon dec 28 19:34:57 cst 2015
t id key mode owner group creator cgroup cbytes qnum qbytes lspid lrpid stime rtime ctime
message queues:
q 524288 0x00002175 --rw-rw-rw- junyu staff junyu staff 0 0 2048 2760 2754 16:42:20 16:42:20 16:41:44
q 393217 0x000004d2 --rw-rw-rw- junyu staff junyu staff 0 0 2048 0 0 no-entry no-entry 19:34:53
其中,cbytes表示在該訊息佇列中的現存訊息所佔的位元組數,qnum表示當前佇列現存訊息的數量,qbytes表示當前佇列最多占用的位元組數,其他的解釋可以man ipcs看一下。
最重要的是,發生洩漏的時候刪除命令:
ipcrm -q id
id即為這個訊息的id,對應-qa顯示時的每行第二項
批量刪除所有訊息佇列命令:
ipcs -q | awk 'nr>3' | xargs -n1 ipcrm -q
其中nr>3表示awk指令碼只從第4行開始處理,xargs -n1表示一行一行的處理傳遞過來的引數,更加詳細的可以參考shell中awk,xargs等用法。
私有佇列
對於鍵值,也就是key_t型別的引數key,可以設定為特殊鍵值ipc_private,用於建立私有佇列,理論上來說,它應該只能被當前程序訪問,但實際情況是很多linux系統下其實並非私有,而且私有佇列的用處並不大[1],所以這個也不是很嚴重的問題,這裡就不再展開討論。
訊息佇列清除與**
程式中建立的佇列,沒有通過呼叫msgctl函式來執行顯式的刪除佇列的話,即使在程序退出時,作業系統也不會刪除該訊息佇列,所以寫**時一定要考慮到這個問題,在合適的情況下刪除訊息佇列,避免訊息佇列的洩露問題。
非阻塞傳送
在實踐中發現(os x 10.9.5),使用msgsnd函式來傳送訊息時,設定了其msg***引數為ipc_nowait,當函式返回-1時,並不完全是該佇列滿的情況,也有可能是當前作業系統不滿足可操作佇列的條件,這個就需要具體**具體分析了。
參考文獻
[1] linux程式設計(第4版)
linux程序間通訊 訊息佇列
訊息佇列由id 唯一標識 訊息佇列就是乙個訊息的列表,使用者可在佇列中新增,讀取訊息等 可按照型別來收發訊息 int msgget key t key,int flag int msgsnd int msqid,const void msgp,size t size,int flag msqid 訊...
Linux程序間通訊 訊息佇列
首先上篇文章我們說到了linux下進行程序間通訊的一種方法或機制匿名管道和命名管道,那麼這裡要說的是另外一種與之不同的通訊方法,即訊息佇列,兩者之間有相同也有不同的地方,具體的下面就一一介紹。一 什麼是訊息佇列?首先它也是一種進行程序間通訊的方式,通過乙個程序向另外乙個程序傳送資料塊的方式,每個資料...
linux程序間通訊 訊息佇列
訊息佇列屬於ipc 兩個程序間要通過訊息佇列進行通訊,比如a通過訊息佇列給b傳送乙個訊息。首先a要建立乙個訊息佇列,然後a往該訊息佇列裡面傳送訊息 由乙個有特殊形式的結構體構成,包括資料型別和資料內容 當不需要使用這個訊息佇列的時候刪除訊息佇列。b要做的事情是開啟訊息佇列,開啟方式是用和a裡面一樣的...