zoukankan      html  css  js  c++  java
  • Linux 消息队列编程

      消息队列、信号量以及共享内存被称作 XSI IPC,它们均来自system V的IPC功能,因此具有许多共性。

    键和标识符:

      内核中的每一种IPC结构(比如信号量、消息队列、共享内存)都用一个非负整数的标识符加以标示(如共享内存的shmid、信号量的semid、以及消息队列的msgid)。不同于文件描述符,IPC标识符不是一个小的非负整数,它是一个int型的整数,当一个标识符被创建,以后又被删除时,这个整数持续加1,达到整型的最大值后,重新回到0。

      但是每一个IPC对象在内核中的标识符只能在内部被识别,为了让不同的进程能够在同一个IPC对象上汇合,还需要一个外部的标识来表示一个IPC 对象,这就是key 键值。或者可以这样理解:标识符是一个打开了的IPC对象的描述符,而键值则让进程获得这个标识符。

      当我们通过一个键值创建了一个IPC对象以后,就可以用这个IPC对象的标识符操作这个IPC对象。例如当进程用指定的键值获得一个共享内存的标识符shmid以后,就可以通过这个标识符将共享内存映射到自身的地址空间上,当然后续对共享内存的操作也是基于这个标识符。

    消息队列编程:

      消息队列作为XSI IPC的一种,许多实现和操作和前面描述的信号量和共享内存有很多相似的地方。先简述一下消息队列的原理:消息队列的本质就是一个消息的链表,而每个消息的结构如下

               struct msgbuf {    

                             long mtype;       /* message type, must be > 0 */       

                         char mtext[1];    /* message data */ 

                      };

    mtype:消息的类型        mtext[]: 消息中的数据

    也就是说消息队列就是一个数据域是以上结构体的链表。既然了解了一个消息队列的本质就是一个链表,可以想象,如果进程间要通过消息队列通信,那么发送消息的进程的主要工作就是要构建一个数据域,然后交给内核来插入到链表中,而接收消息的进程就是通过内核函数来将数据从消息队列中取出来。消息队列还有如下特点:

     *消息队列的消息类型可以不同,进程取出消息时可以指定消息类型取出需要的消息

     *当进程取出一条消息后,该消息会立即被移出消息队列

    利用消息队列通信主要由以下操作完成:

     *创建/打开消息队列   :int msgget(key_t key, int msgflg);

     *发送消息                 :int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg)

     *接收消息                 :ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg)

     *删除消息队列           :int msgctl(int msqid, int cmd, struct msqid_ds *buf)

    这些函数都可以通过man命令查到具体的用法,这里就不在详细解释,下面是一个测试的范例:

    send 程序:

    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/msg.h>
    #include <stdio.h>
    
    
    #define TEXT_SZ 2048
    struct msgt
    {
        long msgtype;
        char msgtext[TEXT_SZ];
    };
    
    int main()
    {
        int msgid;
        key_t key;
        int running = 1;
        struct msgt msg_data;
        int msgtype;
        key = ftok("/home/application/massage_queue",2);
        //创建消息队列
        msgid  = msgget(key, IPC_CREAT);
        //循环
        while(running)
        {
             printf("Please Input msgtype,Input 0 to quit!
    ");
             scanf("%d",&msgtype);
             printf("Please Input datas!
    ");
             
             //从终端读取数据
             scanf("%s",msg_data.msgtext);
             //将数据写入消息队列
             msg_data.msgtype = msgtype;
             msgsnd(msgid, &msg_data, sizeof(msg_data), 0);
             
             if(strncmp(msg_data.msgtext,"end",3)==0)
             {
                 msgsnd(msgid, "end", 3, 0);
                 running = 0;
             }    
        }
        //删除消息队列
        msgctl(msgid, IPC_RMID, 0);
        return 0;
            
    }
    View Code

    receive 程序:

    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/msg.h>
    #include <stdio.h>
    #include <unistd.h>
    
    #define TEXT_SZ 2048
    struct msgt
    {
        long msgtype;
        char msgtext[TEXT_SZ];
    };
    
    int msgid;
    void childprocess()
    {
        struct msgt msg_d;
        int running = 1;
        while(running)
        {
            //接受消息队列中的数据
            msgrcv(msgid, &msg_d, sizeof(msg_d), 0,0);
            //打印数据
            printf("Receive datas from queue:%s",msg_d.msgtext);
            if(strncmp(msg_d.msgtext,"end",3)==0)
            {
                running = 0;    
            }
        }
    
            
    }
    int main()
    {
    
        key_t key;
        pid_t pid;
        int i;
        key = ftok("/home/application/massage_queue",2);
        //打开消息队列
        msgid  = msgget(key, IPC_EXCL);    
        
       for(i=0;i<3;i++)
        {
            pid = fork();
            if(pid<0)
            {
                printf("fork error!
    ");    
            }
            else if(pid==0)
            {
                childprocess();
            }
        }
        
        return 0;    
    }
    View Code

    当两个程序运行起来以后可以发现通过send成序发送的数据在receive 中可以接收到,说明这些函数的调用很成功,同时也证明了消息队列的通信是成功的。

  • 相关阅读:
    在SQL中删除重复记录
    SQL语句生成SQLServer数据字典
    还原数据库时出现的问题
    Active Server Pages 错误 'ASP 0201'
    VS.NET新建项目时出现“两者需要映射到相同的服务器位置”
    cs0016: 未能写入输出文件“c:\windows\microsoft.net\***.dll”错误处理
    Svchost.exe进程详解
    5y5.us
    C# 中返回星期的函数
    怎样用C#实现完整文档打印功能
  • 原文地址:https://www.cnblogs.com/linzizhang/p/4553349.html
Copyright © 2011-2022 走看看