Linux应用开发(7):Linux消息队列:POSIX消息队列

1. 简述

消息队列是Linux提供进程间通信的一种方式。当多个进程需要共享或交换数据时,就会用到消息队列。此外,消息队列还可以作为一个缓冲区,可以在处理速度不同的进程间提供数据交换。

2. POSIX消息队列和System V消息队列

与信号量一样,消息队列也有两个不同的版本,分别是POSIX和System V消息队列。

本文着重讲解POSIX消息队列。

3. POSIX消息队列的特点

****(1)标准化:****POSIX消息队列遵循POSIX标准,这意味着它们在支持POSIX的系统上具有一致的接口和行为,从而提高了代码的可移植性。

****(2)消息优先级:****POSIX消息队列支持为每个消息设置优先级。消息队列中的所有消息都会根据它们的优先级进行排序,优先级高的消息会先被消费。

(3)异步通知:POSIX消息队列允许应用程序注册异步通知。当队列中存在消息时,可以通过信号或线程的方式通知应用程序,这样应用程序就不需要不断轮询队列。

****(4)引用计数:****POSIX消息队列使用引用计数机制来管理其生命周期。队列的创建和删除是通过引用计数来控制的,只有当所有引用队列的进程都关闭队列后,队列才会被真正删除。

(5)非阻塞操作:POSIX消息队列支持非阻塞操作。通过设置非阻塞标志,消息队列可以在消息不可用时立即返回,而不是阻塞等待。

****(6)消息格式化:****消息队列中的消息通常包含格式化的数据,这使得它们可以携带复杂的数据结构和信息。

****(7)安全性:****POSIX消息队列提供了权限检查机制,确保只有具有适当权限的进程可以发送或接收消息。

****(8)独立于消息大小:****POSIX消息队列允许发送和接收固定大小的消息,这有助于减少内存碎片和提高性能。

****(9)多线程安全:****POSIX消息队列的操作是线程安全的,可以在多线程环境中使用而不需要额外的同步机制。

4. 提供的API

(1)获取或创建消息队列

#include <mqueue.h>

mqd_t mq_open(const char *name, int oflag, /* mode_t mode, struct mq_attr *attr */);

name:消息队列的名称。

oflag:打开或创建队列时的选项标志,可以是以下一个或多个选项的按位或:

O_RDONLY:以只读方式打开消息队列。

O_WRONLY:以只写方式打开消息队列。

O_CREAT:如果指定的消息队列不存在,则创建它。

O_EXCL:与O_CREAT一起使用,如果消息队列已存在,则返回错误。

如果使用了O_CREAT标志,还需要提供两个额外的参数:mode_t mode和struct mq_attr *attr。mode指定新队列的权限,attr指定队列的属性;否则,这两个参数可以省略。

返回值:

成功时返回一个消息队列描述符(mqd_t类型),失败时返回-1并设置errno。

(2)关闭消息队列

int mq_close(mqd_t mqdes);

(3)清除消息队列

int mq_unlink(const char *name);

(4)发送消息

mqd_t mq_send(mqd_t mqdes, const char *msg_ptr,

size_t msg_len, unsigned msg_prio);

//成功返回0,出错返回-1

mqd_t mq_timedsend(mqd_t mqdes, const char *msg_ptr,

size_t msg_len, unsigned msg_prio,

const struct timespec *abs_timeout);

mqdes:消息队列描述符;

msg_ptr:指向消息体缓冲区的指针;

msg_len:消息体的长度,其中mq_receive的该参数不能小于能写入队列中消息的最大大小,即一定要大于等于该队列的mq_attr结构中mq_msgsize的大小。如果mq_receive中的msg_len小于该值,就会返回EMSGSIZE错误。POXIS消息队列发送的消息长度可以为0。

msg_prio:消息的优先级;它是一个小于MQ_PRIO_MAX的数,数值越大,优先级越高。POSIX消息队列在调用mq_receive时总是返回队列中最高优先级的最早消息。如果消息不需要设定优先级,那么可以在mq_send是置msg_prio为0,mq_receive的msg_prio置为NULL。

(5)接收消息

mqd_t mq_receive(mqd_t mqdes, char *msg_ptr,

size_t msg_len, unsigned *msg_prio);

//成功返回接收到消息的字节数,出错返回-1

mqd_t mq_timedreceive(mqd_t mqdes, char *msg_ptr,

size_t msg_len, unsigned *msg_prio,

const struct timespec *abs_timeout);

参数与mq_send基本一致。

(6)获取和设置队列属性

int mq_getattr(mqd_t mqdes, struct mq_attr *mqstat);

int mq_setattr(mqd_t mqdes, const struct mq_attr *mqstat, struct mq_attr *omqstat);

mqdes:消息队列描述符。

mqstat:指向mq_attr结构的指针,用于存储或设置消息队列的属性。

omqstat(仅mq_setattr()):如果非NULL,则在此结构中返回旧的队列属性。

返回值:

成功时返回0,失败时返回-1并设置errno。

5. 消息队列例程

#include <stdio.h>  

#include <stdlib.h>  

#include <string.h>  

#include <fcntl.h>  

#include <sys/stat.h>  

#include <mqueue.h>  

#include <unistd.h>  

 

#define QUEUE_NAME  "/msg_queue"  

#define MAX_SIZE    1024  

#define MAX_MSG     10  

#define MSG_STOP    "exit"  

 

int main(int argc, char* argv[])

{

    mqd_t mqdes;  

    char buf[MAX_SIZE];  

    unsigned int prio;  

    struct mq_attr attr;  

    ssize_t nbytes;  

 

    /** 设置消息队列属性. */

    attr.mq_flags = 0;  

    attr.mq_maxmsg = MAX_MSG;  

    attr.mq_msgsize = MAX_SIZE;  

    attr.mq_curmsgs = 0;  

 

    /** 创建消息队列. */

    mqdes = mq_open(QUEUE_NAME, O_CREAT | O_WRONLY, 0644, &attr);  

    if (mqdes == (mqd_t) -1) {  

        perror("mq_open");  

        exit(1);  

    }  

 

    /** 发送消息到队列. */

    while (1) {  

        printf("Enter a message to send to the queue (type 'exit' to stop): ");  

        fgets(buf, MAX_SIZE, stdin);  

        buf[strcspn(buf, "\n")] = 0; ///< 去掉换行符  

 

        if (strcmp(buf, MSG_STOP) == 0) {  

            break;  

        }  

 

        nbytes = mq_send(mqdes, buf, strlen(buf) + 1, 0);  

        if (nbytes == -1) {  

            perror("mq_send");  

            exit(1);  

        }  

    }  

 

    /** 关闭消息队列并重新以只读方式打开. */

    mq_close(mqdes);  

    mqdes = mq_open(QUEUE_NAME, O_RDONLY);  

    if (mqdes == (mqd_t) -1) {  

        perror("mq_open");  

        exit(1);  

    }  

 

    /** 从队列中接收消息并打印. */

    printf("Reading messages from the queue:\n");  

    while (1) {  

        nbytes = mq_receive(mqdes, buf, MAX_SIZE, NULL);  

        if (nbytes == -1) {  

            perror("mq_receive");  

            exit(1);  

        }  

 

        printf("%s\n", buf);  

 

        if (strcmp(buf, MSG_STOP) == 0) {  

            break;  

        }  

    }  

 

    /** 关闭并删除消息队列. */

    mq_close(mqdes);  

    mq_unlink(QUEUE_NAME);  

 

    return 0;  

}
相关推荐
moneyxjj34 分钟前
Linux各种解压命令汇总
linux·运维·服务器
白白♛~1 小时前
网络管理之---3种网络模式配置
linux·服务器·网络
GOTXX1 小时前
NAT、代理服务与内网穿透技术全解析
linux·网络·人工智能·计算机网络·智能路由器
脱了格子衬衫1 小时前
使用源码编译安装 Tomcat
linux·tomcat
陈yanyu1 小时前
Linux - 弯路系列3:安装和编译libvirt-4.5.0及虚拟网卡virbr0(virbr0-nic)创建
linux·运维·服务器
feng68_1 小时前
Linux编辑/etc/fstab文件不当,不使用快照;进入救援模式
linux
码农研究僧2 小时前
详细分析ip addr show 查看网络配置的命令
linux·tcp/ip·ip addr show
码狂☆2 小时前
ubuntu连接orangepi-zero-2w桌面的几种方法
linux·ubuntu
学习向前冲2 小时前
安装一键式重置密码插件(Linux)-CloudResetPwdAgent
linux·运维·服务器
石兴稳3 小时前
Ceph client 写入osd 数据的两种方式librbd 和kernel rbd
linux·ceph