摘要:消息隊列函數由msgget、msgctl、msgsnd、msgrcv四個函數組成。下面的表格列出了這四個函數的函數原型及其具體說明。1. msgget函數原型msgget(得到消息隊列標識符或創建一個消息隊列對象)所需頭文件#include<sys/types.h>#include<sys/ipc.h>#include<sys/msg.h>函數說明得到消息隊列標識符或創建一個消息隊列對象並返回消息隊列標識符函數原型intmsgge
-
消息隊列函數由msgget、msgctl、msgsnd、msgrcv四個函數組成。下面的表格列出了這四個函數的函數原型及其具體說明。
1. msgget函數原型
msgget(得到消息隊列標識符或創建一個消息隊列對象)
所需頭文件
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
函數說明
得到消息隊列標識符或創建一個消息隊列對象並返回消息隊列標識符
函數原型
int msgget(key_t key, int msgflg)
函數傳入值
key
0(IPC_PRIVATE):會建立新的消息隊列
大於0的32位整數:視參數msgflg來確定操作。通常要求此值來源於ftok返回的IPC鍵值
msgflg
0:取消息隊列標識符,若不存在則函數會報錯
IPC_CREAT:當msgflg&;IPC_CREAT為真時,如果內核中不存在鍵值與key相等的消息隊列,則新建一個消息隊列;如果存在這樣的消息隊列,返回此消息隊列的標識符
IPC_CREAT|IPC_EXCL:如果內核中不存在鍵值與key相等的消息隊列,則新建一個消息隊列;如果存在這樣的消息隊列則報錯
函數返回值
成功:返回消息隊列的標識符
出錯:-1,錯誤原因存於error中
附加說明
上述msgflg參數為模式標志參數,使用時需要與IPC對象存取權限(如0600)進行|運算來確定消息隊列的存取權限
錯誤代碼
EACCES:指定的消息隊列已存在,但調用進程沒有權限訪問它
EEXIST:key指定的消息隊列已存在,而msgflg中同時指定IPC_CREAT和IPC_EXCL標志
ENOENT:key指定的消息隊列不存在同時msgflg中沒有指定IPC_CREAT標志
ENOMEM:需要建立消息隊列,但內存不足
ENOSPC:需要建立消息隊列,但已達到系統的限制
如果用msgget創建了一個新的消息隊列對象時,則msqid_ds結構成員變量的值設置如下:
msg_qnum、msg_lspid、msg_lrpid、 msg_stime、msg_rtime設置為0。
msg_ctime設置為當前時間。
msg_qbytes設成系統的限制值。
msgflg的讀寫權限寫入msg_perm.mode中。
msg_perm結構的uid和cuid成員被設置成當前進程的有效用戶ID,gid和cuid成員被設置成當前進程的有效組ID。
2. msgctl函數原型
msgctl (獲取和設置消息隊列的屬性)
所需頭文件
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
函數說明
獲取和設置消息隊列的屬性
函數原型
int msgctl(int msqid, int cmd, struct msqid_ds *buf)
函數傳入值
msqid
消息隊列標識符
cmd
IPC_STAT:獲得msgid的消息隊列頭數據到buf中
IPC_SET:設置消息隊列的屬性,要設置的屬性需先存儲在buf中,可設置的屬性包括:msg_perm.uid、msg_perm.gid、msg_perm.mode以及msg_qbytes
buf:消息隊列管理結構體,請參見消息隊列內核結構說明部分
函數返回值
成功:0
出錯:-1,錯誤原因存於error中
錯誤代碼
EACCESS:參數cmd為IPC_STAT,確無權限讀取該消息隊列
EFAULT:參數buf指向無效的內存地址
EIDRM:標識符為msqid的消息隊列已被刪除
EINVAL:無效的參數cmd或msqid
EPERM:參數cmd為IPC_SET或IPC_RMID,卻無足夠的權限執行
3. msgsnd函數原型
msgsnd (將消息寫入到消息隊列)
所需頭文件
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
函數說明
將msgp消息寫入到標識符為msqid的消息隊列
函數原型
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg)
函數傳入值
msqid
消息隊列標識符
msgp
發送給隊列的消息。msgp可以是任何類型的結構體,但第一個字段必須為long類型,即表明此發送消息的類型,msgrcv根據此接收消息。msgp定義的參照格式如下:
struct s_msg{ /*msgp定義的參照格式*/
long type; /* 必須大於0,消息類型 */
char mtext[256]; /*消息正文,可以是其他任何類型*/
} msgp;msgsz
要發送消息的大小,不含消息類型占用的4個字節,即mtext的長度
msgflg
0:當消息隊列滿時,msgsnd將會阻塞,直到消息能寫進消息隊列
IPC_NOWAIT:當消息隊列已滿的時候,msgsnd函數不等待立即返回
IPC_NOERROR:若發送的消息大於size字節,則把該消息截斷,截斷部分將被丟棄,且不通知發送進程。
函數返回值
成功:0
出錯:-1,錯誤原因存於error中
錯誤代碼
EAGAIN:參數msgflg設為IPC_NOWAIT,而消息隊列已滿
EIDRM:標識符為msqid的消息隊列已被刪除
EACCESS:無權限寫入消息隊列
EFAULT:參數msgp指向無效的內存地址
EINTR:隊列已滿而處於等待情況下被信號中斷
EINVAL:無效的參數msqid、msgsz或參數消息類型type小於0
msgsnd()為阻塞函數,當消息隊列容量滿或消息個數滿會阻塞。消息隊列已被刪除,則返回EIDRM錯誤;被信號中斷返回E_INTR錯誤。
如果設置IPC_NOWAIT消息隊列滿或個數滿時會返回-1,並且置EAGAIN錯誤。
msgsnd()解除阻塞的條件有以下三個條件:
① 不滿足消息隊列滿或個數滿兩個條件,即消息隊列中有容納該消息的空間。
② msqid代表的消息隊列被刪除。
③ 調用msgsnd函數的進程被信號中斷。
4. msgrcv函數原型
msgrcv (從消息隊列讀取消息)
所需頭文件
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
函數說明
從標識符為msqid的消息隊列讀取消息並存於msgp中,讀取后把此消息從消息隊列中刪除
函數原型
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
int msgflg);
函數傳入值
msqid
消息隊列標識符
msgp
存放消息的結構體,結構體類型要與msgsnd函數發送的類型相同
msgsz
要接收消息的大小,不含消息類型占用的4個字節
msgtyp
0:接收第一個消息
>0:接收類型等於msgtyp的第一個消息
<0:接收類型等於或者小於msgtyp絕對值的第一個消息
msgflg
0: 阻塞式接收消息,沒有該類型的消息msgrcv函數一直阻塞等待
IPC_NOWAIT:如果沒有返回條件的消息調用立即返回,此時錯誤碼為ENOMSG
IPC_EXCEPT:與msgtype配合使用返回隊列中第一個類型不為msgtype的消息
IPC_NOERROR:如果隊列中滿足條件的消息內容大於所請求的size字節,則把該消息截斷,截斷部分將被丟棄
函數返回值
成功:實際讀取到的消息數據長度
出錯:-1,錯誤原因存於error中
錯誤代碼
E2BIG:消息數據長度大於msgsz而msgflag沒有設置IPC_NOERROR
EIDRM:標識符為msqid的消息隊列已被刪除
EACCESS:無權限讀取該消息隊列
EFAULT:參數msgp指向無效的內存地址
ENOMSG:參數msgflg設為IPC_NOWAIT,而消息隊列中無消息可讀
EINTR:等待讀取隊列內的消息情況下被信號中斷
msgrcv()解除阻塞的條件有以下三個:
① 消息隊列中有了滿足條件的消息。
② msqid代表的消息隊列被刪除。
③ 調用msgrcv()的進程被信號中斷。
消息隊列使用程序范例
5. 消息隊列控制范例
msgctl.c源代碼如下:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <unistd.h> 5 #include <sys/ipc.h> 6 #include <sys/msg.h> 7 #include <error.h> 8 9 #define TEXT_SIZE 512 10 struct msgbuf 11 { 12 long mtype; 13 char mtext[TEXT_SIZE]; 14 }; 15 16 int main(int argc, char **argv) 17 { 18 int msqid ; 19 struct msqid_ds info ; 20 struct msgbuf buf ; 21 struct msgbuf buf1 ; 22 int flag ; 23 int sendlength, recvlength ; 24 25 msqid = msgget( IPC_PRIVATE, 0666 ) ; 26 if ( msqid < 0 ) 27 { 28 perror("get ipc_id error") ; 29 return -1 ; 30 } 31 32 buf.mtype = 1 ; 33 strcpy(buf.mtext, "happy new year!") ; 34 sendlength = sizeof(struct msgbuf) - sizeof(long) ; 35 flag = msgsnd( msqid, &buf, sendlength , 0 ) ; 36 if ( flag < 0 ) 37 { 38 perror("send message error") ; 39 return -1 ; 40 } 41 buf.mtype = 3 ; 42 strcpy(buf.mtext, "good bye!") ; 43 sendlength = sizeof(struct msgbuf) - sizeof(long) ; 44 flag = msgsnd( msqid, &buf, sendlength , 0 ) ; 45 if ( flag < 0 ) 46 { 47 perror("send message error") ; 48 return -1 ; 49 } 50 51 flag = msgctl( msqid, IPC_STAT, &info ) ; 52 if ( flag < 0 ) 53 { 54 perror("get message status error") ; 55 return -1 ; 56 } 57 printf("uid:%d, gid = %d, cuid = %d, cgid= %d\n" , 58 info.msg_perm.uid, info.msg_perm.gid, info.msg_perm.cuid, info.msg_perm.cgid ) ; 59 printf("read-write:%03o, cbytes = %lu, qnum = %lu, qbytes= %lu\n" , 60 info.msg_perm.mode&0777, info.msg_cbytes, info.msg_qnum, info.msg_qbytes ) ; 61 system("ipcs -q") ; 62 recvlength = sizeof(struct msgbuf) - sizeof(long) ; 63 memset(&buf1, 0x00, sizeof(struct msgbuf)) ; 64 65 flag = msgrcv( msqid, &buf1, recvlength ,3,0 ) ; 66 if ( flag < 0 ) 67 { 68 perror("recv message error") ; 69 return -1 ; 70 } 71 printf("type=%ld, message=%s\n", buf1.mtype, buf1.mtext) ; 72 73 flag = msgctl( msqid, IPC_RMID,NULL) ; 74 if ( flag < 0 ) 75 { 76 perror("rm message queue error") ; 77 return -1 ; 78 } 79 system("ipcs -q") ; 80 81 return 0 ; 82 }
編譯 gcc msgctl.c –o msgctl。
執行 ./msg,執行結果如下:
uid:1008, gid = 1003, cuid = 1008, cgid= 1003
read-write:666, cbytes = 1024, qnum = 2, qbytes= 163840
------ Message Queues --------
key msqid owner perms used-bytes messages
0x00000000 65536 zjkf 666 1024 2
type=3, message=good bye!
------ Message Queues --------
key msqid owner perms used-bytes messages
6. 兩進程通過消息隊列收發消息
(1)發送消息隊列程序
msgsnd.c源代碼如下:
1 #include <stdio.h> 2 #include <string.h> 3 #include <stdlib.h> 4 #include <unistd.h> 5 #include <sys/types.h> 6 #include <sys/ipc.h> 7 #include <sys/msg.h> 8 #include <time.h> 9 10 #define TEXT_SIZE 512 11 struct msgbuf 12 { 13 long mtype ; 14 int status ; 15 char time[20] ; 16 char mtext[TEXT_SIZE] ; 17 } ; 18 char *getxtsj() 19 { 20 time_t tv ; 21 struct tm *tmp ; 22 static char buf[20] ; 23 tv = time( 0 ) ; 24 tmp = localtime(&tv) ; 25 sprintf(buf,"%02d:%02d:%02d",tmp->tm_hour , tmp->tm_min,tmp->tm_sec); 26 return buf ; 27 } 28 29 int main(int argc, char **argv) 30 { 31 int msqid ; 32 struct msqid_ds info ; 33 struct msgbuf buf ; 34 struct msgbuf buf1 ; 35 int flag ; 36 int sendlength, recvlength ; 37 int key ; 38 39 key = ftok("msg.txt", 0x01 ) ; 40 if ( key < 0 ) 41 { 42 perror("ftok key error") ; 43 return -1 ; 44 } 45 46 msqid = msgget( key, 0600|IPC_CREAT ) ; 47 if ( msqid < 0 ) 48 { 49 perror("create message queue error") ; 50 return -1 ; 51 } 52 53 buf.mtype = 1 ; 54 buf.status = 9 ; 55 strcpy(buf.time, getxtsj()) ; 56 strcpy(buf.mtext, "happy new year!") ; 57 sendlength = sizeof(struct msgbuf) - sizeof(long) ; 58 flag = msgsnd( msqid, &buf, sendlength , 0 ) ; 59 if ( flag < 0 ) 60 { 61 perror("send message error") ; 62 return -1 ; 63 } 64 buf.mtype = 3 ; 65 buf.status = 9 ; 66 strcpy(buf.time, getxtsj()) ; 67 strcpy(buf.mtext, "good bye!") ; 68 sendlength = sizeof(struct msgbuf) - sizeof(long) ; 69 flag = msgsnd( msqid, &buf, sendlength , 0 ) ; 70 if ( flag < 0 ) 71 { 72 perror("send message error") ; 73 return -1 ; 74 } 75 system("ipcs -q") ; 76 return 0 ; 77 }
(2)接收消息隊列程序
msgrcv.c源代碼如下:
1 #include <stdio.h> 2 #include <string.h> 3 #include <stdlib.h> 4 #include <unistd.h> 5 #include <sys/types.h> 6 #include <sys/ipc.h> 7 #include <sys/msg.h> 8 9 #define TEXT_SIZE 512 10 struct msgbuf 11 { 12 long mtype ; 13 int status ; 14 char time[20] ; 15 char mtext[TEXT_SIZE] ; 16 }; 17 int main(int argc, char **argv) 18 { 19 int msqid ; 20 struct msqid_ds info ; 21 struct msgbuf buf1 ; 22 int flag ; 23 int recvlength ; 24 int key ; 25 int mtype ; 26 27 key = ftok("msg.txt", 0x01 ) ; 28 if ( key < 0 ) 29 { 30 perror("ftok key error") ; 31 return -1 ; 32 } 33 34 msqid = msgget( key, 0 ) ; 35 if ( msqid < 0 ) 36 { 37 perror("get ipc_id error") ; 38 return -1 ; 39 } 40 41 recvlength = sizeof(struct msgbuf) - sizeof(long) ; 42 memset(&buf1, 0x00, sizeof(struct msgbuf)) ; 43 mtype = 1 ; 44 flag = msgrcv( msqid, &buf1, recvlength ,mtype,0 ) ; 45 if ( flag < 0 ) 46 { 47 perror("recv message error") ; 48 return -1 ; 49 } 50 printf("type=%ld,time=%s, message=%s\n", buf1.mtype, buf1.time, buf1.mtext) ; 51 system("ipcs -q") ; 52 return 0 ; 53 }
(3)編譯與執行程序
① 在當前目錄下利用>msg.tmp建立空文件msg.tmp。
② 編譯發送消息隊列程序 gcc msgsnd.c -o msgsnd。
③ 執行./msgsnd,執行結果如下:
----- Message Queues --------
key msqid owner perms used-bytes messages
0x0101436d 294912 zjkf 600 1072 2
④ 編譯接收消息程序 gcc msgrcv.c -o msgrcv
⑤ 執行./msgrcv,執行結果如下:
type=1,time=03:23:16, message=happy new year!
------ Message Queues --------
key msqid owner perms used-bytes messages
0x0101436d 294912 zjkf 600 536 1
⑥ 利用ipcrm -q 294912刪除該消息隊列。因為消息隊列是隨內核持續存在的,在程序中若不利用msgctl函數或在命令行用ipcrm命令顯式地刪除,該消息隊列就一直存在於系統中。另外信號量和共享內存也是隨內核持續存在的。