进程间通信——消息队列

消息队列

  消息队列即消息的链表,存放于内核并由消息队列标识符表示。是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 };
struct msqid_ds

  发送接收数据

1 struct msgbuf {
2 
3    long mtype;       /* message type, must be > 0 */
4 
5    char mtext[1];    /* message data */
6 
7 };
msqbuf

  消息队列使用步骤

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 }
msg_snd.c
 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 }
msg_rcv.c

结果:

接收所以类型消息

 只接收类型2的消息

原文地址:https://www.cnblogs.com/y4247464/p/12112601.html