A. linux系統編程—消息隊列
消息隊列本質上是位於內核空間的鏈表,鏈表的每個節點都是一條消息。每一條消息都有自己的消息類型,消息類型用整數來表示,而且必須大於 0。每種類型的消息都被對應的鏈表所維護:
其中數字 1 表示類型為 1 的消息,數字2、3、4 類似。彩色塊表示消息數據,它們被掛在對應類型的鏈表上。
值得注意的是,剛剛說過沒有消息類型為 0 的消息,實際上,消息類型為 0 的鏈表記錄了所有消息加入隊列的順序,其中紅色箭頭表示消息加入的順序。
無論你是發送還是接收消息,消息的格式都必須按照規范來。簡單的說,它一般長成下面這個樣子:
所以,只要你保證首4位元組(32 位 linux 下的 long)是一個整數就行了。
舉個例子:
從上面可以看出,正文部分是什麼數據類型都沒關系,因為消息隊列傳遞的是 2 進制數據,不一定非得是文本。
msgsnd 函數用於將數據發送到消息隊列。如果該函數被信號打斷,會設置 errno 為 EINTR。
參數 msqid:ipc 內核對象 id
參數 msgp:消息數據地址
參數 msgsz:消息正文部分的大小(不包含消息類型)
參數 msgflg:可選項
該值為 0:如果消息隊列空間不夠,msgsnd 會阻塞。
IPC_NOWAIT:直接返回,如果空間不夠,會設置 errno 為 EAGIN.
返回值:0 表示成功,-1 失敗並設置 errno。
msgrcv 函數從消息隊列取出消息後,並將其從消息隊列里刪除。
參數 msqid:ipc 內核對象 id
參數 msgp:用來接收消息數據地址
參數 msgsz:消息正文部分的大小(不包含消息類型)
參數 msgtyp:指定獲取哪種類型的消息
msgtyp = 0:獲取消息隊列中的第一條消息
msgtyp > 0:獲取類型為 msgtyp 的第一條消息,除非指定了 msgflg 為MSG_EXCEPT,這表示獲取除了 msgtyp 類型以外的第一條消息。
msgtyp < 0:獲取類型 ≤|msgtyp|≤|msgtyp| 的第一條消息。
參數 msgflg:可選項。
如果為 0 表示沒有消息就阻塞。
IPC_NOWAIT:如果指定類型的消息不存在就立即返回,同時設置 errno 為 ENOMSG
MSG_EXCEPT:僅用於 msgtyp > 0 的情況。表示獲取類型不為 msgtyp 的消息
MSG_NOERROR:如果消息數據正文內容大於 msgsz,就將消息數據截斷為 msgsz
程序 msg_send 和 msg_recv 分別用於向消息隊列發送數據和接收數據。
msg_send 程序定義了一個結構體 Msg,消息正文部分是結構體 Person。該程序向消息隊列發送了 10 條消息。
msg_send.c
程序 msg_send 第一次運行完後,內核中的消息隊列大概像下面這樣:
msg_recv 程序接收一個參數,表示接收哪種類型的消息。比如./msg_recv 4 表示接收類型為 4 的消息,並列印在屏幕。
先運行 msg_send,再運行 msg_recv。
接收所有消息
接收類型為 4 的消息
獲取和設置消息隊列的屬性
msqid:消息隊列標識符
cmd:控制指令
IPC_STAT:獲得msgid的消息隊列頭數據到buf中
IPC_SET:設置消息隊列的屬性,要設置的屬性需先存儲在buf中,可設置的屬性包括:msg_perm.uid、msg_perm.gid、msg_perm.mode以及msg_qbytes
buf:消息隊列管理結構體。
返回值:
成功:0
出錯:-1,錯誤原因存於error中
EACCESS:參數cmd為IPC_STAT,確無許可權讀取該消息隊列
EFAULT:參數buf指向無效的內存地址
EIDRM:標識符為msqid的消息隊列已被刪除
EINVAL:無效的參數cmd或msqid
EPERM:參數cmd為IPC_SET或IPC_RMID,卻無足夠的許可權執行