Linux程序間通訊 訊息佇列

2021-07-09 04:46:43 字數 4153 閱讀 3007

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裡面一樣的...