Linux进程间通信二 System V 消息队列简介与示例

1. SystemV消息队列简介

消息队列,顾名思义即是存放消息的队列,内核为每个SystemV 维护了一个msg_queue的结构体,里面记录了每个消息队列的信息。

struct msg_queue {
    structkern_ipc_perm q_perm;   // 权限相关
    time_tq_stime;         /* 上一次 msgsnd时间 */
    time_tq_rtime;         /* 上一次 msgrcv时间 */
    time_tq_ctime;         /* 属性变化时间 */
    unsignedlong q_cbytes;     /* 队列当前字节总数 */
    unsignedlong q_qnum;       /* 队列当前消息总数 */
    unsignedlong q_qbytes;     /* 一个消息队列允许最大字节数 */
    pid_tq_lspid;          /* 上一个调用msgsnd的进程ID */
    pid_tq_lrpid;          /* 上一个调用msgrcv的进程ID */
    structlist_head q_messages;
    structlist_head q_receivers;
    structlist_head q_senders;
};                                                                                                                                                         

消息队列存储消息的结构体需要用户自定义,且第一个字段必须是long类型。虽然诞生已久,但是systemV支持优先级队列和FIFO这一点确实是挺强大的,缺点也很明显,不支持IO多路复用。一般大型的MQ开发很少用到SystemV mq,因为存在很多限制,比如系统能够创建的消息队列个数有限,单条消息大小受限,单个消息队列里面消息个数受限等等。即便如此还是需要学习一下systemV消息队列,以后自己做相关的开发可以借鉴相关的设计思路。

 

2. API接口

2. 1 创建或获取消息队列ID

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

/**
* @brief 创建或获取消息队列ID
*
* @params key 标识符,整形变量,三种方式,固定值,IPC_PRIVATE,ftok生成
* @params msgflg,标志位,IPC_CREAT and IPC_EXCL
*
* @returns 成功返回消息队列ID,失败返回-1
*/

int msgget(key_t key, int msgflg);

2.2 写消息

/**
* @brief 发送消息
*
* @params msqid 消息队列ID
* @params msgp 待发送消息的地址,类型需要自定义且第一个元素为long类型
* @params msgsz 消息大小
* @params msgflg 标志位,IPC_NOWAIT,未指定情况下,如果消息队列空间满,默认阻塞,设置此标志位则立即返回。

* @returns 成功返回消息队列ID,失败返回-1
*/

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

2.3 读消息

/**
* @brief 发送消息
*
* @params msqid 消息队列ID
* @params msgp 接收消息的缓存,需要和发送方约定好结构体
* @params msgsz 接收消息最大长度,不包括消息类型字段
* @params msgtyp 要接收的消息类型。
>0分为两种情况,如果设置了MSG_EXCEPT,从消息队列中取出第一条消息类型相同的消息。
否则,取出第一条消息类型不同的消息。
=0相当于先入先出,最早进入消息队列的消息被取出。
<0相当于优先级队列,取出值小于或等于msgtyp绝对值的第一条消息

* @params msgflg 标志位,IPC_NOWAIT,MSG_EXCEPT,MSG_NOERROR(消息过长时是否截断)

* @returns 成功返回接收到的消息内容长度,失败返回-1
*/

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

2.4 消息队列其它控制操作

/**
* @brief 控制操作
*
* @params msqid 消息队列ID
* @params cmd 控制命令类型
IPC_STAT 获取或设置消息队列的属性
IPC_SET  设置权限相关属性
IPC_RMID 删除消息队列,释放消息队列中的所有消息

* @returns 成功返回0,失败返回-1
*/


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

3. 示例

下面是一个简单的消息队列例子,分为读消息和写消息两个部分。

3.1读取


// 读取消息队列

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

// 消息结构体,第一个元素必须是消息类型
struct msg{
    long mtype;         // 消息类型
    char data[1024];    // 数据
};

int main()
{
    int mq_id = 0;
    key_t key = 1357;       // 标识符,三种方式,固定值,IPC_PRIVATE,ftok生成
    ssize_t msg_size;
    struct msg message={0};

    // 创建或获取消息队列
    mq_id = msgget(key, IPC_CREAT);
    if (mq_id == -1)
    {
        printf("create mq error");
        return mq_id;
    }

    // 循环监听消息队列
    while (1)
    {
        // 阻塞监听所有消息类型的消息
        msg_size = msgrcv(mq_id, (void *)&message, sizeof(message), 0, 0);    
        if (msg_size != -1)
        {
            printf("read msg type:%ld, data:%s\n", message.mtype, message.data);
        }

        memset(&message,0,sizeof(message));
    }

    return 0;
}

3.2 写入

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

// 消息结构体,第一个元素必须是消息类型
struct msg{
    long mtype;         // 消息类型
    char data[1024];    // 数据
};

int main(int argc, char** argv)
{
    if (argc < 3)
    {
        printf("invalid param, please input msg type and data\n");
        printf("Usage: ./mq_write type data\n");
        return -1;
    }

    int mq_id = 0;
    key_t key = 1357;       // 标识符,三种方式,固定值,IPC_PRIVATE,ftok生成

    // 创建或获取消息队列
    mq_id = msgget(key, IPC_CREAT);
    if (mq_id == -1)
    {
        printf("create mq error\n");
        return mq_id;
    }

    struct msg message;
    message.mtype = strtol(argv[1],NULL,0);
    strcpy(message.data, argv[2]);
    
    // 发送消息
    if (msgsnd(mq_id, (const void*)&message, sizeof(message), 0) == 0)
    {
        printf("send message succ\n");
    }
    else
    {
        printf("send message error\n");
    }
    return 0;
}

3.3 编译&运行

gcc -o mq_read mq_read.c
gcc -o mq_wirte mq_write.c

 

4. 注意事项

4.1  消息队列限制

MSGMAX 单条消息大小有限
cat /proc/sys/kernel/msgmax

MSGMNI 消息队列总个数有限
cat /proc/sys/kernel/msgmni

MSGMNB 单个消息队列最大大小
cat /proc/sys/kernel/msgmnb

5. 参考资料

1. https://man7.org/linux/man-pages/man2/msgsnd.2.html

2. 《Linux环境编程 从应用到内核》

3. 《Unix网络编程 卷二 进程间通信》

================================================================================================

Linux应用程序、内核、驱动、后台开发交流讨论群(745510310),感兴趣的同学可以加群讨论、交流、资料查找等,前进的道路上,你不是一个人奥^_^。...

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章