1、概述
消息隊列可以認為是一個消息連結清單,system v 消息隊列使用消息隊列辨別符辨別。具有足夠特權的任何程序都可以往一個隊列放置一個消息,具有足夠特權的任何程序都可以從一個給定隊列讀出一個消息。在某個程序往一個隊列寫入消息之前,并不需要另外某個程序在該隊列上等待消息的到達。system v 消息隊列是随核心持續的,隻有在核心重起或者顯示删除一個消息隊列時,該消息隊列才會真正被删除。可以将核心中的某個特定的消息隊列畫為一個消息連結清單,如下圖所示:
對于系統中沒個消息隊列,核心維護一個msqid_ds的資訊結構:
struct msqid_ds
{
struct msqid_ds {
struct ipc_perm msg_perm;
struct msg *msg_first; /* first message on queue,unused */
struct msg *msg_last; /* last message in queue,unused */
__kernel_time_t msg_stime; /* last msgsnd time */
__kernel_time_t msg_rtime; /* last msgrcv time */
__kernel_time_t msg_ctime; /* last change time */
unsigned long msg_lcbytes; /* reuse junk fields for 32 bit */
unsigned long msg_lqbytes; /* ditto */
unsigned short msg_cbytes; /* current number of bytes on queue */
unsigned short msg_qnum; /* number of messages in queue */
unsigned short msg_qbytes; /* max number of bytes on queue */
__kernel_ipc_pid_t msg_lspid; /* pid of last msgsnd */
__kernel_ipc_pid_t msg_lrpid; /* last receive pid */
};
2、system v 消息隊列操作函數
系統v消息隊列api共有四個,使用時需要包括幾個頭檔案:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
1)int msgget(key_t key, int msgflg)
參數key是一個鍵值,由ftok獲得;msgflg參數是一些标志位。該調用傳回與健值key相對應的消息隊列描述字。
在以下兩種情況下,該調用将建立一個新的消息隊列:
如果沒有消息隊列與健值key相對應,并且msgflg中包含了ipc_creat标志位;
key參數為ipc_private;
參數msgflg可以為以下:ipc_creat、ipc_excl、ipc_nowait或三者的或結果。
調用傳回:成功傳回消息隊列描述字,否則傳回-1。
注:參數key設定成常數ipc_private并不意味着其他程序不能通路該消息隊列,隻意味着即将建立新的消息隊列。
2)int msgrcv(int msqid, struct msgbuf *msgp, int msgsz, long msgtyp, int msgflg);
該系統調用從msgid代表的消息隊列中讀取一個消息,并把消息存儲在msgp指向的msgbuf結構中。
msqid為消息隊列描述字;消息傳回後存儲在msgp指向的位址,msgsz指定msgbuf的mtext成員的長度(即消息内容的長度),msgtyp為請求讀取的消息類型;讀消息标志msgflg可以為以下幾個常值的或:
ipc_nowait 如果沒有滿足條件的消息,調用立即傳回,此時,errno=enomsg
ipc_except 與msgtyp>0配合使用,傳回隊列中第一個類型不為msgtyp的消息
ipc_noerror 如果隊列中滿足條件的消息内容大于所請求的msgsz位元組,則把該消息截斷,截斷部分将丢失。
msgrcv手冊中詳細給出了消息類型取不同值時(>0; <0; =0),調用将傳回消息隊列中的哪個消息。
msgrcv()解除阻塞的條件有三個:
消息隊列中有了滿足條件的消息;
msqid代表的消息隊列被删除;
調用msgrcv()的程序被信号中斷;
調用傳回:成功傳回讀出消息的實際位元組數,否則傳回-1。
3)int msgsnd(int msqid, struct msgbuf *msgp, int msgsz, int msgflg);
向msgid代表的消息隊列發送一個消息,即将發送的消息存儲在msgp指向的msgbuf結構中,消息的大小由msgze指定。
對發送消息來說,有意義的msgflg标志為ipc_nowait,指明在消息隊列沒有足夠空間容納要發送的消息時,msgsnd是否等待。造成msgsnd()等待的條件有兩種:
目前消息的大小與目前消息隊列中的位元組數之和超過了消息隊列的總容量;
目前消息隊列的消息數(機關"個")不小于消息隊列的總容量(機關"位元組數"),此時,雖然消息隊列中的消息數目很多,但基本上都隻有一個位元組。
msgsnd()解除阻塞的條件有三個:
不滿足上述兩個條件,即消息隊列中有容納該消息的空間;
調用msgsnd()的程序被信号中斷;
調用傳回:成功傳回0,否則傳回-1。
4)int msgctl(int msqid, int cmd, struct msqid_ds *buf);
該系統調用對由msqid辨別的消息隊列執行cmd操作,共有三種cmd操作:ipc_stat、ipc_set 、ipc_rmid。
ipc_stat:該指令用來擷取消息隊列資訊,傳回的資訊存貯在buf指向的msqid結構中;
ipc_set:該指令用來設定消息隊列的屬性,要設定的屬性存儲在buf指向的msqid結構中;可設定屬性包括:msg_perm.uid、msg_perm.gid、msg_perm.mode以及msg_qbytes,同時,也影響msg_ctime成員。
ipc_rmid:删除msqid辨別的消息隊列;
寫個程式建立一個消息隊列,然後網消息隊列中放置一個含有1個位元組的消息,發出msgctl的ipc_stat指令,使用system函數執行ipcs指令,最後使用ipc_rmid指令删除該隊列。程式如下:
程式執行結果如下:
system v消息隊列是随核心持續的,我們可以編寫一組小程式來操作消息隊列。程式如下:
建立消息隊列msgcreate程式:
向消息隊列放置資訊msgsnd程式:
從消息隊列中讀出一個消息msgrcv程式:
删除一個消息隊列msgrmid程式:
程式測試結果如下:
寫個客戶-伺服器例子,建立兩個消息隊列,一個隊列用來從客戶到伺服器的消息,一個隊列用于從伺服器到客戶的消息。主要功能是:客戶向伺服器發現一條消息,服務接收到并輸出接收到的客戶消息,然後伺服器向客戶發送一條消息,客戶顯示伺服器發送的消息。程式如下:
公共頭檔案svmsg.h:
用戶端程式svmsg_client.c
伺服器程式svmsg_server.c
3、複用消息
與一個隊列中的每個消息相關聯的類型字段提供了兩個特性:
(1)類型字段可以用于辨別消息,進而允許多個程序在單個隊列上複用消息。
(2)類型字段可以用做優先級,允許接收者以不同于先進先出的某個順序讀出各個消息。