IPC之消息隊列詳解與使用

一、    概念

 消息隊列就是一個消息的鏈表。對消息隊列有寫權限的進程可以向其中按照一定的規則添加新消息;對消息隊列有讀權限的進程可以從消息隊列中讀出消息。消息隊列是隨內核持續的。下面介紹三個概念:

1;隨進程持續:IPC一直存在,直至打開IPC對象的最後一個進程關閉該對象爲止,如管道和有名管道

2;隨內核持續:IPC一直持續到內核重新自舉或者顯示刪除對象爲止。如:消息隊列,信號量,共享內存

3;隨文件系統持續:IPC一直持續的顯示刪除該對象爲止

System V消息隊列目前被大量使用。

二、    消息隊列的信息

每個消息隊列都有一個隊列頭,用結構struct msg_queue來描述。隊列頭中包含了該消息隊列的大量信息,包括消息隊列鍵值、用戶ID、組ID、消息隊列中消息數目等等,甚至記錄了最近對消息隊列讀寫進程的ID。讀者可以訪問這些信息,也可以設置其中的某些信息。這個結構存於系統空間。

struct msg_queue {
    structkern_ipc_perm q_perm;
    time_tq_stime;         /* last msgsndtime */
    time_tq_rtime;         /* last msgrcvtime */
    time_tq_ctime;         /* last changetime */
    unsignedlong q_cbytes;     /* current number of bytes on queue*/
    unsignedlong q_qnum;       /* number of messages inqueue */
    unsignedlong q_qbytes;     /* max number of bytes on queue */
    pid_tq_lspid;          /* pid oflast msgsnd */
    pid_tq_lrpid;          /* lastreceive pid */
    structlist_head q_messages;
    structlist_head q_receivers;
    structlist_head q_senders;
};
 


結構msqid_ds用來設置或返回消息隊列的信息,存在於用戶空間;

structmsqid_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 32bit */
    unsigned long msg_lqbytes; /* ditto */ 
    unsigned short msg_cbytes; /* current number of byteson queue */ 
    unsigned short msg_qnum; /* number of messages in queue*/
    unsigned short msg_qbytes; /* max number of bytes onqueue */ 
    __kernel_ipc_pid_t msg_lspid; /* pid of last msgsnd */
    __kernel_ipc_pid_t msg_lrpid; /* last receive pid*/ 
};


三、    打開創建消息隊列

  消息隊列的內核持續性要求每個消息隊列都在系統範圍內對應唯一的鍵值,所以,要獲得一個消息隊列的描述字,只需提供該消息隊列的鍵值即可。msgget用於創建一個消息隊列或打開一個現存的隊列。

名稱::

msgget

功能:

創建消息隊列

頭文件:

#include <sys/types.h>

#include <sys/msg.h>

#inlcude <sys/ipc.h>

函數原形:

int msgget(key_t key,int msgflag);

參數:

key 消息隊列的鍵

flag 一些標誌位

返回值:

若成功則爲消息隊列描述字若出錯則爲-1。

參數key是一個鍵值,由ftok獲得;msgflg參數是一些標誌位。該調用返回與健值key相對應的消息隊列描述字。

       在以下兩種情況下,該調用將創建一個新的消息隊列:

       1.如果沒有消息隊列與健值key相對應,並且msgflg中包含了IPC_CREAT標誌位;

       2.key參數爲IPC_PRIVATE;

       參數msgflg可以爲以下:IPC_CREAT(創建消息隊列)、IPC_EXCL(  )、IPC_NOWAIT(  )或三者的或結果。

       還有注意的是:當創建一個新隊列時,系統自動初始化struct msqid_ds結構的下列成員。

ipc_perm結構按我們以前說的進行初始化。該結構中mode成員按flag中的相應權限位設置。

msg_qnum,msg_lspid,msg_lrpid,msg_stime,msg_rtime都設置爲0。

msg_ctime設置爲當前時間。

msg_qbytes設置爲系統限制值。

 

四、獲得和修改消息隊列屬性,刪除消息隊列

名稱::

msgctl

功能:

對消息隊列進行多種操作

頭文件:

#include <sys/msg.h>

函數原形:

int msgctl(int msqid, int cmd,struct msqid_ds *buf);

參數:

msqid   消息隊列描述字

cmd    要執行的操作

buf     此隊列的struct msqid_ds結構

返回值:

若成功返回0,若出錯返回-1。

 

 該系統調用對由msqid標識的消息隊列執行cmd操作,共有三種cmd操作:IPC_STAT、IPC_SET、IPC_RMID。

IPC_STAT:該命令用來獲取消息隊列信息,返回的信息存貯在buf指向的msqid_da結構中;

IPC_SET:該命令用來設置消息隊列的屬性,要設置的屬性存儲在buf指向的msqid_ds結構中;可設置屬性包括:msg_perm.uid、msg_perm.gid、msg_perm.mode以及msg_qbytes,同時,也影響msg_ctime成員。

IPC_RMID:刪除msqid_ds標識的消息隊列.

 

五、用消息隊列發送和接收消息

名稱::

msgsnd

功能:

將數據放到消息隊列上

頭文件:

#include <sys/types.h>

#include <sys/msg.h>

#inlcude <sys/ipc.h>

函數原形:

int msgsnd(int msqid, struct msgbuf *msgp, int msgsz, int msgflg);

參數:

msqid   消息隊列描述字

msgp       指向消息數據的指針

msgsz    發送消息的大小

msgflg       標誌位

返回值:

若成功則爲0,若出錯則爲-1。

      

 

 

 

 

 

 

 

 向msgid代表的消息隊列發送一個消息,即將發送的消息存儲在msgp指向的msgbuf結構中,消息的大小由msgze指定。

       structmsgbuf{

              longmtype; /*消息類型*/

              charmtext[1]; /*消息數據*/

       };

       我們可以把msgbuf結構看成是一個模版,程序員可以根據自己的需要來設計直接的消息結構。舉例來說,如果某個應用需要交換由一個整數後跟一個8字節字符數組構成的消息,那它可以如下定義自己的結構:

       typedefstruct my_msgbuf{

              longmtypel

              int    mshort;

              char mchar[MY_DATA];

       }Message;

       對發送消息來說,有意義的msgflg標誌爲IPC_NOWAIT,指明在消息隊列沒有足夠空間容納要發送的消息時,msgsnd是否等待。造成msgsnd()等待的條件有兩種:

       1.當前消息的大小與當前消息隊列中的字節數之和超過了消息隊列的總容量;

       2.當前消息隊列的消息數(單位"個")不小於消息隊列的總容量(單位"字節數"),此時,雖然消息隊列中的消息數目很多,但基本上都只有一個字節。

       msgsnd()解除阻塞的條件有三個:

       1.不滿足上述兩個條件,即消息隊列中有容納該消息的空間;

       2.msqid代表的消息隊列被刪除;

      3.調用msgsnd()的進程被信號中斷;

       當msgsnd成功返回,與消息隊列相關的msqid_ds結構得到更新,以標明發出該調用的進程ID(msg_lspid),進行該調用的時間(msg_stime),並指示隊列中增加了一條消息。

 六、消息隊列的使用

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <unistd.h>
void msg_stat(int,struct msqid_ds );

int main(void)
{
int gflags,sflags,rflags;
key_t key;
int msgid;
int reval;
struct msgsbuf
{
      int mtype;
      char mtext[1];
   }msg_sbuf;      /*發送消息緩衝區數據結構*/
struct msgmbuf
   {
      int mtype;
      char mtext[10];
   }msg_rbuf;      /*接收消息緩衝區數據結構*/
struct msqid_ds msg_ginfo,msg_sinfo;
char* msgpath="/unix/msgqueue";
key=ftok(msgpath,'a');  /*獲取消息隊列鍵值*/
gflags=IPC_CREAT|IPC_EXCL;
msgid=msgget(key,gflags|00666);  /*調用msgget創建消息隊列*/
if(msgid==-1)
{
       printf("msg create error\n");
       return;
}
msg_stat(msgid,msg_ginfo);
/*創建一個消息隊列後,輸出消息隊列缺省屬性。第一次調用msg_stat 子函數*/
sflags=IPC_NOWAIT;   /*消息隊列滿時,msgsnd 不等待,立刻出錯返回*/
msg_sbuf.mtype=10;
msg_sbuf.mtext[0]='a';   /*將要發送的消息數據*/
reval=msgsnd(msgid,&msg_sbuf,sizeof(msg_sbuf.mtext),sflags); /*調用msgsnd發送消息*/
if(reval==-1)
{
       printf("message send error\n");
}
msg_stat(msgid,msg_ginfo);
/*成功發送一個消息後,輸出此時消息隊列屬性。第二次調用msg_stat 子函數*/
rflags=IPC_NOWAIT|MSG_NOERROR;       /*含義見表10.1*/
reval=msgrcv(msgid,&msg_rbuf,4,10,rflags);  
/*調用msgrcv接收消息,接收數據長度爲4,type > 0,含義見表10.2*/
if(reval==-1)
{
       printf("read msg error\n");
    }
else
{
       printf("read from msg queue %d bytes\n",reval);   /*打印接收到數據的字節數*/
    }
msg_stat(msgid,msg_ginfo);
/*從消息隊列中讀出消息後,再次輸出消息隊列屬性。第三次調用msg_stat 子函數*/
msg_sinfo.msg_perm.uid=8;
/*試圖更改消息隊列的缺省屬性(要求root用戶權限),所有者有效用戶ID更改爲8*/
msg_sinfo.msg_perm.gid=8;
/*消息隊列的所有者有效組ID更改爲8*/
msg_sinfo.msg_qbytes=16388;
/*消息隊列可容納最大字節數更改爲16388(缺省爲16384)*/
reval=msgctl(msgid,IPC_SET,&msg_sinfo);   /*調用msgctl設置消息隊列屬性*/
if(reval==-1)
{
       printf("msg set info error\n");
       return;
}
msg_stat(msgid,msg_ginfo);  /*驗證設置消息隊列屬性。第三次調用msg_stat 子函數*/
reval=msgctl(msgid,IPC_RMID,NULL);  /* 操作完畢,調用msgctl刪除消息隊列*/
if(reval==-1)
{
       printf("unlink msg queue error\n");
       return;
}
return 0;
}

void msg_stat (int msgid,struct msqid_ds msg_info)
{
int reval;
sleep(1);   /*只是爲了後面輸出時間的方便*/
reval=msgctl(msgid,IPC_STAT,&msg_info);  /*調用msgctl 獲得消息隊列屬性信息*/
if(reval==-1)
{
      printf("get msg info error\n");
      return;
}
printf("\n");
printf("current number of bytes on queue is %d\n",msg_info.msg_cbytes);
printf("number of messages in queue is %d\n",msg_info.msg_qnum);
printf("max number of bytes on queue is %d\n",msg_info.msg_qbytes);
/*每個消息隊列的容量(字節數)都有限制MSGMNB,值的大小因系統而異。在創建*/
/*新的消息隊列時,msg_qbytes的缺省值就是MSGMNB*/
printf("pid of last msgsnd is %d\n",msg_info.msg_lspid);
/*最近一個執行msgsnd函數的進程ID*/
printf("pid of last msgrcv is %d\n",msg_info.msg_lrpid);
/*最近一個執行msgrcv函數的進程ID*/
printf("last msgsnd time is %s", ctime(&(msg_info.msg_stime)));
/*最近一次執行msgsnd函數的時間。ctime()將時間轉變成周、月、日、時分秒、年的*/
/*形式,屬於標準C函數*/
printf("last msgrcv time is %s", ctime(&(msg_info.msg_rtime)));
/*最近一次執行msgrcv函數的時間*/
printf("last change time is %s", ctime(&(msg_info.msg_ctime)));
/*最近一次改變該消息隊列的時間*/
printf("msg uid is %d\n",msg_info.msg_perm.uid);/*消息隊列所有者的有效用戶ID*/
printf("msg gid is %d\n",msg_info.msg_perm.gid); /*消息隊列所有者的有效組ID*/
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章