標籤:ipc
【著作權聲明:尊重原創,轉載請保留出處:blog.csdn.net/shallnet 或 .../gentleliu,文章僅供學習交流,請勿用於商業用途】
system V訊息佇列和posix訊息佇列類似,linux系統這兩種訊息佇列都支援。先來看一下system V訊息佇列相關操作及其函數。
- msgget()函數建立一個訊息佇列或開啟一個訊息佇列。
#include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> int msgget(key_t key, int msgflg);
參數key為ftok返回值或IPC_PRIVATE;參數msgflg為IPC_CREAT或和IPC_CREAT | IPC_EXCL,當訊息佇列已經存在時,而msgflg指定為IPC_CREAT | IPC_EXCL,函數返回EEXIST。在系統中的每一個訊息佇列,核心維護這樣一個結構體:
struct msqid_ds { struct ipc_perm msg_perm; /* Ownership and permissions */ time_t msg_stime; /* Time of last msgsnd(2) */ time_t msg_rtime; /* Time of last msgrcv(2) */ time_t msg_ctime; /* Time of last change */ unsigned long __msg_cbytes; /* Current number of bytes in queue (nonstandard) */ msgqnum_t msg_qnum; /* Current number of messages in queue */ msglen_t msg_qbytes; /* Maximum number of bytes allowed in queue */ pid_t msg_lspid; /* PID of last msgsnd(2) */ pid_t msg_lrpid; /* PID of last msgrcv(2) */ };
其中ipc_perm 結構體定義如下:
struct ipc_perm { key_t __key; /* Key supplied to msgget(2) */ uid_t uid; /* Effective UID of owner */ gid_t gid; /* Effective GID of owner */ uid_t cuid; /* Effective UID of creator */ gid_t cgid; /* Effective GID of creator */ unsigned short mode; /* Permissions */ unsigned short __seq; /* Sequence number */ };
當一個訊息佇列初始化完後,該訊息佇列對應的結構被作如下初始化:msg_perm.cuid 和 msg_perm.uid被設定為當前進程的使用者ID,msg_perm.cuid 和 msg_perm.uid被設定為當前進程的組ID;msg_perm.mode被設定為msgflg;msg_qnum, msg_lspid, msg_lrpid, msg_stime and msg_rtime 被設定為 0;msg_ctime被設定為目前時間;msg_qbytes被設定為系統限制值MSGMNB。
#include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
參數msgqid為訊息佇列標識,為msgget()返回值;參數msgp為指向使用者定義結構體的指標,結構體定義格式模板如下:
struct msgbuf { long mtype; /* message type, must be > 0 */ char mtext[1]; /* message data */ };
mtex域為數組,該數組大小為函數參數msgsz,mtype的值必須大於0;該結構由使用者自己定義,一般根據實際應用來定義。參數msgsz為將要發送訊息的長度,可以指定為0,一般指定為msgbuf結構體長度減去long類型長度(sizeof(msgbuf) - sizeof(long));參數msgflg為0,或IPC_NOWAIT,IPC_NOWAIT為非阻塞,發送時如果對列資料已滿,則函數馬上返回錯誤EAGAIN,如果msgflg為0,則阻塞直到訊息佇列由足夠空間添加新的訊息,或訊息佇列從系統中刪除,或被訊號中斷。
#include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
參數msgid同函數msgsnd;參數msgp通函數函數msgsnd,為指向接收訊息結構體指標;參數msgsz為接受訊息的緩衝大小;參數msgtyp為讀取何種訊息,如果為: 0 - 讀取訊息佇列中最早的訊息,以先進先出的方式讀取訊息。 > 0 - 讀取訊息佇列類型為msgtyp最早的訊息。 < 0 - 讀取訊息佇列中,訊息類型小於或等於msgtyp絕對值的訊息中最小訊息類型的最早訊息。參數msgflg為如下的值或者幾個值得或: IPC_NOWAIT - 如果隊列中沒有則立即返回;如果未指定該選項,函數將一直阻塞,直到指定的訊息可讀取,或訊息佇列從系統中刪除,或被訊號中斷; MSG_NOERROR - 截斷訊息如果接收到的訊息大小大於msgsz給定的值; MSG_EXCEPT - 使用大於0的msgtyp去讀取訊息類型不為msgtyp第一個訊息;
msgctl()函數對一個隊列做控制操作。 #include <sys/msg.h> int msgctl(int msqid, int cmd, struct msqid_ds *buf);參數cmd為對隊列要做的操作,這些操作有:
IPC_STAT - 擷取訊息佇列資訊,從核心資料結構中拷貝訊息佇列為msqid的結構體到buf指標指向的msqid_ds 結構體中。 IPC_SET - 使用buf指向的結構體來設定msgqid對應的訊息佇列核心結構體; IPC_RMID - 刪除msgqid訊息佇列; IPC_INFO,MSG_INFO, MSG_STAT詳見linux man函數手冊。樣本:服務進程:
#include <stdio.h>#include <string.h>#include <sys/types.h>#include <sys/ipc.h>#include <errno.h>#include <unistd.h>#include <signal.h>#include <stdlib.h>#include "slnmq.h"static int recv_type, msgid;int sln_server_loop(void){ int rcvmsg_len; key_t key; struct sln_msgbuf slnmsg; key = ftok(SLN_IPC_MQ_NAME, 0); if (key < 0) { fprintf(stderr, "ftok: %s\n", strerror(errno)); return -1; } msgid = msgget(key, IPC_CREAT | IPC_EXCL); if ((msgid < 0) && (errno != EEXIST)) { fprintf(stderr, "msgget: %s\n", strerror(errno)); return -1; } if (msgid < 0) { printf("Open existing mq!\n"); msgid = msgget(key, IPC_CREAT); if (msgid < 0) { fprintf(stderr, "msgget: %s\n", strerror(errno)); return -1; } } sleep(10); //just for test for (;;) { rcvmsg_len = msgrcv(msgid, &slnmsg, SLN_IPC_MQ_MSGSIZE, recv_type, 0); if (rcvmsg_len < 0) { fprintf(stderr, "msgrcv: %s\n", strerror(errno)); sleep(1); continue; } fprintf(stdout, "receive - recv len: %d, msg type: %d, msg: %s\n", rcvmsg_len, slnmsg.msgtype, slnmsg.msg); } return 0;}static void sigint_func(int sig){ if (msgctl(msgid, IPC_RMID, NULL) < 0) { fprintf(stderr, "msgctl: %s\n", strerror(errno)); } exit(0);}int main(int argc, const char *argv[]){ FILE *fp = NULL; if (argc != 2) { fprintf(stderr, "Usage: %s <msg type>\n", argv[0]);//運行時,指定服務要進程接收的訊息類型 return -1; } recv_type = atoi(argv[1]); if (access(SLN_IPC_MQ_NAME, F_OK) < 0) { fp = fopen(SLN_IPC_MQ_NAME, "w+"); if (NULL != fp) { fclose(fp); } } signal(SIGINT, sigint_func); //服務進程捕獲中斷訊號,在中斷處理函數裡面刪除訊息佇列並推出程式 sln_server_loop(); return 0;}
客戶進程:
#include <stdio.h>#include <string.h>#include <sys/types.h>#include <sys/ipc.h>#include <errno.h>#include "slnmq.h"int sln_msgsnd(int msg_type, const void *sndmsg, int sndlen){ int msgid, rcvmsg_len; key_t key; struct sln_msgbuf slnmsg; key = ftok(SLN_IPC_MQ_NAME, 0); if (key < 0) { fprintf(stderr, "ftok: %s\n", strerror(errno)); return -1; } msgid = msgget(key, IPC_CREAT); if (msgid < 0) { fprintf(stderr, "msgget: %s\n", strerror(errno)); return -1; } slnmsg.msgtype = msg_type; memcpy(&slnmsg.msg, sndmsg, sndlen); msgsnd(msgid, (void *)&slnmsg, sndlen, 0);}int main(int argc, const char *argv[]){ int type; if (argc != 3) { fprintf(stderr, "Usage: %s <msg type> <msg content>\n", argv[0]); return -1; } type = atoi(argv[1]); sln_msgsnd(type, argv[2], strlen(argv[2]) + 1); return 0;}
服務進程首先運行,並在開始讀取訊息處等待10秒,客戶進程在服務進程還未開始讀取資料時向訊息佇列加入多條訊息,如下:
./client 1 abcd./client 2 efghi./client 3 jkl./client 4 mn./client 5 opqrst./client 6 uv./client 7 w./client 8 xyz
當服務進程運行時會從訊息佇列中取出服務進程要取出類型(運行時參數指定)的訊息,下面分別是伺服器運行時指定的訊息類型以及取出的訊息(用戶端的運行都如上):
# ./server 0(讀取所有訊息)receive - recv len: 5, msg type: 1, msg: abcdreceive - recv len: 6, msg type: 2, msg: efghireceive - recv len: 4, msg type: 3, msg: jklreceive - recv len: 3, msg type: 4, msg: mnreceive - recv len: 7, msg type: 5, msg: opqrstreceive - recv len: 3, msg type: 6, msg: uvreceive - recv len: 2, msg type: 7, msg: wreceive - recv len: 4, msg type: 8, msg: xyz
# ./server 4 (讀取訊息類型為4的訊息)Open existing mq!receive - recv len: 3, msg type: 4, msg: mn
# ./server -5 (讀取訊息小於5的訊息) Open existing mq!receive - recv len: 5, msg type: 1, msg: abcdreceive - recv len: 6, msg type: 2, msg: efghireceive - recv len: 4, msg type: 3, msg: jklreceive - recv len: 3, msg type: 4, msg: mnreceive - recv len: 7, msg type: 5, msg: opqrst
在伺服器未退出時,可以使用系統命令ipcs查看系統中的訊息佇列:
# ipcs------ Message Queues --------key msqid owner perms used-bytes messages 0x001fffde 229376 root 0 0 0
當使用者按下CTRL+C時,程式會刪除掉該訊息佇列,按下CTRL+C後,再查看:
# ipcs------ Message Queues --------key msqid owner perms used-bytes messages
可以看到程式中的刪除是成功的。
posix訊息佇列和system V訊息佇列大致類似,不過通過這兩節可以看到有一些差別:posix訊息佇列有優先順序的概念,system V沒有,只能按照訊息類型的先進先出來讀取訊息即FIFO;system V訊息佇列有訊息類型的概念,並且可以按照訊息類型取出訊息,而posix不能區分訊息類型;本節源碼下載:點擊開啟連結
細說linux IPC(十):system V 訊息佇列