消息隊列是消息的鏈接表,存放在內核中并由消息隊列標識符標識。在本節中,我們把消息隊列簡稱為隊列(queue),其標識符為隊列ID(queue ID)。
msgget用于創建一個新隊列或打開一個現存的隊列。msgsnd將新消息添加到隊列尾端。每個消息包含一個正長整型類型字段,一個非負長度以及實際數據字節(對應于長度),所有這些都在將消息添加到隊列時,傳送給msgsnd。msgrcv用于從隊列中取消息。我們并不一定要以先進先出次序取消息,也可以按消息的類型字段取消息。
每個隊列都有一個msgqid_ds結構與其相關聯:
struct msqid_ds { struct ipc_perm msg_perm; /* http://www.CUOXin.com/nufangrensheng/p/3561681.html */ msgqnum_t msg_qnum; /* # of messages on queue */ msglen_t msg_qbytes; /* max # of bytes no queue */ pid_t msg_lspid; /* pid of last msgsnd() */ pid_t msg_lrpid; /* pid of last msgrcv() */ time_t msg_stime; /* last-msgsnd() time */ time_t msg_rtime; /* last-msgrcv() time */ time_t msg_ctime; /* last-change time */ ...};
此結構規定了隊列的當前狀態。結構中所示的各成員是由Single UNIX Specification定義的。具體實現可能包括標準中沒有定義的另一些字段。
調用的第一個函數通常是msgget,其功能是打開一個現存隊列或創建一個新隊列。
#include <sys/msg.h>int msgget(key_t key, int flag);返回值:若成功則返回消息隊列ID,若出錯則返回-1
http://www.CUOXin.com/nufangrensheng/p/3561681.html中標識符和鍵部分,說明了將key變換成一個標識符的規則,并且討論是否創建一個新隊列或訪問一個現存隊列。
當創建一個新隊列時,初始化msqid_ds結構的下列成員:
若執行成功,msgget返回非負隊列ID。此后,該值就可被用于其他三個消息隊列函數(msgsnd、msgrcv和msgctl)。
msgctl函數對隊列執行多種操作。它和另外兩個與信號量和共享存儲有關的函數(semctl和shmctl)是XSI IPC的類似于ioctl的函數(亦即垃圾桶函數)。
#include <sys/msg.h>int msgctl(int msqid, int cmd, struct msqid_ds *buf);返回值:若成功則返回0,若出錯則返回-1
cmd參數說明對由msqid指定的隊列要執行的命令:
IPC_STAT 取此隊列的msqid_ds結構,并將它存放在buf指向的結構中。
IPC_SET 按由buf指定結構中的值,設置與此隊列相關結構中的下列四個字段:msg_perm.uid、msg_perm.gid、msg_perm.mode和msg_qbytes。此命令只能由下列兩種進程執行:一種是其有效用戶ID等于msg_perm.cuid或msg_perm.uid;另一種是具有超級用戶特權的進程。只有超級用戶才能增加msg_qbytes的值。
IPC_RMID 從系統中刪除該消息隊列以及仍在該隊列中的所有數據。這種刪除立即生效。仍在使用這一消息隊列的其他進程在它們下一次試圖對此隊列進行操作時,將出錯返回EIDRM。此命令只能由下列兩種進程執行:一種是其有效用戶ID等于msg_perm.cuid或msg_perm.uid;另一種是具有超級用戶特權的進程。
這三條命令(IPC_STAT、IPC_STAT和IPC_RMID)也可用于信號量和共享存儲。
調用msgsnd將數據放到消息隊列中。
#include <sys/msg.h>int msgsnd(int msqid, const void *ptr, size_t nbytes, int flag);返回值:若成功則返回0,若出錯則返回-1
每個消息都由三部分組成,它們是:正長整型類型字段、非負長度(nbytes)以及實際數據字節(對應于長度)。消息總是放在隊列尾端。
ptr參數指向一個長整型數,它包含了正的整型消息類型,在其后緊跟著消息數據。(若nbytes是0,則無消息數據。)若發送的最長消息是512字節,則可定義下列結構:
struct mymesg { long mtype; /* positive message type */ char mtext[512]; /* message data, of length nbytes */};
于是,ptr就是一個指向mymesg結構的指針。接收者可以使用消息類型以非先進先出的次序取消息。
參數flag的值可以指定為IPC_NOWAIT。這類似于文件I/O的非阻塞I/O標志(見http://www.CUOXin.com/nufangrensheng/p/3544997.html)。若消息隊列已滿(或者是隊列中的消息總數等于系統限制值,或隊列中的字節總數等于系統限制值),則指定IPC_NOWAIT使得msgsnd立即出錯返回EAGAIN。如果沒有指定IPC_NOWAIT,則進程阻塞直到下述情況出現為止:有空間可以容納要發送的消息;從系統中刪除了此隊列;或捕捉到一個信號,并從信號處理程序返回。在第二種情況下,返回EIDRM(“標識符被刪除”)。最后一種情況則返回EINTR。
注意,對刪除消息隊列的處理不是很完善。因為對每個消息隊列并沒有設置一個引用計數器(對打開文件則有這種計數器),所以刪除一個隊列會造成仍在使用這一隊列的進程在下次對隊列進行操作時出錯返回。信號量機制也以同樣的方式處理其刪除。相反,刪除一個文件時,要等到使用該文件的最后一個進程關閉了它的文件描述符后,才能刪除文件中的內容。
當msgsnd成功返回,與消息隊列相關的msqid_ds結構得到更新,以表明發出該調用的進程ID(msg_lspid)、進行該調用的時間(msg_stime),并指示隊列中增加了一條消息(msg_qnum)。
msgrcv從隊列中取用消息:
#include <sys/msg.h>ssize_t msgrcv(int msqid, void *ptr, size_t nbytes, long type, int flag);返回值:若成功則返回消息的數據部分的長度,若出錯則返回-1
如同msgsnd中一樣,ptr參數指向一個長整型數(返回的消息類型存放在其中),跟隨其后的是存放實際消息數據的緩沖區。nbytes說明數據緩沖區的長度。若返回的消息大于nbytes,而且在flag中設置了MSG_NOERROR,則該消息被截短。(在這種情況下,不通知我們消息截短了,消息的截去部分被丟棄。)如果沒有設置這一標志,而消息又太長,則出錯返回E2BIG(消息仍留在隊列中)。
參數type使我們可以指定想要哪一種消息:
type == 0 返回隊列中的第一個消息。
type > 0 返回隊列中消息類型為type的第一個消息。
type < 0 返回隊列中消息類型值小于或等于type絕對值的消息,如果這種消息有若干個,則取類型值最小的消息。
type值非0用于以非先進先出次序讀消息。例如,若應用程序對消息賦優先權,那么type就可以是優先權值。如果一個消息隊列由多個客戶進程和一個服務器進程使用,那么type字段可以用來包含客戶進程的進程ID(只要進程ID可以存放在長整型中)。
可以指定flag值為IPC_NOWAIT,使操作不阻塞。這使得如果沒有所指定類型的消息,則msgrcv返回-1,errno設置為ENOMSG。如果沒有指定IPC_NOWAIT,則進程阻塞直至如下情況出現才終止:有了指定類型的消息;從系統中刪除了此隊列(出錯則返回-1且errno設置為EIDRM);或捕捉到一個信號并從信號處理程序返回(msgrcv返回-1,errno設置為EINTR)。
msgrcv成功執行時,內核更新與該消息隊列相關聯的msqid_ds結構,以指示調用者的進程ID(msg_lrpid)和調用時間(msg_rtime),并將隊列中的消息數(msg_qnum)減1。
消息隊列原來的實施目的是提供比一般IPC更高速度的進程通信方法,但現在與其他形式的IPC相比,在速度方面已經沒有什么差別了??紤]到使用消息隊列具有的問題(見http://www.CUOXin.com/nufangrensheng/p/3561681.html中優點和缺點部分),我們得出的結論是,在新的應用程序中不應當再使用它們。
本篇博文內容摘自《UNIX環境高級編程》(第二版),僅作個人學習記錄所用。關于本書可參考:http://www.apuebook.com/。
新聞熱點
疑難解答