#include<sys/types.h>
#include<sys/msg.h>
int msgget(key_t key,int msgflg);
//return message queue identifier on success,or -1 on error
複製代碼
key: 參數能夠設置值爲IPC_PRIVATE,系統會建立一個全新的IPC對象,或者使用ftok()函數生成一個(接近惟一)key,key相同的狀況下,系統會返回一個已建立相同key的IPC對象。程序員
msgflg:指定施加於新消息隊列之上的權限和檢查一個既有隊列的權限的位掩碼(相似與文件權限)安全
建立一個消息隊列bash
int msqid=msgget(IPC_PRIVATE,IPC_CREAT|S_IRUSR|S_IWUSR);//Read and Write by owner
if(msqid==-1){
errExit("msgget");
}
複製代碼
msgsnd()和msgrcv()系統調用執行消息隊列上的I/O。這兩個系統調用接收的第一個參數是消息隊列標識符(msqid)。第二個是參數msgp是一個由程序員定義的結構的指針,該結構用於存放被髮送或接受的消息,結構的常規形式以下:服務器
struct msg{
long type;//message type
void* body;//message body
...
}
複製代碼
消息的第一個部分必須指明瞭消息的類型,它用一個類型爲long的整數來表示,而消息的剩餘部分則是自定義的一個結構,其長度、內容、字段名和類型都均可以是任意的,也能夠多個字段。數據結構
需特別指出:消息的大小是除了type字段外的全部字段的大小socket
從消息隊列中寫入消息須要隊列上的寫權限ide
#include<sys/types.h>
#include<sys/msg.h>
int msgsnd(int msqid,const void *msgp,size_t msgsz, int msgflg);
//return 0 on success,or -1 on error
複製代碼
使用msgsnd()發送消息必需要將消息結構中的type字段值設爲一個大於0的值並將須要傳遞的信息複製到自定義的body字段中。函數
msqid隊列的標識符ui
msgp消息的結構體指針spa
msgsz參數指定了body字段包含的字節數,即消息的大小。
msgflg是一組標記的位掩碼,用於控制msgsnd()操做
使用msgsnd()發送一條消息
struct mbuf{
long mtype; //message type
char mtext[1024]; //message body
}
struct mbuf msg;
int msqid,msgLen;
...
msgLen=strlen(msg.mtext);
if(msgsnd(msqid,&msg,msgLen,IPC_NOWAIT)==-1){
errExit("msgsnd");
}
...
複製代碼
從消息隊列中讀取消息須要隊列上的讀權限
#include<sys/types.h>
#include<sys/msg.h>
ssize_t msgrcv(int msqid,void *msgp,size_t maxmsgsz,long msgtyp,int msgflg);
//return number of bytes copied into body field, or -1 on error
複製代碼
msqid 隊列的標識符
maxmsgsz 參數值要大於或等於需讀取的消息的大小
msgp 緩衝區中消息的最大可用空間是經過maxmsgz參數來指定的。若是隊列中待刪除的消息體的大小超過了maxmsgsz字節,那麼就不會從隊列中刪除消息,而且會返回錯誤E2BIG。
mtype 讀取消息的順序能夠根據mtype字段來選擇
mshtyp==0 將會刪除隊列中的第一條消息並將其返回給調用進程。
msgtyoe>0 將隊列中第一條消息裏的type字段值等於msgtype的消息刪除並返回給進程。能夠用於讓各個進程選取與本身的進程ID匹配的消息,這樣就會競爭讀取同一條消息。
msgtype<0 將隊列變成優先隊列即最小堆形式。隊列中消息type最小而且其值小於或等於msgtype的絕對值的第一條消息刪除並返回給調用進程,若是沒有,則堵塞直到出現匹配的消息爲止。
msgflg 控制msgrcv()操做
使用msgrcv()讀取一條消息
struct mbuf{
long mtype;
char mtext[1024];
}
int msqid,msgLen;
struct mbuf msg;
....
msgLen=msgrcv(msqid,&msg,1024,0,IPC_NOWAIT);
if(msgLen==-1){
errExit("msgrcv");
}
複製代碼
#include<sys/types.h>
#include<sys/msg.h>
int msgctl(int msqid,int cmd,struct msqid_ds *buf);
// return 0 on success,or -1 on error
複製代碼
還有一些其餘的cmd參數,感興趣能夠網上查看下,這裏只列出常見參數。
使用msgctl刪除System V 消息隊列
...
if(msgctl(msqid,IPC_RMID,NULL)==-1){
errExit("msgctl");
}
複製代碼
struct msqid_ds
{
struct ipc_perm msg_perm; /* structure describing operation permission */
__time_t msg_stime; /* time of last msgsnd command */
__time_t msg_rtime; /* time of last msgrcv command */
__time_t msg_ctime; /* time of last change */
__syscall_ulong_t __msg_cbytes; /* current number of bytes on queue */
msgqnum_t msg_qnum; /* number of messages currently on queue */
msglen_t msg_qbytes; /* max number of bytes allowed on queue */
__pid_t msg_lspid; /* pid of last msgsnd() */
__pid_t msg_lrpid; /* pid of last msgrcv() */
__syscall_ulong_t __glibc_reserved4;
__syscall_ulong_t __glibc_reserved5;
};
struct ipc_perm
{
__key_t __key; /* Key. */
__uid_t uid; /* Owner's user ID. */ __gid_t gid; /* Owner's group ID. */
__uid_t cuid; /* Creator's user ID. */ __gid_t cgid; /* Creator's group ID. */
unsigned short int mode; /* Read/write permission. */
unsigned short int __pad1;
unsigned short int __seq; /* Sequence number. */
unsigned short int __pad2;
__syscall_ulong_t __glibc_reserved1;
__syscall_ulong_t __glibc_reserved2;
};
複製代碼
修改一個System V 消息隊列的msg_qbytes 設置
...
struct msqid_ds ds;
int msqid;
...
if(msgctl(msqid,IPC_STAT,&ds)==-1){
errExit("msgctl");
}
ds.msg_qbytes=1048576 //1MB
if(msgctl(msqid,IPC_SET,&ds)==-1){
errExit("msgctl");
}
複製代碼
還有一些其餘的限制,感興趣能夠網上查看下,這裏只列出常見限制。
Linux 特有的msgctl() IPC_INFO 操做可以獲取一個類型爲msginfo的結構,其中包含了各類消息隊列限制的值
struct msginfo buf;
msgctl(0, IPC_INFO,(struct msqid_ds *)&buf);
/* buffer for msgctl calls IPC_INFO, MSG_INFO */
struct msginfo
{
int msgpool;
int msgmap;
int msgmax;
int msgmnb;
int msgmni;
int msgssz;
int msgtql;
unsigned short int msgseg;
};
複製代碼
服務器端核心代碼
...
for(;;){
msgLen=msgcrv(serverId,&req,REQ_MSG_SIZE,0,0);
if(msgLen==-1){
if(errno==EINTR)//Interrupted by SIGCHLD handler?
continue;
errMsg("msgrcv");
break;
}
pid=fork();
if(pid==-1){
errMsg("fork");
break;
}
if(pid==0){
serveRequest(&req);
_exit(EXIT_SUCCESS);
}
}
...
複製代碼
客戶端核心代碼
...
clientId=msgget(IPC_PRIVATE,S_IRUSR|S_IWUSR|S_IWGRP);//確保服務端可以有寫權限
...
msgLen=msgrcv(clientId,&req,RESP_MSG_SIZE,0,0);
if(msgLen==-1){
errExit("msgrcv");
}
...
for(;;){
msgLen=msgrcv(clientId,&resp,RESP_MSG_SIZE,0,0);
if(msgLen==-1){
errExit("msgrcv");
}
...
}
...
複製代碼
消息隊列是經過標識符引用的,而不是像大多數其餘UNIX I/O機制那樣使用文件描述符。這意味這在各類基於文件描述符的I/O技術(如select()、poll()以及epoll)將沒法應用於消息隊列上。此外,在程序中編寫同時處理消息隊列的輸入和基於文件描述符的I/O機制的代碼要比編寫只處理文件描述符的代碼要更加複雜。
使用鍵而不是文件名來標識消息隊列會增長額外的程序設計複雜性。ftok()函數一般能產生一個惟一的鍵,但卻沒法保證,使用IPC_PRIVATE鍵能確保產生惟一的隊列標識符,但須要使這個標識符對須要用到它的其餘進程可見。
消息隊列是無鏈接的,內核不會對待管道、FIFO以及socket那樣維護引用隊列的進程數,會帶來幾個問題:
消息隊列的總數、消息的大小以及單個隊列的容量都是有限制的。這些限制都是可配置的,但若是一個應用程序超出了這些默認限制的範圍,那麼安裝應用程序的時候就須要完成一些額外的工做了。