进程间通信——消息队列
Posted y4247464
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了进程间通信——消息队列相关的知识,希望对你有一定的参考价值。
消息队列
消息队列即消息的链表,存放于内核并由消息队列标识符表示。是Linux进程通信机制中常见的一种通信方式,常用来在不同进程间发送特定格式的信息数据。linux支持POSIX消息队列与System V消息队列,本文阐述后者,详可见:https://www.cnblogs.com/linuxbug/p/4872496.html
消息队列与管道通信有较大的区别,主要有以下不同:
- 进程向消息队列写入消息前,并不需要某个进程在该队列上等待该消息的到达,而管道和FIFO是相反的,进程向其中写消息时,管道和FIFO必须已经打开来读,否则写进程就会阻塞(默认情况下)。
- IPC的持续性不同。管道和FIFO是随进程的持续性,当管道和FIFO最后一次关闭发生时,仍在管道和FIFO中的数据会被丢弃。消息队列是随内核的持续性,即一个进程向消息队列写入消息后,然后终止,另外一个进程可以在以后某个时刻打开该队列读取消息。只要内核没有重新自举,消息队列没有被删除。
消息队列中的每条消息通常具有以下属性:
- 一个表示优先级的整数;
- 消息的数据部分的长度;
- 消息数据本身;
消息队列的结构
在内核中的表示:
数据结构
控制结构
1 struct msqid_ds { 2 3 struct ipc_perm msg_perm; /* Ownership and permissions */ 4 5 time_t msg_stime; /* Time of last msgsnd(2) */ 6 7 time_t msg_rtime; /* Time of last msgrcv(2) */ 8 9 time_t msg_ctime; /* Time of last change */ 10 11 unsigned long __msg_cbytes; /* Current number of bytes in 12 13 queue (non-standard) */ 14 15 msgqnum_t msg_qnum; /* Current number of messages 16 17 in queue */ 18 19 msglen_t msg_qbytes; /* Maximum number of bytes 20 21 allowed in queue */ 22 23 pid_t msg_lspid; /* PID of last msgsnd(2) */ 24 25 pid_t msg_lrpid; /* PID of last msgrcv(2) */ 26 27 };
发送接收数据
1 struct msgbuf { 2 3 long mtype; /* message type, must be > 0 */ 4 5 char mtext[1]; /* message data */ 6 7 };
消息队列使用步骤
1 打开/创建消息队列——msgget 2 向消息队列发送消息——msgsnd 3 从消息队列接收消息——msgrcv 4 控制消息队列——msgctl
1、打开/创建消息队列——msgget
1 #include <sys/types.h> 2 #include <sys/ipc.h> 3 #include <sys/msg.h> 4 int msgget(key_t key, int msgflag); 5 6 //成功返回消息队列id,失败返回EOF 7 //参数: 8 // key---和消息队列关联的key,IPC_PRIVATE或ftok 9 //msgflag---标志位:IPC|CREAT|0666
2、向消息队列发送消息——msgsnd
1 #include <sys/types.h> 2 #include <sys/ipc.h> 3 #include <sys/msg.h> 4 int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg); 5 6 //成功0,失败-1 7 //参数: 8 //msgid---消息队列id 9 //msgp---消息缓冲区地址(指向消息结构的指针) 10 //msgze---消息正文长度,不包括消息类型的长度 11 //msgflg---标志位0或 IPC_NOWAIT 12 13 //消息结构msgbuf 14 struct msgbuf 15 { 16 long mtye; //消息类型,该结构必须从此域开始 17 char mtext[1]; //消息正文 18 };
消息格式:
1 · 通信双方首先定义好统一的信息格式 2 · 用户根据应用需求定义结构体类型 3 · 首成员类型必须为long,代表信息类型(正整数) 4 · 其他成员都属于消息正文 5 · 消息长度不包括首类型long
消息发送示例:
1 ... 2 typedef struct { 3 long mtype; 4 char mtext[64]; 5 }MSG; 6 7 #define LEN (sizeof(MSG) - sizeof(long)) 8 int main() 9 { 10 MSG buf; 11 ... 12 buf.mtype = 100; 13 fgets(buf.mtext, 64, stdin); 14 msgsnd(msgid, &buf, LEN, 0); 15 ... 16 return 0; 17 }
3、消息接收——msgrcv
从一个消息队列接受消息
1 #include <sys/ipc.h> 2 #include <sys/msg.h> 3 int msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg); 4 5 //成功返回接收到的消息长度,失败返回-1 6 //参数: 7 //msgid---消息队列id 8 //msgp---消息缓冲区地址 9 //size---指定接收的消息长度 10 //msgtype---指定接收消息类型
0--所以类型消息,正数n--接收类型n的消息,(-n)--接收前n种类型的消息
...,3,MSG_EXCEPE); 接收除类型3的消息
11 //msgflg---标志位 0或IPC_NOWAIT
4、控制消息队列—msgctl
1 #include <sys/types.h> 2 #include <sys/ipc.h> 3 #include <sys/msg.h> 4 int msgctl(int msqid, int cmd, struct msqid_ds *buf); 5 6 //成功返回0,失败-1 7 //参数: 8 //msgid---消息队列id 9 //cmd---要执行的操作: 10 // IPC_STAT / IPC_SET / IPC_REID(删除消息队列)
测试程序:
1 #include <stdio.h> 2 #include <sys/ipc.h> 3 #include <string.h> 4 #include <sys/msg.h> 5 6 //创建消息队列结构体 7 typedef struct{ 8 long type; 9 char txt[64]; 10 }MSG; 11 12 #define LEN sizeof(MSG)-sizeof(long) 13 14 int main(){ 15 16 key_t ipkey; 17 int msgid; 18 19 MSG msg_t; 20 21 //生成一个键key来命名某个特定的消息队列 22 ipkey = ftok(".",23); 23 if(ipkey == -1) 24 { 25 perror("ftok"); 26 return -1; 27 } 28 29 //创建与访问一个消息队列 30 msgid = msgget(ipkey,IPC_CREAT|0666); 31 if(msgid==-1) 32 { 33 perror("msgget"); 34 return -1; 35 } 36 37 //把消息传输到消息队列中 38 msg_t.type = 1; //类型1 39 strcpy(msg_t.txt,"msg type one "); 40 msgsnd(msgid, &msg_t, LEN, 0); 41 42 msg_t.type = 2; //类型2 43 strcpy(msg_t.txt,"msg type two "); 44 msgsnd(msgid, &msg_t, LEN, 0); 45 46 msg_t.type = 3; //类型3 47 strcpy(msg_t.txt,"msg type three"); 48 msgsnd(msgid, &msg_t, LEN, 0); 49 50 }
1 #include <stdio.h> 2 #include <sys/ipc.h> 3 #include <string.h> 4 #include <sys/msg.h> 5 6 //创建消息队列结构体 7 typedef struct{ 8 long type; 9 char txt[64]; 10 }MSG; 11 12 #define LEN sizeof(MSG)-sizeof(long) 13 14 int main(){ 15 16 key_t ipkey; 17 int msgid; 18 int re; 19 MSG msg_t; 20 21 //生成一个键key来命名某个特定的消息队列 22 ipkey = ftok(".",23); 23 if(ipkey == -1) 24 { 25 perror("ftok"); 26 return -1; 27 } 28 29 //创建与访问一个消息队列 30 msgid = msgget(ipkey,IPC_CREAT|0666); 31 if(msgid==-1) 32 { 33 perror("msgget"); 34 return -1; 35 } 36 37 while(1) 38 { 39 // re = msgrcv(msgid,&msg_t,LEN,0,0); //倒二参数为0,表示接收所有类型 40 re = msgrcv(msgid,&msg_t,LEN,2,0); //只接收2类型 41 printf("receive msg:type=%d,txt=%s ",(int)msg_t.type,msg_t.txt); 42 if(re <= 0) 43 { 44 break; 45 } 46 } 47 }
结果:
接收所以类型消息
只接收类型2的消息
以上是关于进程间通信——消息队列的主要内容,如果未能解决你的问题,请参考以下文章