訊息佇列(message queue)
管線給你的是沒有邊界的原始位元組流——你分不出一則訊息在哪裡結束、下一則從哪裡開始。有時你要的恰恰相反:丟進一整則訊息,讓接收方把那則訊息原封不動、當成一個單位整個取出。訊息佇列是一個由核心管理、保留這些邊界的信箱。你放進一則離散的訊息;讀取方取出一則離散的訊息。分組被保留下來。
它有兩種風味。System V 訊息佇列(較舊)用 msgget() 建立、用 msgsnd() 與 msgrcv() 使用;每則訊息帶一個「型別」欄位,一個整數,你能用它選擇性地只接收某個型別的訊息——一個小小的內建路由技巧。POSIX 訊息佇列(較新,常更好用)用 mq_open() 以名字開啟(名字長得像 /myqueue),用 mq_send() 送、用 mq_receive() 收,並加上訊息優先權(優先權高的先遞送)與訊息到達時收到通知的選項。兩者中核心都儲存排入佇列的訊息,所以即使傳送方在接收方讀取之前就結束,訊息仍然存在——不像信號,一經處理就消失。
當你想要訊息傳遞語意時訊息佇列大放異彩:在一台機器上的行程之間做乾淨、有分框、有優先權、非同步的交接,而不必在管線之上自己發明分框。誠實的限制:佇列有有限容量,所以滿了的佇列會讓 mq_send() 阻塞(非阻塞時則失敗)——又是背壓。System V 佇列會持續存在直到被明確移除,且用彆扭的數字鍵定址;POSIX 佇列以名字存在,且必須被 unlink。而要跨機器交談根本不會用這些——真正的網路訊息中介或通訊端才是工具。這些是本機 IPC。
mqd_t q = mq_open("/jobs", O_CREAT|O_WRONLY, 0600, NULL); mq_send(q, "build #7", 8, 5); /* 一整則訊息,優先權 5 */ /* 讀取方的 mq_receive() 恰好取得「build #7」,邊界完整 */
不像管線,佇列保留訊息邊界與(POSIX 的)優先權。核心保存訊息直到被讀取。
訊息佇列保留邊界(一次送 = 一次收),這是管線做不到的。但它們仍是容量固定的本機 IPC——滿的佇列會阻塞傳送方,而且它會持續存在,意味著你必須自己移除佇列。