Linux多进程和多线程(五)进程间通信-消息队列

  • 多进程(五)
    • 进程间通信
      • 消息队列
    • ftok()函数
    • 创建消息队列
      • 创建消息队列示例
    • msgctl 函数
    • 示例:在上⼀个示例的基础上,加上删除队列的代码
  • 发送消息
    • 示例:
  • 接收消息
  • 示例

多进程(五)

进程间通信

消息队列

消息队列是一种进程间通信机制,它允许两个或多个进程之间进行通信。

消息队列的实现依赖于操作系统提供的消息队列机制,它可以实现不同进程之间的数据交换。

IPC : Inter-Process Communication (进程间通讯)

System V是早期的UNIX系统,曾经被成为AT & T System V,是unix操作系统中比较重要的一个分支
现在的Linux操作系统也支持System V IPC

System V IPC 对象共有三种:

消息队列

共享内存

信号量

System V IPC是由内核维护的若干个对象,通过ipcs命令查询

在这里插入图片描述

每个IPC对象都有自己的唯一ID,可以通过ftok()函数生成IPC对象的ID
消息队列是属于 sytem ipc 的⼀种, 由内核维护与管理 可以通过 ipcs -q 查看

ftok()函数

函数头文件:

#include <sys/ipc.h>

函数原型:

key_t ftok(const char *pathname, int proj_id);

参数说明:

  • pathname: 要生成IPC对象的路径名

  • proj_id: 项目ID,用于区分不同IPC对象

  • 每个存在的文件都有一个id,叫做inode节点号,可以通过ll 命令查询

  • inode节点号 + proj_id(低8bit) 生成key_t类型的值,作为IPC对象的ID

  • key_t类型的值可以用ftok()函数生成,也可以用mkkey()函数生成

函数返回值:

  • 成功: 返回一个key_t类型的整数,该整数是IPC对象的ID
  • 失败: 返回-1,并设置errno

创建消息队列

函数头文件:

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

函数原型:

int msgget(key_t key, int msgflg);

参数说明:

  • key: 要生成IPC对象的ID
  • msgflg: 标志位,用于设置消息队列的访问模式,可取值如下:
    • IPC_CREAT: 如果key对应的消息队列不存在,则创建该消息队列
    • IPC_EXCL: 如果key对应的消息队列已经存在,则返回错误
    • 0: 打开已存在的消息队列
    • 权限控制标志: 如0666,表示创建的消息队列具有读写权限

函数返回值:

  • 成功: 返回消息队列的ID
  • 失败: 返回-1,并设置errno

创建消息队列示例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
//创建消息队列

#define MSG_PATH "."
#define MSG_ID 88
int main(){
    key_t key;//消息队列的key
    key= ftok(MSG_PATH,MSG_ID);//通过文件路径和ID生成key
    if(key==-1){
        printf("ftok()");
        exit(EXIT_FAILURE);
    }

    int msgid= msgget(key,IPC_CREAT|0666);//创建消息队列
    if(msgid==-1){
        printf("msgget()");
        exit(EXIT_FAILURE);
    }
    printf("Message Queue ID: %d\n",msgid);
    return 0;
}

运行结果:

在这里插入图片描述

msgctl 函数

功能: 操作消息队列

函数头文件:

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

函数原型:

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

参数说明:

  • msqid: 要操作的消息队列ID
  • cmd: 操作命令,可取值如下:
    • IPC_STAT: 获取消息队列的状态信息 //和struct msqid_ds *buf参数一起使用
    • IPC_SET: 设置消息队列的状态信息 //和struct msqid_ds *buf参数一起使用
    • IPC_RMID: 删除消息队列 //使用这个命令时,第三个参数为NULL
  • buf: 消息队列属性结构体对象指针,用于设置或获取消息队列的状态信息,

函数返回值:

  • 成功: 返回0
  • 失败: 返回-1,并设置errno

消息队列属性结构体定义如下:

struct msqid_ds
{
#ifdef __USE_TIME_BITS64
# include <bits/types/struct_msqid64_ds_helper.h>
#else
  struct ipc_perm msg_perm;	/* 描述操作权限的结构 */
  
  struct ipc_perm
{
  __key_t __key;				/* Key.  */
  __uid_t uid;					/* 所有者的用户 ID.  */
  __gid_t gid;					/* 所有者组 ID.  */
  __uid_t cuid;					/* 创作者的用户 ID.  */
  __gid_t cgid;					/* 创作者的组 ID.  */
  __mode_t mode;				/* 读/写权限.  */
  unsigned short int __seq;			/* 序列号.  */
  unsigned short int __pad2;  
  __syscall_ulong_t __glibc_reserved1; 
  __syscall_ulong_t __glibc_reserved2;
};
  
  
# if __TIMESIZE == 32
  __time_t msg_stime;		//上次发送消息的时间
  unsigned long int __msg_stime_high; 
  __time_t msg_rtime;		//上次接收消息的时间
  unsigned long int __msg_rtime_high;
  __time_t msg_ctime;	//消息队列的创建时间
  unsigned long int __msg_ctime_high;
# else
  __time_t msg_stime;		//上次发送消息的时间
  __time_t msg_rtime;		//上次接收消息的时间
  __time_t msg_ctime;		//消息队列的创建时间
# endif
  __syscall_ulong_t __msg_cbytes;  //消息队列中消息的字节数
  msgqnum_t msg_qnum;		//消息队列中消息的数量
  __pid_t msg_lspid;		//最后发送消息的进程ID
  __syscall_ulong_t __glibc_reserved4; //保留
  __syscall_ulong_t __glibc_reserved5;//保留
#endif
};

示例:在上⼀个示例的基础上,加上删除队列的代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
//创建消息队列

#define MSG_PATH "."
#define MSG_ID 88
int main(){
    key_t key;//消息队列的key
    key= ftok(MSG_PATH,MSG_ID);//通过文件路径和ID生成key
    if(key==-1){
        printf("ftok()");
        exit(EXIT_FAILURE);
    }

    int msgid= msgget(key,IPC_CREAT|0666);//创建消息队列
    if(msgid==-1){
        printf("msgget()");
        exit(EXIT_FAILURE);
    }
    printf("Message Queue ID: %d\n",msgid);

    int ret= msgctl(msgid,IPC_RMID,NULL);//删除消息队列
    if(ret==-1){
        printf("msgctl()");
        exit(EXIT_FAILURE);
    }
    printf("消息队列已删除.\n");


    return 0;
}

发送消息

发送消息队列的函数是msgsnd()
msgsnd函数是用于向System V消息队列发送消息的一个系统调用。消息队列是一种由操作系统提供的进程间通信(IPC)机制,允许一个进程发送消息并且另一个进程接收消息。以下是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);

参数说明:

  • msqid: 要发送的消息队列ID

  • msgp: 要发送的消息内容指针

  • msgsz: 要发送的消息内容长度

  • msgflg: 标志位,用于设置消息发送的模式,可取值如下:

    • IPC_NOWAIT: 若消息队列已满,则立即返回错误 ,返回-1,并设置errno为EAGAIN
    • 0: 若消息队列已满,则阻塞等待直到消息队列空闲
    • 对发送消息来说,有意义的flags标志为IPC_NOWAIT,
    • 在消息队列没有足够的空间容纳要发送的数据时,设置了该标志,
    • 则msgsnd()函数立刻出错返回,
    • 否则发送消息的进程被阻塞,直至消息队列有空间或队列被删除时返回。

函数返回值:

  • 成功: 返回0
  • 失败: 返回-1,并设置errno

在这里插入图片描述

示例:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define MSG_PATH "/home/gopher"
#define MSG_ID 88

//消息队列发送  MessageQueues2中接收
#define MSG_SZ 100
struct msgbuf{//消息队列结构
    long mtype;//消息类型
    char mtext[MSG_SZ];//消息内容
};
int main(){



    key_t key;//消息队列的key
    //通过文件路径和ID生成key,
    key= ftok(MSG_PATH,MSG_ID);
    if(key==-1){
        printf("ftok()");
        exit(EXIT_FAILURE);
    }
    printf("key: %d\n",key);

    //使用key 创建消息队列
    int msgid= msgget(key,IPC_CREAT|0666);
    if(msgid==-1){
        printf("msgget()");
        exit(EXIT_FAILURE);
    }
    printf("消息队列ID: %d\n",msgid);

    //准备消息模板
    struct msgbuf msg;//消息队列结构
    msg.mtype=101;//消息类型
    strcpy(msg.mtext,"Hello,world!");//消息内容
    //msgsnd函数第一个参数是消息队列ID,第二个参数是消息队列结构的指针,第三个参数是消息长度,第四个参数是消息类型
    int ret= msgsnd(msgid,(const void*)&msg,strlen(msg.mtext)+1,0);// 0: 若消息队列已满,则阻塞等待直到消息队列空闲
    if(ret==-1){
        printf("msgsnd()");
        exit(EXIT_FAILURE);
    }

    return 0;
}

常见错误

EINVAL: 无效的消息队列标识符或无效的消息大小。

EIDRM: 消息队列已被标记为删除。

EINTR: 调用被信号中断。

EAGAIN: 消息队列满,并且指定了IPC_NOWAIT标志。

接收消息

msgrcv函数是用于在System V消息队列中接收消息的函数。msgrcv函数从消息队列中读取消息,并从队列中删除该消息。以下是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);

参数说明:

msqid: 消息队列标识符,通常由msgget函数返回。
msgp: 指向用户定义的消息缓冲区的指针。结构体中至少应包含一个long mtype成员,用于指定消息的类型。其余部分可根据需要定义为消息数据。
msgsz: 指定消息数据部分的最大字节数(不包括mtype成员的大小)。
msgtyp: 指定要接收的消息类型。如果msgtyp为零,则接收队列中的第一个消息。
msgflg: 操作标志,可以是以下值的按位或:
      IPC_NOWAIT: 如果没有合适的消息可供接收,函数立即返回而不是阻塞。
      MSG_EXCEPT: 接收不等于msgtyp的第一个消息。
      MSG_NOERROR: 如果消息过长,将其截断。

返回值:

  • 成功: 返回实际接收的消息的字节数。
  • 失败: 返回-1,并设置errno。

常见错误

EINVAL: 无效的消息队列标识符。

EINTR: 调用被信号中断。

E2BIG: 消息太长并且未指定MSG_NOERROR标志。

ENOMSG: 没有符合msgtyp条件的消息,并且未指定IPC_NOWAIT标志。

示例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define MSG_PATH "/home/gopher"
#define MSG_ID 88

//消息队列接收
#define MSG_SZ 100
struct msgbuf{//消息队列结构
    long mtype;//消息类型
    char mtext[MSG_SZ];//消息内容
};
int main(){



    key_t key;//消息队列的key
    //通过文件路径和ID生成key,
    key= ftok(MSG_PATH,MSG_ID);
    if(key==-1){
        printf("ftok()");
        exit(EXIT_FAILURE);
    }
    printf("key: %d\n",key);
    //使用key 创建消息队列
    int msgid= msgget(key,IPC_CREAT|0666);
    if(msgid==-1){
        printf("msgget()");
        exit(EXIT_FAILURE);
    }
    printf("消息队列ID: %d\n",msgid);
    //准备消息模板
    struct msgbuf msg;//消息队列结构
    msg.mtype=101;//消息类型
    ssize_t nbytes;//接收到的字节数

    nbytes= msgrcv(msgid,(void*)&msg,MSG_SZ,101,0);//接收消息  //0接收第一条消息
    //MSG_SZ为msg能接受的最大字节数
    if(nbytes==-1){
        printf("msgrcv()");
        exit(EXIT_FAILURE);
    }
    printf("消息类型: %ld\n",msg.mtype);
    printf("已收到消息: %s\n",msg.mtext);

    return 0;
}



)");
        exit(EXIT_FAILURE);
    }
    printf("消息队列ID: %d\n",msgid);
    //准备消息模板
    struct msgbuf msg;//消息队列结构
    msg.mtype=101;//消息类型
    ssize_t nbytes;//接收到的字节数

    nbytes= msgrcv(msgid,(void*)&msg,MSG_SZ,101,0);//接收消息  //0接收第一条消息
    //MSG_SZ为msg能接受的最大字节数
    if(nbytes==-1){
        printf("msgrcv()");
        exit(EXIT_FAILURE);
    }
    printf("消息类型: %ld\n",msg.mtype);
    printf("已收到消息: %s\n",msg.mtext);

    return 0;
}



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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

单例模式详解:概念与实用技巧

目录 单例模式单例模式结构单例模式适用场景单例模式优缺点练手题目题目描述输入描述输出描述输入示例输出示例提示信息题解 单例模式 单例模式是一种创建型设计模式&#xff0c; 让你能够保证一个类只有一个实例&#xff0c; 并提供一个访问该实例的全局节点。 只有一个实例的…

【深入理解Java虚拟机】判断垃圾-引用计数法及其缺陷

什么是引用计数法 引用计数法用来判断对象是否存活 给对象中添加一个引用计数器&#xff0c;每当有一个地方引用它时&#xff0c;计数器的值加一&#xff1b;当引用失效时&#xff0c;计数器的值就减一&#xff0c;任何时刻计数器为0的对象是不可能在被使用的。&#xff08;存…

c++类模板及应用

文章目录 为什么要有函数模板一般实现举例类模板举例 继承中类模板的使用特殊情况 友元函数模板类和静态成员类模板实践 为什么要有函数模板 项目需求: 实现多个函数用来返回两个数的最大值&#xff0c;要求能支持char类型、int类型、double 一般实现举例 类模板举例 继承中类…

2.2 ROS2话题通信

场景 话题通信是ROS中使用频率最高的一种通信模式&#xff0c;话题通信是基于发布订阅模式的&#xff0c;也即&#xff1a;一个节点发布消息&#xff0c;另一个节点订阅该消息。话题通信的应用场景也极其广泛&#xff0c;比如如下场景&#xff1a; 机器人在执行导航功能&#…

肺炎-X光-图像分类数据集

肺炎-X光-图像分类数据集 数据集&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1bt6tf-jHqgufKqPmCFHbrQ?pwdaj54 提取码&#xff1a;aj54 数据集信息介绍&#xff1a; 文件夹 健康 中的图片数量: 1575 文件夹 新冠肺炎 中的图片数量: 1728 文件夹 普通肺炎 中的…

AI:开发者的超级助手,而非取代者

AI&#xff1a;开发者的超级助手&#xff0c;而非取代者 引言 在这个日新月异的科技时代&#xff0c;人工智能&#xff08;AI&#xff09;已悄然渗透到我们生活的方方面面&#xff0c;尤其是在软件开发领域&#xff0c;它正以一种前所未有的方式改变着我们的工作方式。作为一名…

Redis 中的通用命令(命令的返回值、复杂度、注意事项及操作演示)

Redis 中的通用命令(高频率操作) 文章目录 Redis 中的通用命令(高频率操作)Redis 的数据类型redis-cli 命令Keys 命令Exists 命令Expire 命令Ttl 命令Type命令 Redis 的数据类型 Redis 支持多种数据类型&#xff0c;整体来说&#xff0c;Redis 是一个键值对结构的&#xff0c;…

《数据结构与算法基础 by王卓老师》学习笔记——2.5线性表的链式表示与实现1

1.链式表示 2.链表举例 3.链式存储的相关术语 4.三个讨论题

【软件测试】之自动化测试

&#x1f3c0;&#x1f3c0;&#x1f3c0;来都来了&#xff0c;不妨点个关注&#xff01; &#x1f3a7;&#x1f3a7;&#x1f3a7;博客主页&#xff1a;欢迎各位大佬! 文章目录 什么是自动化测试Selenium介绍什么是SeleniumSelenium的特点工作原理 SeleniumJava环境搭建下载…

数学建模------Matlab数据可视化

目录 1.plot函数 &#xff08;1&#xff09;函数介绍 &#xff08;2&#xff09;参数介绍 &#xff08;3&#xff09;图形美化 &#xff08;4&#xff09;背景更改 &#xff08;5&#xff09;多组绘制 &#xff08;6&#xff09;图形叠加 &#xff08;7&#xff09;添加…

【2024最新华为OD-C/D卷试题汇总】[支持在线评测] 英文单词联想(100分) - 三语言AC题解(Python/Java/Cpp)

🍭 大家好这里是清隆学长 ,一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 💻 ACM银牌🥈| 多次AK大厂笔试 | 编程一对一辅导 👏 感谢大家的订阅➕ 和 喜欢💗 📎在线评测链接 https://app5938.acapp.acwing.com.cn/contest/2/problem/OD…

Flume集群部署(手把手部署图文详细版)

前景概要&#xff1a; Kafka消息订阅系统在大数据业务中有着重要运用&#xff0c;尤其在实时业务中&#xff0c;kafka是必不可少的组件之一。 Flume是大数据组件中重要的数据采集工具&#xff0c;我们常利用Flume采集各种数据源的数据供其他组件分析使用。例如在实时业务中&…

大白菜U盘启动工具

大白菜如何u盘启动进winpe装系统大白菜是一款非常实用的U盘启动盘制作工具&#xff0c;可以帮助用户快速地将U盘制作成启动盘&#xff0c;从而方便地进行系统安装、维护和修复等操作。官方网站&#xff1a; 大白菜u盘启动盘制作工具_大白菜u盘装系统_大白菜pe_大白菜官网-首页…

机器人控制系列教程之Stewart平台简介和运动学分析

Stewart平台简介及应用场景 六自由度 Stewart 并联机器人结构简图如下图所示&#xff0c;主要有一个固定平台和一个移动平台以及六个可伸缩的推杆组成&#xff0c;通常情况下&#xff0c;固定平台与底座连接&#xff0c;移动平台在空间具有六个自由度&#xff0c;通过六个推杆…

设置Docker中时区不生效的问题

项目中使用docker-compose&#xff0c;并通过以下方式设置了时区 environment:- SET_CONTAINER_TIMEZONEtrue- CONTAINER_TIMEZONEAsia/Shanghai 但是并没有正确生效&#xff0c;网上有很多博客都在推荐这个做法&#xff0c;另外一种是使用标准环境标量 -TZAsia/Shangehai …

大型网站软件系统架构演进过程

在我们的生活中,通常会使用大型网站系统,比如购物网站淘宝,京东,阿里1688;大型搜索引擎网站百度,社交类的如腾讯旗下的微信,QQ及新浪旗下的微博等,他们通常都有一下特点: 高并发、大流量&#xff1a;这些系统必须能够处理成千上万甚至数百万的并发用户请求&#xff0c;以及持续…

Entity Framework EF Migration 迁移

针对Code First来说关注的只有实体类。当需求变更时只需要添加新的实体类或者在实体类中添加、删除、修改属性即可。但是修改完成之后要如何将修改同步到数据库中&#xff1f; migration 机制就出现了 ●启用Migrations   ●通过Add-Migration添加Migration   ●Update-D…

Feign 原理流程图练习-01

目录 作业: 老师给的参考流程图 要求 解答 知识扩展 Feign基础原理 接口定义 代理对象生成 请求调用 请求发送 响应处理 容错与熔断 总结 作业: 老师给的参考流程图 pdf版本 【金山文档 | WPS云文档】 Feign https://kdocs.cn/l/ctbagIyxN348 ​ 要求 结合上面…

Kafka集群部署(手把手部署图文详细版)

1.1.1 部署zookpeer 在node02下载并解压zookeeper软件包 cd /usr/local wget https://archive.apache.org/dist/zookeeper/zookeeper-3.4.6/zookeeper-3.4.6.tar.gz 或者&#xff1a;scp cat192.168.28.100:/home/cat/zookeeper-3.4.6.tar.gz /tmp&#xff08;注意目录&#xf…