天天看點

10.3 消息隊列程式設計

System V的IPC機制提供了對消息隊列進行程式設計的接口,主要包括消息隊列的建立、消息的讀寫、消息隊列的控制及删除。另外,IPC機制還提供了生成IPC鍵值的函數ftok。

10.3.1 鍵值生成函數         鍵值生成函數ftok提供了一種鍵值生成方法,即根據檔案名和一個整型的變量生成一個惟一的鍵值,并且保證每次以同樣的參數調用ftok傳回的鍵值是相同的。這裡的整型變量成為項目ID。通過這種機制,使得程式設計人員不需要在程式中直接指定長整型的鍵值,而是通過定義一個檔案和一個項目ID的方式即可。函數ftok的原型為: #include <sys/ipc.h> key_t ftok(const char *pathname,int prjid); 參數說明: 1)pathname:檔案路徑名稱。可以使用絕對路徑或者相對路徑。 2)prjid:項目ID。指定相同的檔案名稱和一個不同的項目ID,可以生成不同的鍵值。 傳回值: 1)若失敗,傳回-1。可能的原因包括檔案不存在或者沒有足夠的通路權限。 2)若成功,傳回其他數值。此時傳回值即是可用的鍵值。 注意事項: 1)選擇的檔案應該是内容和屬性(修改時間等)長期不發生變化的,并且是對程序而言是可讀的,如“/etc/profile”等。如果檔案内容或者屬性經常發生變化,盡管用同樣的參數去調用ftok,而傳回的鍵值可能是不同的。 2)可以通過指定同一個檔案,而使用不同的項目ID的方式擷取不同的鍵值。

10.3.2 建立消息隊列         一個消息隊列實際上是Linux在核心配置設定的一個資料結構。要使用消息隊列進行消息傳遞,首先要建立一個消息隊列。在Linux下,實作建立消息隊列功能的是函數msgget。該函數除了具有建立新的消息隊列的功能外,還可以傳回一個已存在的消息隊列的辨別符。函數msgget的原型為: #include <sys/msg.h> int msgget(key_t key,int msgflg); 參數說明: 1)key:鍵值。該參數的輸入方式有3種,分别是:一、直接指定一個整數;二、用ftok産生一個鍵值作為輸入;三、用IPC_PRIVATE為參數輸入,由系統建立一個可用的隊列。 2)msgflg:标志和權限資訊。通常情況下,在建立一個新的隊列時,用參數IPC_CREAT;而在擷取一個已存在的消息隊列的辨別符時,則将該參數設定為0。該參數包括兩部分内容,一是标志,二是權限。 傳回值: 若失敗,傳回-1;若成功,傳回其他值,該值即是可用的辨別符。 注意事項: 1)如果使用IPC_PRIVATE調用msgget,系統都将建立一個新的消息隊列,即msgget調用将總是傳回成功,并且該消息隊列的鍵值恒為0。由此産生的問題是,在其他程序中不能用鍵值通路該消息隊列,而必須用msgget傳回的辨別符來通路。是以,程序間必須通過某種機制(子程序繼承等)來擷取辨別符,然後通過該辨別符進行消息傳遞和接收。 2)msgget函數不僅有建立消息隊列的功能,而且在消息隊列已存在的情況下,還可以傳回該消息隊列的辨別符。要特别注意IPC_CREAT和IPC_EXCL合用時的含義。

10.3.3 發送消息         消息隊列建立成功後,便可以調用消息發送函數向隊列中發送消息了。消息的發送由函數msgsnd完成,其原型為: #include <sys/msg.h> int msgsnd(int msqid,const void *msgp,size_t msgsz,int msgflg); 參數說明: 1)msqid:消息隊列的辨別符,由msgget函數傳回的辨別符。 2)msgp:輸入參數,消息結構指針,該參數的類型是void*,實際上應該向該參數傳遞一個“消息結構”的指針。不過在傳遞參數的時候強制類型轉換為void*。 3)msgsz:消息的長度,這裡的長度是指真正的消息内容的長度,而不是消息結構的尺寸。 4)msgflg:發送消息可選标志,如果不需要指定任何标志,在這裡輸入0即可。可以選擇的标志隻有IPC_NOWAIT。在預設狀态下,消息的發送操作是阻塞的。一旦由于該消息隊列中存儲的消息已達到最大值或者存儲的消息條數已達到最大值,此時調用msgsnd将阻塞,直到有程序調用msgrcv從隊列中讀出消息。如果選擇了IPC_NOWAIT标志,表明消息發送的過程是非阻塞的。如果隊列已滿,則msgsnd将立即傳回-1。 傳回值: 若成功,傳回0;若失敗,傳回-1。

10.3.4 接收消息         消息已發送到消息隊列中,其他程序如何擷取該消息呢?這就需要用到消息接收的操作。利用消息接收操作可以從消息隊列中讀取指定類型的消息,也可以不指定類型,按照“先進先出”的原則讀取隊列頭部的第一條消息。消息接收的函數是msgrcv,其原型為: #include <sys/msg.h> int msgrcv(int msqid,void *msgp,size_t msgsz,long msgtyp,int msgflg); 參數說明: 1)msqid:消息隊列的辨別符,由msgget函數傳回的辨別符。 2)msgp:輸出參數,消息結構指針,該參數的類型是void*,實際上應該向該參數傳遞一個“消息結構”的指針。 3)msgsz:要讀取的消息長度,在不确定消息長度的情況下,此處可以傳遞一個稍大的長度,系統将讀取相應消息的全部内容,并在msgrcv的傳回值中指明消息的正确長度。如果此處傳遞的要讀取的長度比真正的消息内容長度要小,則預設狀态下将傳回失敗。不過,如果指定了MSG_NOERROR可選标志,也可以成功讀取。 4)msgflg:接收消息可選标志。 傳回值: 若失敗,傳回-1;若成功,傳回實際讀取到的消息内容的位元組數。

10.3.5 控制消息隊列         消息隊列建立成功後,核心不僅建立了消息隊列結構用于存儲消息,并且建立了消息隊列的控制結構(msqid_ds)。該結構中指定了該消息隊列的權限資訊、發送接收消息的程序等資訊。IPC機制提供了專門的函數用于對該結構進行控制,這個函數就是msgctl,其原型為: #include <sys/msg.h> int msgctl(int msqid,int cmd,struct msqid_ds *buff); 參數說明: 1)msqid:消息隊列的辨別符,由msgget函數傳回的辨別符。 2)cmd:控制指令,可取值分别為:0(IPC_RMID)、1(IPC_SET)、2(IPC_STAT)、3(IPC_INFO)。 3)buff:輸入或輸出參數,指向msqid_ds結構的指針。在cmd參數為IPC_SET時,該參數用作輸入;在cmd參數為IPC_STAT時,該參數用作輸出。 傳回值: 若成功,傳回0;若失敗,傳回-1。

例10-1:程式設計實作消息隊列的建立,消息的發送、接收及消息隊列的控制。本例将編寫4個程式(createmsq,sendmsg,recvmsg和ctrlmsq),分别用于建立消息隊列、發送消息、接收消息和控制消息隊列。 createmsq代碼如下: #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> void main() { //鍵值 key_t key; //消息隊列辨別符 int msqid; //生成鍵值 if((key=ftok("/etc/profile",1))==-1) { perror("ftok"); return; } //建立消息隊列 if((msqid=msgget(key,IPC_CREAT|IPC_EXCL|0666))==-1) { //如果建立失敗并且隊列不存在 if(errno!=EEXIST) { perror("msgget"); return; } //如果建立失敗但隊列已經存在,則繼續擷取隊列辨別符 if((msqid=msgget(key,0))==-1) { perror("msgget"); return; } } //建立成功,輸出資訊 printf("ok: msqid=%d\n",msqid); }

sendmsg代碼如下: #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> //自定義消息參數類型 typedef struct { long type;         //消息類型 char name[20]; //消息内容 int id; //消息内容 }MSG; void main(int argc,char *argv[]) { //鍵值 key_t key; //消息隊列辨別符 int msqid; //消息變量 MSG msg; //檢測指令行參數 if(argc!=3) { printf("usage: sendmsg name id\n"); return; } //設定消息内容 memset(&msg,0,sizeof(MSG)); msg.type=1; sscanf(argv[1],"%s",msg.name); sscanf(argv[2],"%d",&msg.id); //生成鍵值 if((key=ftok("/etc/profile",1))==-1) { perror("ftok"); return; } //擷取消息隊列辨別符 if((msqid=msgget(key,0))==-1) { perror("msgget"); return; } //發送消息 if(msgsnd(msqid,&msg,sizeof(MSG)-4,0)<0) //阻塞模式發送 { perror("msgsnd"); return; } //發送成功,輸出資訊 printf("ok: send message successful!\n"); }

recvmsg代碼如下: #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> //自定義消息參數類型 typedef struct { long type;         //消息類型 char name[20]; //消息内容 int id; //消息内容 }MSG; void main() { //鍵值 key_t key; //消息隊列辨別符 int msqid; //消息變量 MSG msg; //生成鍵值 if((key=ftok("/etc/profile",1))==-1) { perror("ftok"); return; } //擷取消息隊列辨別符 if((msqid=msgget(key,0))==-1) { perror("msgget"); return; } //接收消息 memset(&msg,0,sizeof(MSG)); if(msgrcv(msqid,&msg,sizeof(MSG)-4,1,0)<0) { perror("msgrcv"); return; } //接收消息成功,輸出資訊 printf("ok: recveive message: name=%s,id=%d\n",msg.name,msg.id); }

ctrlmsg代碼如下: #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> //自定義消息參數類型 typedef struct { long type; //消息類型 char name[20]; //消息内容 int id; //消息内容 }MSG; void main() { //鍵值 key_t key; //消息隊列辨別符 int msqid; //消息變量 MSG msg; //消息隊列控制結構變量 struct msqid_ds msqds; //生成鍵值 if((key=ftok("/etc/profile",1))==-1) { perror("ftok"); return; } //擷取消息隊列辨別符 if((msqid=msgget(key,0))==-1) { perror("msgget"); return; } //擷取消息隊列狀态 memset(&msqds,0,sizeof(msqds)); if(msgctl(msqid,IPC_STAT,&msqds)<0) { perror("msgctl IPC_STAT"); return; } //擷取消息隊列狀态成功,輸出相關資訊 printf("max msq size=%d bytes\n",msqds.msg_qbytes); //輸出該消息隊列允許存儲的最大長度 printf("current msg count=%d\n",msqds.msg_qnum); //輸出目前該隊列的消息數目 printf("current msg size=%d bytes\n",msqds.__msg_cbytes);  //輸出目前該隊列中的消息總長度 }

說明: 1)編譯連結分别生成可執行程式:createmsq,sendmsg,recvmsg及ctrlmsq。 2)建立消息隊列,執行指令: ./createmsq 3)發送消息,執行指令: ./sendmsg Jim 123 4)接收消息,執行指令: ./recvmsg 5)擷取消息隊列狀态資訊,執行指令: ./ctrlmsq 6)注意觀察控制台的輸出結果。   原文位址: http://blog.163.com/ljf_gzhu/blog/static/131553440201111218430340/  

繼續閱讀