- 什麼是消息隊列?
消息隊列是消息的鏈接表,儲存在內核中,由消息隊列標識符標識。每個數據塊都被認爲有一個類型。接受者進程接收的數據塊可以有不同的類型值。 - 消息隊列的特點
消息隊列不同於管道,消息隊列是基於數據塊的,而管道是基於字節流的,並且消息隊列讀取不一定要先入先出,可以根據數據類型讀取。還有就是因爲管道是隨進程的,進程結束管道生命週期也就結束,而對於消息隊列來說,是隨內核的,就算進程退出,不去手動的釋放消息隊列,消息隊列依然是存在的。
可以用下面連個命令查看消息隊列和釋放消息隊列:
ipcs -q //查看
ipcrm -q msgid//刪除 - 消息的數據結構
每個隊列都有一個msqid_ds結構與其相對應。
消息隊列是鏈式隊列,它通過內核提供一個struct msqid_ds *msque[MSGMNI]向量維護內核的洗個消息隊列列表,每個msqid_ds表示一個消息隊列,系統最多可以用MSGSNI個消息隊列。
msg_ds用來設置或返回消息隊列的消息,存在與用戶空間。
另外還有msg_queue用來描述消息隊列頭,存在於系統空間。
另外我們可以看到msg_ds的第一個就是一個ipc_perm結構體
IPC對象數據結構:內核爲每個IPC對象維護⼀個數據結構(/usr/include/linux/ipc.h)
消息隊列,共享內存和信號量都有這樣⼀個共同的數據結構。可以通過這個結構體來訪問IPC對象的key值。
- 消息隊列的創建或訪問:msgget函數
該函數用來創建或訪問消息隊列。原型爲:
#include<sys/msg.h>
int msgget(key_t key, int msgflg);
返回值:若成功,返回消息隊列ID;若出錯返回-1;
與其他IPC機制一樣,需要提供一個鍵值key來命名某個特定的消息隊列。可通過ftok()來生成。
msgflg表示消息隊列訪問權限。可與兩個宏配合進行操作:
IPC_CREAT:如果不存在消息隊列鍵值爲key那麼就創建一個鍵值爲key的消息隊列,如果存在,則進行打開此消息隊列。
IPC_EXCL:一般與IPC_CREAT一起使用,msgget(key,IPC_CREAT|IPC_EXCL),如果該IPC已存在,則返回-1,一起使用可以保證IPC對象是新創建的不是打開已有對象。
- 將數據放在消息隊列中:msgend
#include<sys/msg.h>
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
返回值:若成功,返回0;若出錯,返回-1;
參數:
1)msqid:是msgget返回的消息隊列ID。
2)msgp:是一個指向準備發送消息的指針,但是消息的數據結構卻有一定的要求,指針msg_ptr所指向的消息結構一定要是以一個長整型成員變量開始的結構體,接收函數將用這個成員來確定消息的類型。例如:
struct mymsgp{
long mtype;
char mtext[512];
};
這向mymsgp結構的指針。
3)msgsz:是msgp指向的消息的長度,注意是消息的長度,而不是整個結構體的長度,也就是說msgsz是不包括長整型消息類型成員變量的長度
4)msgflg:用於控制當前消息隊列滿或隊列消息到達系統範圍的限制時將要發生的事情。
如果調用成功,消息數據的一分副本將被放到消息隊列中,並返回0,失敗時返回-1。
- 從消息隊列中取用消息:msgrcv
原型:
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
int msgflg);
返回值:若成功,返回消息數據部分的長度;若出錯,返回-1;
參數:
msqid,msgp,msgsz的作用和函數msgsnd函數的一樣
1)msgtyp可以實現簡單的接受優先級。
msgtyp==0 返回隊列中的第一個消息。
msgtyp>0 返回隊列中消息類型爲msgtyp的第一個消息。
msgtyp<0 返回隊列中消息類型值小於等於msgtyp絕對值的消息,如果有多個,則取類型值最小的消息。
2)msgflg用於控制當隊列中沒有相應類型的消息時將發生的事情。
- 消息隊列控制函數:msgctl
原型:
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
返回值:若成功,返回0;若失敗,返回-1
參數:
1)msqid是msgget的返回值。
2)cmd:cmd參數指定將要執行的動作、命令。它可以取三個值。
IPC_STAT:取此消息隊列的msqid_ds結構,並將它存放在buf指向的結構裏面。
IPC_SET:將字段msg_perm.uid、msg_perm.gid、msg_perm.mode和msg_qbytes從buf指向的結構複製到與這個隊列相關的msqid_ds結構中。(執行只能是有效ID等於msg_perm.cuid或uid的進程,或者超級用戶權限的進程)
IPC_RMID:從系統中刪除該消息隊列以及仍在該隊列中的所以數據。
三個命令也可用於信號量和共享存儲中。
3)buf:buf是指向msqid_ds的結構的指針。
- 代碼實例:
msgsend.c
#include<stdio.h>
#include<stdlib.h>
#include<sys/msg.h>
#include<unistd.h>
#include<error.h>
#include<string.h>
struct mymesg{
long mtype;
char mtext[512];
};
int main()
{
int msgid=-1;
msgid=msgget(1234,0666|IPC_CREAT);
struct mymesg data;
if(msgid==-1)
{
perror("msgget is faild!!!\n");
exit(1);
}
while(1)
{
printf("007say:");
fgets(data.mtext,512,stdin);
if(msgsnd(msgid,&data,sizeof(data.mtext),0)==-1)
{
perror("send is faild\n");
exit(1);
}
if(strncmp(data.mtext,"end",3)==0)
{
printf("007 bye");
break;
}
memset(data.mtext,0,sizeof(data.mtext));
if(msgrcv(msgid,&data,sizeof(data.mtext),0,0)==-1)
{
perror("msgrcv is faild\n");
exit(1);
}
printf("008say:%s",data.mtext);
if(strncmp(data.mtext,"end",3)==0)
break;
}
return 0;
}
receive.c
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/msg.h>
#include<string.h>
struct mymesg{
long mtype;
char mtext[512];
};
int main()
{
int msgid=-1;
struct mymesg data;
memset(data.mtext,0,sizeof(data.mtext));
msgid=msgget(1234,IPC_EXCL|IPC_CREAT|0666);
if(msgid==-1)
{
perror("msgget is error!!\n");
exit(1);
}
while(1)
{
if(msgrcv(msgid,&data,sizeof(data.mtext),0,0)==-1)
{
perror("msgrcv is faild\n");
exit(1);
}
printf("007:%s",data.mtext);
printf("008:");
memset(data.mtext,0,sizeof(data.mtext));
fgets(data.mtext,512,stdin);
if(msgsnd(msgid,&data,sizeof(data.mtext),0)==-1)
{
perror("send is faild\n");
exit(1);
}
if(strncmp(data.mtext,"end",3)==0)
{
printf("007 bye!\n");
break;
}
}
if(msgctl(msgid,IPC_RMID,0)==-1)
{
perror("msgctl is faild\n");
exit(1);
}
return 0;
}