zoukankan      html  css  js  c++  java
  • 进程间通信——消息队列

    消息队列

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

  • 相关阅读:
    DAY7-面向对象之封装
    Java遇到的问题、错误——持续更新
    008单例、继承、final
    java一些使用
    2.1端口扫描器
    PyCharm设置
    常用算法
    PyCharm最新2018激活码,最新方法
    004数组
    042多进程
  • 原文地址:https://www.cnblogs.com/y4247464/p/12112601.html
Copyright © 2011-2022 走看看