Linux进程间通信(IPC) - 消息队列

一、消息队列的特点

消息队列是另外一种Linux下进程间通信的方式,可以在相互独立的进程间进行通信。通过在内核维护一个消息队列链表来通信,允许在进程间以异步的方式进行数据交换。相比与之前提到的共享内存,消息队列能传输特定类型的消息,可以根据先进先出的方式获取消息体,也可以获取特定类型的消息体,使得通信更加的便捷。消息队列有以下几个特点: 

  1. 异步通信: 进程间传输数据不需要同步,可以随时传输
  2. 消息类型: 发送和接收都可以指定消息类型
  3. 先进先出: 如果不指定消息类型,则是按先进先出的方式来获取消息体

二、常见的API分析

1,msgget函数,用于创建或者获取一个消息队列标识符ID, 定义如下:

int msgget(key_t key, int msgflg);

key:键值,通过ftok获取,或者直接指定,key值唯一确定消息队列标识符ID

msgflag:消息队列的创建标志,同时还可以指定访问权限

        IPC_CREATE : 消息队列不存在则创建

        IPC_EXCL: 如果和IPC_CREATE一起使用,如果存在则返回错误,并返回已存在的错误码EEXIST

 使用示例如下:

msgId = msgget(key, IPC_CREAT|IPC_EXCL|0666);
if(msgId == -1){
   if(errno == EEXIST)  //已存在则去获取
   {
      msgId = msgget(key, 0666);
   }
}

2. msgsnd函数:发送消息,成功返回0,失败则返回-1,定义如下:

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

msqid:消息队列标识符

msqp:消息体地址,一般是一个以下的自定义结构体:

struct msgQueueData
{
    long mtype;         // 消息类型, 必须大于0
    char mtext[128];    // 消息体,消息体可以任何格式,后面还可以有其他属性
    ...
};

msgsz: 消息体大小(不包括消息类型)

msgflg:发送标志,

        0: 如果消息队列已满则阻塞,直到发送出去再返回。

        IPC_NOWAIT: 不阻塞,立即返回。

使用示例如下:

struct msgQueueData sendBuff;
sendBuff.mtype = 20;
strncpy(sendBuff.mtext, "hello", sizeof(sendBuff.mtext));
while (1)
{
   if(0 != msgsnd(msgId, &sendBuff, sizeof(sendBuff)-sizeof(long), 0)){
        perror("msg send failed");
        msgctl(msgId, IPC_RMID, NULL); //如果发送失败则删除消息队列标识符
        return -1;
   }
   sleep(2);
}

3. msgrecv函数: 接收消息,成功返回数据大小,失败则返回-1,定义如下

ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
                      int msgflg);

msqid:消息队列标识符

msgq:存放消息数据的指针,类型和发送一致。

msgsz:msgq中空间的大小

msgtyp:消息类型,如果是0则从接受任意消息,先进先出

msgflg:接受标志

        0: 没有数据则阻塞,直到接收到数据才返回

        IPC_NOWAIT: 没有数据立即返回

使用示例如下:

int recvlen = msgrcv(msgId, &recvBuff, sizeof(recvBuff)-sizeof(long), 20, 0);
if(recvlen > 0){
     printf("recv text:%s\n", recvBuff.mtext);
}else if(recvlen < 0){
     perror("msg recv failed");
     msgctl(msgId, IPC_RMID, NULL); //接收失败则删除消息队列标识符
     return -1;
}

4. msgctl函数: 控制消息队列,比如删除消息队列,获取消息队列信息等

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

msqid: 消息队列标识符

cmd:操作的命令

        IPC_RMID: 删除消息队列, 第三个参数可以传NULL

        IPC_INFO: 获取消息队列信息,信息结构体如下,可以获得消息队列最大的字节等信息:

struct msginfo {
    int msgpool; /* Size in kibibytes of buffer pool
                                      used to hold message data;
                                      unused within kernel */
    int msgmap;  /* Maximum number of entries in message
                                      map; unused within kernel */
    int msgmax;  /* Maximum number of bytes that can be
                                      written in a single message */
    int msgmnb;  /* Maximum number of bytes that can be
                                      written to queue; used to initialize
                                      msg_qbytes during queue creation
                                      (msgget(2)) */
    int msgmni;  /* Maximum number of message queues */
    int msgssz;  /* Message segment size;unused within kernel */
    int msgtql;  /* Maximum number of messages on all queues in system; unused within kernel */
    unsigned short msgseg;/* Maximum number of segments;unused within kernel */
};

删除的使用示例如下:

msgctl(msgId, IPC_RMID, NULL);

三、代码示例

下面是一个代码示例,运行的时候传"s"参数标识消息队列服务端,接收消息数据; 运行的时候传 "c"标识客户端,发送消息。先运行服务端没有任何消息,然后运行客户端,就会以2s的间隔接收到 "hello" 字符串, 说明两个进程间已经实现了同步。这里采用的都是阻塞的收发方式。

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>

struct msgQueueData
{
    long mtype;         // message type, must be > 0
    char mtext[128];    // message data
};

int main(int argc, char ** argv)
{
    int msgId;
    int key = ftok("/tmp", 0x1234);
    if(key == -1){
        perror("ftok failed");
        return -1;
    }
    msgId = msgget(key, IPC_CREAT|IPC_EXCL|0666);
    if(msgId == -1){
        if(errno == EEXIST)
        {
            msgId = msgget(key, 0666);
        }
    }
    if(msgId < 0){
        perror("msgId create or get failed");
        return -1;
    }
    printf("msgId:%d\n", msgId);
    if(strcmp(argv[1], "s") == 0){
        struct msgQueueData recvBuff;
        printf("This is msg queue server\n");
        while(1){
            int recvlen = msgrcv(msgId, &recvBuff, sizeof(recvBuff)-sizeof(long), 20, 0);
            if(recvlen > 0){
                printf("recv text:%s\n", recvBuff.mtext);
            }else if(recvlen < 0){
                perror("msg recv failed");
                msgctl(msgId, IPC_RMID, NULL);
                return -1;
            }
        }
    }else if(strcmp(argv[1], "c") == 0){
        printf("This is msg queue client\n");
        struct msgQueueData sendBuff;
        sendBuff.mtype = 20;
        strncpy(sendBuff.mtext, "hello", sizeof(sendBuff.mtext));
        while (1)
        {
            if(0 != msgsnd(msgId, &sendBuff, sizeof(sendBuff)-sizeof(long), 0)){
                perror("msg send failed");
                msgctl(msgId, IPC_RMID, NULL);
                return -1;
            }
            sleep(2);
        }
        
    }else{
        printf("args error\n");
        msgctl(msgId, IPC_RMID, NULL);
        return -1;
    }

    msgctl(msgId, IPC_RMID, NULL);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

深耕嵌入式

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值