Linux的訊息佇列(queue)實質上是一個鏈表, 它有訊息佇列標識符(queue ID). msgget建立一個新隊列或開啟一個存在的隊列; msgsnd向隊列末端添加一條新訊息; msgrcv從隊列中取訊息, 取訊息是不一定遵循先進先出的, 也可以按訊息的類型欄位取訊息.
1. 標識符(des)和鍵(key):
訊息佇列, 訊號量和共用儲存段, 都屬於核心中的IPC結構, 它們都用標識符來描述. 這個標識符是一個非負整數, 與檔案描述符不同的是, 建立時並不會重複利用通過刪除回收的整數, 而是每次+1, 直到整數最大值迴轉到0.
標識符是IPC對象的內部名, 而它的外部名則是key(鍵), 它的基本類型是key_t, 在標頭檔<sys/types.h>中定義為長整型. 鍵由核心變換成標識符.
2. 訊息佇列狀態msqid_ds:
每個訊息佇列都有一個msqid_ds結構與其關聯:
struct msqid_ds
...{
struct ipc_perm msg_perm; /**//* access */
msgqnum_t msg_qnum; /**//* # of messages on queue */
msglen_t msg_qbytes; /**//* max # of bytes on 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 */
...
...
...
};
3. 由路徑名和項目ID產生一個key:
如果客戶進程和伺服器處理序認同一個路徑名和項目ID(0~255間的字元值), 接著調用ftok將這兩個值變換為一個key.
- 原型: key_t ftok(const char *path, int id);
- 標頭檔: <sys/ipc.h>
- 傳回值: 成功則返回key, 出錯則返回(key_t)-1.
- 參數: path參數必須引用一個現存檔案. 當產生key時 只使用id參數的低8位.
- 說明: 如果兩個路徑名引用兩個不同的檔案, 對這兩個路徑名調用ftok通常返回不同的key. 但是, 因為i節點號和key通常存放在長整型中, 於是建立key時可能會丟失資訊. 這意味著, 如果使用同一項目ID, 那麼對於不同檔案的兩個路徑名可能產生相同的key. 該函數的工作方式為:
- 按給定的路徑名取得其stat結構.
- 從該結構中取出部分st_dev和st_ino欄位, 與項目ID組合起來.
4. 建立/開啟訊息佇列:
msgget可以建立一個新隊列或開啟一個存在的隊列.
- 原型: int msgget(key_t key, int flag);
- 標頭檔: <sys/msg.h>
- 傳回值: 成功則返回訊息佇列ID, 出錯則返回-1.
- 參數:
- key: 訊息佇列的key值.
- flag: 標誌位.
- 說明:
- 建立隊列有兩種方法:
- key是IPC_PRIVATE.
- key當前未與特定類型的IPC結構相結合, 並且flag中指定了IPC_CREAT位.
- 初始化msqid_ds成員:
- ipc_perm中的mode成員按flag進行設定.
- msg_qnum, msg_lspid, msg_lrpid, msg_stime和msg_rtime都設定為0.
- msg_ctime設定為目前時間.
- msg_qbytes設定為系統限制值.
5. 訊息佇列的垃圾桶函數:
msgctl類似於驅動程式中的ioctl函數, 可對訊息佇列執行多種操作.
- 原型: int msgctl(int msqid, int cmd, struct msgqid_ds *buf);
- 標頭檔: <sys/msg.h>
- 傳回值: 成功則返回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_per.uid.
- 具有超級使用者特權的進程.
- IPC_RMID: 從系統中刪除該訊息佇列以及仍在該隊列中的所有資料. 執行許可權同上.
6. 將資料放到訊息佇列:
調用msgsnd將資料放到訊息佇列中.
- 原型: int msgsnd(int msqid, const void *ptr, size_t nbytes, int flag);
- 標頭檔: <sys/msg.h>
- 傳回值: 成功則返回0, 出錯則返回-1.
- 說明: 可以定義一個訊息結構, 結構中帶類型, 這樣就可用非先進先出順序取訊息了. 當msgsnd成功返回, 與訊息佇列相關的msqid_ds結構得到更新, 以標明發出該調用的進程ID(msg_lsqid), 進行該調用的時間(msg_stime), 並指示隊列中增加了一條訊息(msg_qnum).
7. 從訊息佇列中取訊息:
調用msgrcv將從訊息佇列中取訊息.
- 原型: ssize_t msgrcv(int msqid, void *ptr, size_t nbytes, long type, int flag);
- 標頭檔: <sys/msg.h>
- 傳回值: 成功則返回訊息的資料部分的長度, 出錯則返回-1.
- 參數:
- ptr: 指向一個長整型數(返回的訊息類型存放在其中), 跟隨其後的是存放實際訊息資料的緩衝區.
- nbytes: 資料緩衝區的長度. 若返回的訊息大於nbytes, 且在flag中設定了MSG_NOERROR, 則該訊息被截短.
- type:
- type == 0: 返回隊列中的第一個訊息.
- type > 0: 返回隊列中訊息類型為type的第一個訊息.
- type < 0: 返回隊列中訊息類型值小於或等於type絕對值的訊息, 如果這種訊息有若干個, 則取類型值最小的訊息.
- 說明: 當msgrcv成功返回時, 與訊息佇列相關的msqid_ds結構被更新, 以指示調用者的進程ID(msg_lrpid), 調用時間(msg_rtime)和隊列中的訊息數(msg_qnum)減1.