Linux-----进程通讯(消息队列)

news2025/1/14 18:58:34

目录

相关API

1.相关数据类型

mqd_t

struct mq_attr

 struct timespec

2.相关系统调用接口

mq_open()

mq_timedsend() && mq_send()

mq_timedreceive() && mq_receive()

mq_unlink()

clock_gettime()

父子进程使用消息队列通讯

平行进程使用消息队列通讯(生产者-消费者)

 生产者(producer.c)

消费者(customer.c)


相关API

1.相关数据类型

mqd_t

该数据类型定义在mqueue.h中,是用来记录消息队列描述符的。

typedef int mqd_t;

实质上是int类型的别名

struct mq_attr
/**
 * @brief 消息队列的属性信息
 * mq_flags 标记,对于mq_open,忽略它,因为这个标记是通过前者的调用传递的
 * mq_maxmgs 队列可以容纳的消息的最大数量
 * mq_msgsize 单条消息的最大允许大小,以字节为单位
 * mq_curmsgs 当前队列中的消息数量,对于mq_open,忽略它
 */
struct mq_attr {
long mq_flags;   /* Flags (ignored for mq_open()) */
long mq_maxmsg;  /* Max. # of messages on queue */
long mq_msgsize; /* Max. message size (bytes) */
long mq_curmsgs; /* # of messages currently in queue
                (ignored for mq_open()) */
};
 struct timespec
/**
 * @brief 时间结构体,提供了纳秒级的UNIX时间戳
 * tv_sec 秒
 * tv_nsec 纳秒
 */
struct timespec {
time_t tv_sec;        /* seconds */
long   tv_nsec;       /* nanoseconds */
};

2.相关系统调用接口

mq_open()
#include <fcntl.h>    /* For O_* constants */
#include <sys/stat.h> /* For mode constants */
#include <mqueue.h>

/**
 * @brief 创建或打开一个已存在的POSIX消息队列,消息队列是通过名称唯一标识的。
 *
 * @param name 消息队列的名称
 * 命名规则:必须是以正斜杠/开头,以\0结尾的字符串,中间可以包含若干字符,但不能有正斜杠
 * @param oflag 指定消息队列的控制权限,必须也只能包含以下三者之一
 * O_RDONLY 打开的消息队列只用于接收消息
 * O_WRONLY 打开的消息队列只用于发送消息
 * O_RDWR 打开的消息队列可以用于收发消息
 * 可以与以下选项中的0至多个或操作之后作为oflag
 * O_CLOEXEC 设置close-on-exec标记,这个标记表示执行exec时关闭文件描述符
 * O_CREAT 当文件描述符不存在时创建它,如果指定了这一标记,需要额外提供mode和attr参数
 * O_EXCL 创建一个当前进程独占的消息队列,要同时指定O_CREAT,要求创建的消息队列不存在,否则将会失败,并提示错误EEXIST
 * O_NONBLOCK 以非阻塞模式打开消息队列,如果设置了这个选项,在默认情况下收发消息发生阻塞时,会转而失败,并提示错误EAGAIN
 * @param mode 每个消息队列在mqueue文件系统对应一个文件,mode是用来指定消息队列对应文件的权限的
 * @param attr 属性信息,如果为NULL,则队列以默认属性创建

* @return mqd_t 成功则返回消息队列描述符,失败则返回(mqd_t)-1,同时设置errno以指明错误原因
*/
mqd_t mq_open(const char *name, int oflag, mode_t mode, struct mq_attr *attr);

/**
 * @brief 当oflag没有包含O_CREAT时方可调用
 *
 * @param name 同上
 * @param oflag 同上
 * @return mqd_t 同上
 */
mqd_t mq_open(const char *name, int oflag);
mq_timedsend() && mq_send()

 对于 mq_send()这个方式就是发送没有超时机制,可以一直处于阻塞状态。

#include <time.h>
#include <mqueue.h>

/**
 * @brief 将msg_ptr指向的消息追加到消息队列描述符mqdes指向的消息队列的尾部。如果消息队列已满,默认情况下,调用阻塞直至有充足的空间允许新的消息入队,或者达到abs_timeout指定的等待时间节点,或者调用被信号处理函数打断。需要注意的是,正如上文提到的,如果在mq_open时指定了O_NONBLOCK标记,则转而失败,并返回错误EAGAIN。
 * 
 * @param mqdes 消息队列描述符
 * @param msg_ptr 指向消息的指针
 * @param msg_len msg_ptr指向的消息长度,不能超过队列的mq_msgsize属性指定的队列最大容量,长度为0的消息是被允许的
 * @param msg_prio 一个非负整数,指定了消息的优先级,消息队列中的数据是按照优先级降序排列的,如果新旧消息的优先级相同,则新的消息排在后面。
 * @param abs_timeout 指向struct timespec类型的对象,指定了阻塞等待的最晚时间。如果消息队列已满,且abs_timeout指定的时间节点已过期,则调用立即返回。
 * @return int 成功返回0,失败返回-1,同时设置errno以指明错误原因。
 */
int mq_timedsend(mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned int msg_prio, const struct timespec *abs_timeout);
mq_timedreceive() && mq_receive()

 mq_receive()同样的也是没有超时机制,当没有数据来的时候就处于阻塞状态。

#include <time.h>
#include <mqueue.h>

/**
 * @brief 从消息队列中取走最早入队且权限最高的消息,将其放入msg_ptr指向的缓存中。如果消息队列为空,默认情况下调用阻塞,此时的行为与mq_timedsend同理。
 * 
 * @param mqdes 消息队列描述符
 * @param msg_ptr 接收消息的缓存
 * @param msg_len msg_ptr指向的缓存区的大小,必须大于等于mq_msgsize属性指定的队列单条消息最大字节数
 * @param msg_prio 如果不为NULL,则用于接收接收到的消息的优先级 
 * @param abs_timeout 阻塞时等待的最晚时间节点,同mq_timedsend
 * @return ssize_t 成功则返回接收到的消息的字节数,失败返回-1,并设置errno指明错误原因
 */
ssize_t mq_timedreceive(mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned int *msg_prio, const struct timespec *abs_timeout);
mq_unlink()
#include <mqueue.h>
/**
 * @brief 清除name对应的消息队列,mqueue文件系统中的对应文件被立即清除。消息队列本身的清除必须等待所有指向该消息队列的描述符全部关闭之后才会发生。
 * 
 * @param name 消息队列名称
 * @return int 成功返回0,失败返回-1,并设置errno指明错误原因
 */
int mq_unlink(const char *name);
clock_gettime()
#include <time.h>

/**
 * @brief 获取以struct timespec形式表示的clockid指定的时钟
 * 
 * @param clockid 特定时钟的标识符,常用的是CLOCK_REALTIME,表示当前真实时间的时钟
 * @param tp 用于接收时间信息的缓存
 * @return int 成功返回0,失败返回-1,同时设置errno以指明错误原因
 */
int clock_gettime(clockid_t clockid, struct timespec *tp);

父子进程使用消息队列通讯

#include <fcntl.h>
#include <sys/stat.h>
#include <mqueue.h>
#include <stdio.h>
#include <time.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>

int main(int argc, char const *argv[])
{
    struct mq_attr attr;
    // 有用的参数,表示消息队列的长度
    attr.mq_maxmsg = 10;
    attr.mq_msgsize = 100;
    // 下面参数没什么作用,填0就行了
    attr.mq_flags = 0;
    attr.mq_curmsgs = 0;
    // 创建消息队列
    char* queue_name = "/fitz";
    mqd_t mqdes = mq_open(queue_name, O_RDWR | O_CREAT, 0664, &attr); // 队列文件的引用符
    if (mqdes == (mqd_t)-1) {
        perror("mq_open");
        exit(EXIT_FAILURE);
    }
    pid_t pid = fork();
    if (pid == -1) {
        perror("fork");
        exit(EXIT_FAILURE);
    }
    // 子进程---接收消息
    else if (pid == 0) {
        char recv_buf[100];
        struct timespec time_info;
        for (int i = 0;i < 5;i++) {
            // 情况接收缓冲器
            memset(recv_buf, 0, sizeof(recv_buf));
            clock_gettime(0, &time_info);
            time_info.tv_sec += 15;
            // 把接收的消息打印出来
            if(mq_timedreceive(mqdes, recv_buf, sizeof(recv_buf), 0,&time_info) == -1){
                perror("mq_timedreceive");
            }
            printf("子进程接受的数据是:%s\n", recv_buf);
        }
    }
    // 父进程---发送消息
    else {
        char send_buf[100];
        struct timespec time_info; // 超时时间
        for (int i = 0;i < 5;i++) {
            memset(send_buf, 0, sizeof(send_buf));
            sprintf(send_buf, "父进程第%d发\n", i + 1);
            // 获取当前时间
            clock_gettime(0, &time_info);
            time_info.tv_sec += 5; 
            if (mq_timedsend(mqdes, send_buf, strlen(send_buf), 0, &time_info) == -1) {
                perror("mq_timedsend");
            }
            printf("父进程发送消息成功\n");
            sleep(1);
        }
    }

    // 不管是父进程还是子进程,都需要释放消息队列的引用
    close(mqdes);
    // 清除消息队列只需要清除一部分就行了
    if (pid > 0) { 
        mq_unlink(queue_name);
    }

    printf("父子进程通讯完毕\n");
    return 0;
}

平行进程使用消息队列通讯(生产者-消费者)

 生产者(producer.c)

#include <fcntl.h>
#include <sys/stat.h>
#include <mqueue.h>
#include <stdio.h>
#include <time.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>

int main(int argc, char const *argv[])
{
    struct mq_attr attr;
    // 有用的参数,表示消息队列的长度
    attr.mq_maxmsg = 10;
    attr.mq_msgsize = 100;
    // 下面参数没什么作用,填0就行了
    attr.mq_flags = 0;
    attr.mq_curmsgs = 0;
    // 创建消息队列
    char* queue_name = "/prodecer_consumer";
    mqd_t mqdes = mq_open(queue_name, O_RDWR | O_CREAT, 0664, &attr); // 队列文件的引用符
    if (mqdes == (mqd_t)-1) {
        perror("mq_open");
        exit(EXIT_FAILURE);
    }
    
    // 发送端接受控制台数据,发送给对方
    char buf[100];
    struct timespec time_info;
    while (1)
    {
        memset(buf, 0, sizeof(buf));
        ssize_t count = read(STDIN_FILENO, buf, sizeof(buf));
        clock_gettime(0, &time_info);
        time_info.tv_sec += 5;
        if (count == -1) {
            perror("read");
            continue;
        }
        // ctrl+d 退出控制台的情况,发送EOF告诉对方结束了
        else if (count == 0) {
            printf("EOF ,exit \n");
            char eof = EOF;
            if (mq_timedsend(mqdes, &eof, 1, 0, &time_info) == -1) {
                perror("mq_timedsend");
            }
            break;
        }
        // 正常发送
        else {
            if (mq_timedsend(mqdes, buf, strlen(buf), 0, &time_info) == -1) {
                perror("mq_timedsend");
            }
            printf("发送成功\n");
        }
    }
    
    printf("发送端关闭\n");
    close(mqdes); // 关闭描述符
    
    return 0;
}

消费者(customer.c)

#include <fcntl.h>
#include <sys/stat.h>
#include <mqueue.h>
#include <stdio.h>
#include <time.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>

int main(int argc, char const *argv[])
{
    struct mq_attr attr;
    // 有用的参数,表示消息队列的长度
    attr.mq_maxmsg = 10;
    attr.mq_msgsize = 100;
    // 下面参数没什么作用,填0就行了
    attr.mq_flags = 0;
    attr.mq_curmsgs = 0;
    // 创建消息队列
    char* queue_name = "/prodecer_consumer";
    mqd_t mqdes = mq_open(queue_name, O_RDWR | O_CREAT, 0664, &attr); // 队列文件的引用符
    if (mqdes == (mqd_t)-1) {
        perror("mq_open");
        exit(EXIT_FAILURE);
    }
    
    // 发送端接受控制台数据,发送给对方
    char buf[100];
    struct timespec time_info;
    while (1)
    {
        memset(buf, 0, sizeof(buf));
        clock_gettime(0, &time_info);
        time_info.tv_sec += 15;
        
        if (mq_timedreceive(mqdes, buf, sizeof(buf), NULL, &time_info) == -1) {
            perror("mq_timedreceived");
        }
        // 判断数据是否EOF结束
        if (buf[0] == EOF) {
            printf("生产者发送了结束信息,结束通讯\n");
            break;
        }
        else {
            printf("接收的消息:%s\n", buf);
        }
    }

    printf("接收端关闭\n");
    close(mqdes); // 关闭描述符
    // 这里选择消费者来结束队列,这样是比较合理的,因为最后一个信息EOF是被消费者接受到的,在此之前队列必须存在
    mq_unlink(queue_name); // 清除消息队列
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2276605.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

YOLOv8从菜鸟到精通(二):YOLOv8数据标注以及模型训练

数据标注 前期准备 先打开Anaconda Navigator&#xff0c;点击Environment&#xff0c;再点击new(new是我下载anaconda的文件夹名称)&#xff0c;然后点击创建 点击绿色按钮&#xff0c;并点击Open Terminal 输入labelimg便可打开它,labelimg是图像标注工具&#xff0c;在上篇…

mac homebrew配置使用

本文介绍mac上homebrew工具的安装、配置过程。homebrew功能类似于centos的yum&#xff0c;用于软件包的管理&#xff0c;使用上有命令的差异。 本次配置过程使用mac&#xff0c;看官方文档&#xff0c;在linux上也可以用&#xff0c;但我没试过&#xff0c;有兴趣的同学可以试试…

《使用 YOLOV8 和 KerasCV 进行高效目标检测》

《使用 YOLOV8 和 KerasCV 进行高效目标检测》 作者&#xff1a;Gitesh Chawda创建日期&#xff1a;2023/06/26最后修改时间&#xff1a;2023/06/26描述&#xff1a;使用 KerasCV 训练自定义 YOLOV8 对象检测模型。 &#xff08;i&#xff09; 此示例使用 Keras 2 在 Colab 中…

【Uniapp-Vue3】onShow和onHide钩子的对比和执行顺序

页面生命周期函数的执行顺序是onLoad>onShow>onReady&#xff0c;其中只有onReady中才能获取到DOM节点。 一、onShow函数 每一次的页面切入都会触发onShow函数。 import {onShow} from "dcloudio/uni-app"; onShow(()>{...}) 如果我点击“跳转页面1”再返…

GPT 系列论文精读:从 GPT-1 到 GPT-4

学习 & 参考资料 前置文章 Transformer 论文精读 机器学习 —— 李宏毅老师的 B 站搬运视频 自监督式学习(四) - GPT的野望[DLHLP 2020] 來自猎人暗黑大陆的模型 GPT-3 论文逐段精读 —— 沐神的论文精读合集 GPT&#xff0c;GPT-2&#xff0c;GPT-3 论文精读【论文精读】…

(STM32笔记)十二、DMA的基础知识与用法 第二部分

我用的是正点的STM32F103来进行学习&#xff0c;板子和教程是野火的指南者。 之后的这个系列笔记开头未标明的话&#xff0c;用的也是这个板子和教程。 DMA的基础知识与用法 二、DMA传输设置1、数据来源与数据去向外设到存储器存储器到外设存储器到存储器 2、每次传输大小3、传…

2024年11月架构设计师综合知识真题回顾,附参考答案、解析及所涉知识点(一)

软考高级系统架构设计师考试包含三个科目&#xff1a;信息系统综合知识、系统架构设计案例分析和系统架构设计论文。考试形式为机考。本文主要回顾2024年下半年(2024-11-10)系统架构设计师考试上午综合知识科目的选择题&#xff0c;同时附带参考答案、解析和所涉知识点。 由于机…

AI浪潮下的IT变革之路:机遇、挑战与重塑未来

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享一篇文章&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目录 AI浪…

【RTSP】使用webrtc播放rtsp视频流

一、简介 rtsp流一般是监控、摄像机的实时视频流,现在的主流浏览器是不支持播放rtsp流文件的,所以需要借助其他方案来播放实时视频,下面介绍下我采用的webrtc方案,实测可行。 二、webrtc-streamer是什么? webrtc-streamer是一个使用简单机制通过 WebRTC 流式传输视频捕获…

【芯片设计- RTL 数字逻辑设计入门 9.2 -- flip flop 与 寄存器的关系详细介绍】

请阅读【嵌入式开发学习必备专栏 Cache | MMU | AMBA BUS | CoreSight | Trace32 | CoreLink | ARM GCC | CSH】 文章目录 Overview硬件角度的 Flip-Flop软件角度的寄存器举例说明硬件设计角度软件开发角度D Flip-Flop 实现基本原理:Verilog 代码:UT 示例JK Flip-Flop 实现基…

Harry技术添加存储(minio、aliyun oss)、短信sms(aliyun、模拟)、邮件发送等功能

Harry技术添加存储&#xff08;minio、aliyun oss&#xff09;、短信sms&#xff08;aliyun、模拟&#xff09;、邮件发送等功能 基于SpringBoot3Vue3前后端分离的Java快速开发框架 项目简介&#xff1a;基于 JDK 17、Spring Boot 3、Spring Security 6、JWT、Redis、Mybatis-P…

深度学习中的卷积和反卷积(三)——卷积和反卷积的计算

1 Stride和Padding的介绍 计算卷积和反卷积绕不开stride和padding的讨论。卷积和反卷积里都有stride和padding参数&#xff0c;但是同一个参数在卷积和反卷积里的作用不一样&#xff0c;非常容易使人困惑&#xff0c;本文试图理清他们的关系&#xff0c;并用实际数值例子演示计…

网页美观进阶:每一种渐变的实现方式

CSS 渐变效果详解&#xff1a;每一种渐变的实现方式 在现代网页设计中&#xff0c;CSS 渐变效果为我们提供了一种丰富的视觉表现手段&#xff0c;可以使简单的背景或元素具有动态和立体感。渐变从单一颜色转变为另一种颜色&#xff0c;为网站增添了活力与美感。在这篇博文中&a…

Mac MySQL 8.0.30的安装(保姆级教程)

目录预览&#xff1a; 一、下载及安装1.下载2.安装 二、环境变量配置1.编辑文件2.添加配置3.配置生效4.版本查看 三、启动1.MySQL服务的启停和状态的查看2.启动mysql2.1 查看服务状态2.2 Mysql关掉重启2.2.1 查看进程2.2.2 杀死进程2.2.3 验证进程是否成功杀死2.2.4 重新启动My…

Linux服务器网络丢包场景及解决办法

一、Linux网络丢包概述 在数字化浪潮席卷的当下&#xff0c;网络已然成为我们生活、工作与娱乐不可或缺的基础设施&#xff0c;如同空气般&#xff0c;无孔不入地渗透到各个角落。对于 Linux 系统的用户而言&#xff0c;网络丢包问题却宛如挥之不去的 “噩梦”&#xff0c;频繁…

浅谈云计算09 | 服务器虚拟化

服务器虚拟化基础 一、虚拟化的定义二、系统虚拟化三、服务器虚拟化的核心要义四、典型实现&#xff1a;探索不同路径五、全虚拟化与半虚拟化六、主流服务器虚拟化技术 一、虚拟化的定义 虚拟化是一种将物理资源抽象为逻辑资源的技术&#xff0c;通过在物理硬件与操作系统、应…

traceroute原理探究

文章中有截图&#xff0c;看不清的话&#xff0c;可以把浏览器显示比例放大到200%后观看。 linux下traceroute的原理 本文通过抓包观察一下linux下traceroute的原理 环境&#xff1a;一台嵌入式linux设备&#xff0c;内网ip是192.168.186.195&#xff0c;其上有192.168.202.…

uni-app无限级树形组件简单实现

因为项目一些数据需要树形展示&#xff0c;但是官网组件没有。现在简单封装一个组件在app中使用&#xff0c;可以无线嵌套&#xff0c;展开&#xff0c;收缩&#xff0c;获取子节点数据等。 简单效果 组件TreeData <template><view class"tree"><te…

4种革新性AI Agent工作流设计模式全解析

文章目录 导读&#xff1a;AI Agent的四种关键设计模式如下&#xff1a;1. 反思2. 工具使用3. 规划4. 多Agent协作 总结内容简介&#xff1a; 导读&#xff1a; AI Agent是指能够在特定环境中自主执行任务的人工智能系统&#xff0c;不仅接收任务&#xff0c;还自主制定和执行…

GO语言实现KMP算法

前言 本文结合朱战立教授编著的《数据结构—使用c语言&#xff08;第五版&#xff09;》&#xff08;以下简称为《数据结构&#xff08;第五版&#xff09;朱站立》&#xff09;中4.4.2章节内容编写&#xff0c;KMP的相关概念可参考此书4.4.2章节内容。原文中代码是C语言&…