Linux进程间的通信之消息队列

 

目录

 

消息队列

MQ的基本步骤

基本API

编程示例


​​​​​​​

  • 消息队列

消息队列(Message Queue,简称MQ)提供了一个从一个进程向另外一个进程发送一块数据的方法,每个数据块都被认为 是有一个类型,接收者进程接收的数据块可以有不同的类型值。消息队列也有管道一样的不足,就是每个消息的最大长度是有上 限的(MSGMAX),每个消息队列的总的字节数是有上限的(MSGMNB),系统上消息队列的总数也有一个上限 (MSGMNI)。


    MQ 传递的是消息,消息即是我们需要在进程间传递的数据。MQ 采用链表来实现消息队列,该链表是由系统内核维护,系统中可能有很多的 MQ,每个 MQ 用消息队列描述符(消息队列 ID:qid)来区分,qid 是唯一的,用来区分不同的 MQ。在进行 进程间通时一个进程将消息加到 MQ 尾端,另一个进程从消息队列中取消息(不一定以先进先出来取消息,也可以按照消息 类型字段取消息),这样就实现了进程间的通信。如下 MQ 的模型, 进程 1 向内核维护的消息队列中发消息,进程 1 从消息队 列中取消息,从而实现了 A1和 2的进程间通信。

  • MQ的基本步骤

  1.  创建和访问 MQ
  2.  发送消息
  3. 接受消息
  4. 删除 MQ
  • 基本API

  1.  ftok()
    #include <sys/types.h>
    #include <sys/ipc.h>
     
    key_t ftok(const char *pathname, int proj_id);

    该函数根据pathname指定的文件或目录的索引节点号和proj_id计算并返回一个key_t类型的ID值,如果失败则返回-1;

    第一个参数pathname是一个系统中必须存在的文件或文件夹的路径,会使用该文件的索引节点;

    第二个参数proj_id是用户指定的一个子序号,这个数字有的称之为project ID。它是一个8bit的整数,取值范围是1~255。 

    注意:如果要确保key值不变,要么确保ftok()的文件不被删除,要么不用ftok()指定一个固定的key值。

  2. msgget() 

    
           #include <sys/types.h>
           #include <sys/ipc.h>
           #include <sys/msg.h>
    
           int msgget(key_t key, int msgflg);

    说明:该函数用来创建消息队列ID,成功返回一个非负整数,即该共享内存段的标识码;失败返回-1。

第一个参数key是ftok()返回的key_t类型键值;

第二个参数msgflg是创建标志:IPC_CREAT,不存在则创建,存在则返回已有的mqid,IPC_CREAT|IPC_EXCL,不存在则创建,存在则返 回出错。

3.msgsnd()


       #include <sys/types.h>
       #include <sys/ipc.h>
       #include <sys/msg.h>

       int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

  

说明:用来来发送一个消息,必须要有写消息队列的权限。成功返回 0, 失败返回 -1, 并设置 erron。

第一个参数msgid是由msgget函数返回的消息队列 ID;

第二个参数msgp是一个指针,它指向要发送的消息结构体类型的变量。消息结构在两方面受到制约。首先,它必须小于系统规定的上 限值;         其次,它必须以一个long int长整数开始,接收者函数将利用这个长整数确定消息的类型,其参考类型定义形式如下:  

  typedef struct st_msgbuf{        
    long  mtype;        
    char  mtext[256]; 
   
} t_msgbuf;  

第三个参数msgsz是要发送消息的长度;

第四个参数msgflg 控制着当前消息队列满或到达系统上限时将要发生的事情,设置为IPC_NOWAIT表示队列满不等待,返回EAGAIN 错误。
4.msgrcv()

       #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);

 说明:用来从一个消息队列接收消息。成功返回实际放到接收缓冲区里去的字符个数,失败返回-1, 并设置 erron。

 第一个参数msgid是由msgget函数返回的消息队列 ID;  

第二个参数msgp是一个指针,它指向准备接收的消息;  

第三个参数msgsz是msgp指向的消息长度,这个长度不含保存消息类型的那个long int长整型;  

第四个参数msgtype是消息的类型,它可以实现接收优先级的简单形式;        

msgtype=0返回队列第一条信息         msgtype>0返回队列第一条类型等于msgtype的消息          

msgtype<0返回队列第一条类型小于等于msgtype绝对值的消息,并且是满足条件的消息类型最小的消息;

第五个参数msgflg 控制着队列中没有相应类型的消息可供接收时将要发生的事;

msgflg=IPC_NOWAIT,队列没有可读消息不等待,返回ENOMSG错误。        

msgflg=MSG_NOERROR,消息大小超过msgsz时被截断        

msgtype>0且msgflg=MSG_EXCEPT,接收类型不等于msgtype的第一条消息。
5.msgctl()

       #include <sys/types.h>
       #include <sys/ipc.h>
       #include <sys/msg.h>

       int msgctl(int msqid, int cmd, struct msqid_ds *buf);


  说明:该函数用于控制消息队列  

第一个参数msgid是由msgget函数返回的消息队列 ID;  

第二个参数cmd是要采取的操作,它可以取下面的三个值:        

IPC_STAT:把msqid_ds结构中的数据设置为消息队列的当前关联值;        

IPC_SET: 如果进程有足够的权限,就把消息队列的当前关联值设置为msqid_ds结构中给出的值;        

IPC_RMID:删除消息队列;  

第三个参数buf是一个结构指针,它指向存储消息队列的相关信息的 buf;

 

  • 编程示例

  1. msgqueue_send 代码
    
    #include <stdio.h>
    #include <string.h> 
    #include <errno.h>
    #include <unistd.h> 
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/msg.h>
    #define FTOK_PATH            "/dev/zero" 
    #define FTOK_PROJID          0x22
    typedef struct s_msgbuf {   
        long  mtype;  
        char  mtext[512];
    } t_msgbuf;
    int main(int argc, char **argv) 
    { 
        key_t             key;   
        int               msgid; 
        t_msgbuf          msgbuf;   
        int               msgtype;
        int               i;  
    
        if( (key=ftok(FTOK_PATH, FTOK_PROJID)) < 0 )   
        {
            printf("ftok() get IPC token failure: %s\n", strerror(errno));     
            return -1; 
        }   
    
        msgid = msgget(key, IPC_CREAT|0666);
        if( msgid < 0)
        {
            printf("shmget() create shared memroy failure: %s\n", strerror(errno));
            return -2; 
        }   
    
        msgtype = (int)key; 
        printf("key[%d] msgid[%d] msgypte[%d]\n", (int)key, msgid, msgtype);
    
        for(i=0; i<4; i++)   
        {
            msgbuf.mtype = msgtype;     
            strcpy(msgbuf.mtext,"Hello Word");
    
            if( msgsnd(msgid, &msgbuf, sizeof(msgbuf.mtext), IPC_NOWAIT) < 0)     
            {
                printf("msgsnd() send message failure: %s\n", strerror(errno));
                break;
            }   
    
            printf("Send message: %s\n", msgbuf.mtext);
    
            sleep(1);   
        }   
    
        msgctl(msgid, IPC_RMID, NULL);
    
        return 0; 
    }
    
    
  2. msgqueue_send 代码
#include <stdio.h>  
#include <string.h>
#include <errno.h>
#include <unistd.h> 
#include <stdlib.h>
#include <sys/types.h> 
#include <sys/ipc.h>
#include <sys/msg.h>
#define FTOK_PATH            "/dev/zero"
#define FTOK_PROJID          0x22
typedef struct s_msgbuf { 
    long  mtype;  
    char  mtext[512];
} t_msgbuf;
int main(int argc, char **argv) 
{
    key_t             key;  
    int               msgid; 
    t_msgbuf          msgbuf;
    int               msgtype;
    int               i;  

    if( (key=ftok(FTOK_PATH, FTOK_PROJID)) < 0 )    
    {
        printf("ftok() get IPC token failure: %s\n", strerror(errno));    
        return -1;
    }   

    msgid = msgget(key, IPC_CREAT|0666);    
    if( msgid < 0)  
    {
        printf("shmget() create shared memroy failure: %s\n", strerror(errno));    
        return -2;  
    }   

    msgtype = msgtype = (int)key;   
    printf("key[%d] msgid[%d] msgypte[%d]\n", (int)key, msgid, msgtype);

    for(i=0; i<4; i++)
    {
        memset(&msgbuf,0,sizeof(msgbuf));     
        if( msgrcv(msgid, &msgbuf, sizeof(msgbuf.mtext), msgtype, IPC_NOWAIT) < 0 )    
        {
            printf("msgsnd() receive message failure: %s\n", strerror(errno)); 
            break;    
        }   

        printf("Receive Message: %s\n", msgbuf.mtext);    
        sleep(1); 
    }   

    msgctl(msgid, IPC_RMID, NULL);

    return 0; 
}

分析:进程msgqueue_send ;往内核的消息队列里写入内容"Hello Word",而 进程msgqueue_recv 则从消息队列里读出并打印该消息。

 

发布了47 篇原创文章 · 获赞 37 · 访问量 3684

猜你喜欢

转载自blog.csdn.net/qq_44045338/article/details/105013336
今日推荐