一、消息隊列簡介
消息隊列是由存放在內核中的消息組成的鏈表,由IPC id標識。
顧名思義,消息隊列就是依稀消息的列表。用戶可以在消息隊列中添加消息和讀取消息等。從這點看,消息隊列具有一定的FIFO特性,但是它可以實現消息的隨機查詢。消息隊列是由存放在內核中的消息組成的鏈表,由IPC id標識。
消息隊列是鏈表隊列,它通過內核提供一個struct msqid_ds *msgque[MSGMNI]向量維護內核的一個消息隊列列表,因此linux系統支持的大消息隊列數由msgque數組大小來決定,每一個msqid_ds表示一個消息隊列,并通過msqid_ds.msg_first、msg_last維護一個先進先出的msg鏈表隊列,當發送一個消息到該消息隊列時,把發送的消息構造成一個msg結構對象,并添加到msqid_ds.msg_first、msg_last維護的鏈表隊列,同樣,接收消息的時候也是從msg鏈表隊列尾部查找到一個msg_type匹配的msg節點,從鏈表隊列中刪除該msg節點,并修改msqid_ds結構對象的數據
二、消息隊列的實現
消息隊列的實現包括創建或打開消息隊列、添加消息、讀取消息和控制消息隊列這四種操作。
由msgget創建新隊列或打開已經存在的隊列
由msgsnd將消息添加到消息隊列尾,每個消息包括正整數標識的類型,非負的長度,及數據。
由msgrcv從消息隊列中取消息,不必按FIFO取消息,可以通過類型字段取相應的消息。
由msgctl控制消息隊列。
三、消息隊列函數介紹
1、msgget函數(創建新消息隊列或取得已存在消息隊列)
該函數用來創建和訪問一個消息隊列。它的原型為:
int msgget(key_t, key, int msgflg);
參數:
key:可以認為是一個端口號,也可以由函數ftok生成。
msgflg:IPC_CREAT值,若沒有該隊列,則創建一個并返回新標識符;若已存在,則返回原標識符。
IPC_EXCL值,若沒有該隊列,則返回-1;若已存在,則返回0。
與其他的IPC機制一樣,程序必須提供一個鍵來命名某個特定的消息隊列。msgflg是一個權限標志,表示消息隊列的訪問權限,它與文件的訪問權限一樣。msgflg可以與IPC_CREAT做或操作,表示當key所命名的消息隊列不存在時創建一個消息隊列,如果key所命名的消息隊列存在時,IPC_CREAT標志會被忽略,而只返回一個標識符。
它返回一個以key命名的消息隊列的標識符(非零整數),失敗時返回-1.
2、msgsnd函數(將數據放到消息隊列中)
該函數用來把消息添加到消息隊列中。它的原型為:
int msgsend(int msgid, const void *msg_ptr, size_t msg_sz, int msgflg);
msgid是由msgget函數返回的消息隊列標識符。
msg_ptr是一個指向準備發送消息的指針,但是消息的數據結構卻有一定的要求,指針msg_ptr所指向的消息結構一定要是以一個長整型成員變量開始的結構體,接收函數將用這個成員來確定消息的類型。所以消息結構要定義成這樣:
struct my_message{
long int message_type;
/* The data you wish to transfer*/
};
msg_sz是msg_ptr指向的消息的長度,注意是消息的長度,而不是整個結構體的長度,也就是說msg_sz是不包括長整型消息類型成員變量的長度。
msgflg用于控制當前消息隊列滿或隊列消息到達系統范圍的限制時將要發生的事情。
如果調用成功,消息數據的一分副本將被放到消息隊列中,并返回0,失敗時返回-1.
3、msgrcv函數(從隊列中取用消息)
該函數用來從一個消息隊列獲取消息,它的原型為
int msgrcv(int msgid, void *msg_ptr, size_t msg_st, long int msgtype, int msgflg;
msgid, msg_ptr, msg_st的作用也函數msgsnd函數的一樣。
msgtype可以實現一種簡單的接收優先級。如果msgtype為0,就獲取隊列中的第一個消息。如果它的值大于零,將獲取具有相同消息類型的第一個信息。如果它小于零,就獲取類型等于或小于msgtype的絕對值的第一個消息。
msgflg用于控制當隊列中沒有相應類型的消息可以接收時將發生的事情。
調用成功時,該函數返回放到接收緩存區中的字節數,消息被復制到由msg_ptr指向的用戶分配的緩存區中,然后刪除消息隊列中的對應消息。失敗時返回-1.
4、msgctl函數(設置消息隊列屬性)
該函數用來控制消息隊列,它與共享內存的shmctl函數相似,它的原型為:
int msgctl(int msgid, int command, struct msgid_ds *buf);
command是將要采取的動作,它可以取3個值,
IPC_STAT:把msgid_ds結構中的數據設置為消息隊列的當前關聯值,即用消息隊列的當前關聯值覆蓋msgid_ds的值。
IPC_SET:如果進程有足夠的權限,就把消息列隊的當前關聯值設置為msgid_ds結構中給出的值
IPC_RMID:刪除消息隊列
buf是指向msgid_ds結構的指針,它指向消息隊列模式和訪問權限的結構。msgid_ds結構至少包括以下成員:
struct msgid_ds
{
uid_t shm_perm.uid;
uid_t shm_perm.gid;
mode_t shm_perm.mode;
};
成功時返回0,失敗時返回-1.
四、實例:
消息發送端:send.c
/*send.c*/
#include
#include
#include
#include
#include
#define MSGKEY 1024
struct msgstru
{
long msgtype;
char msgtext[2048];
};
main()
{
struct msgstru msgs;
int msg_type;
char str[256];
int ret_value;
int msqid;
msqid=msgget(MSGKEY,IPC_EXCL); /*檢查消息隊列是否存在*/
if(msqid < 0){
msqid = msgget(MSGKEY,IPC_CREAT|0666);/*創建消息隊列*/
if(msqid <0){
printf("failed to create msq | errno=%d [%s]\n",errno,strerror(errno));
exit(-1);
}
}
while (1){
printf("input message type(end:0):");
scanf("%d",&msg_type);
if (msg_type == 0)
break;
printf("input message to be sent:");
scanf ("%s",str);
msgs.msgtype = msg_type;
strcpy(msgs.msgtext, str);
/* 發送消息隊列 */
ret_value = msgsnd(msqid,&msgs,sizeof(struct msgstru),IPC_NOWAIT);
if ( ret_value < 0 ) {
printf("msgsnd() write msg failed,errno=%d[%s]\n",errno,strerror(errno));
exit(-1);
}
}
msgctl(msqid,IPC_RMID,0); //刪除消息隊列
}
消息接收端 receive.c
/*receive.c */
#include
#include
#include
#include
#include
#define MSGKEY 1024
struct msgstru
{
long msgtype;
char msgtext[2048];
};
/*子進程,監聽消息隊列*/
void childproc(){
struct msgstru msgs;
int msgid,ret_value;
char str[512];
while(1){
msgid = msgget(MSGKEY,IPC_EXCL );/*檢查消息隊列是否存在 */
if(msgid < 0){
printf("msq not existed! errno=%d [%s]\n",errno,strerror(errno));
sleep(2);
continue;
}
/*接收消息隊列*/
ret_value = msgrcv(msgid,&msgs,sizeof(struct msgstru),0,0);
printf("text=[%s] pid=[%d]\n",msgs.msgtext,getpid());
}
return;
}
void main()
{
int i,cpid;
/* create 5 child process */
for (i=0;i<5;i++){
cpid = fork();
if (cpid < 0)
printf("fork failed\n");
else if (cpid ==0) /*child process*/
childproc();
}
}