当前位置: 首页 > ds >正文

Linux系统编程 | IPC对象---消息队列

        在Linux系统编程的进程间通信部分中,前面几篇博客已经对管道通信signal信号做了较为完善的梳理总结,接下来将对进程间通信部分的IPC对象进行梳理,初步预计分为IPC对象的消息队列共享内存信号量三个小部分进行讲解梳理。如果对博主前面的文章感兴趣的网友,欢迎访问我的Linux系统编程专栏进行查阅交流。

系统编程_奔跑的蜗牛!的博客-CSDN博客https://blog.csdn.net/weixin_49337111/category_12952742.html?spm=1001.2014.3001.5482

1、IPC对象

(1)、什么是IPC对象

        IPC对象是活动在内核级别的一种进程间通信的工具。各种不同的IPC其实是在不同时期逐步引入的,在UNIX伯克利版本system-V(念作系统五,V是罗马数字,是Unix伯克利分支的版本号)中引入的三种通信方式(消息队列共享内存信号量组)被称为IPC对象,它们有较多共同的特性:

  • 在系统中使用键值(KEY)唯一确定,类似于文件系统中的文件路径(绝对路径)
  • 当某个进程创建(或打开)一个IPC对象时,将会获得一个整型ID类似于文件描述符
  • IPC对象属于系统,而不是进程,因此在没有明确删除操作的情况下,IPC对象不会因为进程的退出而消失

        如果需要使用IPC对象实现进程之间的通信,首先必须为IPC对象申请对应的资源。比如,如果要使用消息队列来通信,那么就必须先申请消息队列对应的 key 值ID 号

(2)、查看IPC对象

        ipcs -a

Snail@ubuntu:~/Desktop/process$ ipcs -a------ Message Queues --------            //消息队列
key        msqid      owner      perms      used-bytes   messages    ------ Shared Memory Segments --------    //共享内存
key        shmid      owner      perms      bytes      nattch     status      
0x00000000 9          Snail      600        524288     2          dest         ------ Semaphore Arrays --------//信号量
key        semid      owner      perms      nsems     Snail@ubuntu:~/Desktop/process$ 

        key值:类似于 文件的路径名

        ID号:类似于文件描述符

(3)、删除IPC对象

        删除消息队列:

                ipcrm -q 消息队列的key值

                ipcrm -q 消息队列的ID值

        删除共享内存:

                ipcrm -m 共享内存的key值

                ipcrm -m 共享内存的ID值

        删除信号量:

                ipcrm -s 信号量的key值

                ipcrm -s 信号量的ID值

2、消息队列基础

        消息队列是system-V三种IPC对象之一,其最主要的特征是允许发送的数据携带类型,具有相同类型的数据在消息队列内部排队,读取的时候也要指定类型,然后依次读出数据。这使得消息队列用起来就像一个多管道集合,如下图所示:

        由于每个消息都携带有类型,相同的类型自成一队,因此读取方可以根据类型来“挑选”不同的队列,也因此MSG适用于所谓“多对一”的场景,经典案例是系统日志:多个不同的、不相关的进程向同一管道输入数据。

3、消息队列API

(1)、ftok

//函数原型
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);//函数功能
​​生成一个唯一的 key 值​​,用于标识 IPC 资源//函数参数
pathname:一个已经存在的文件路径,用于计算 key 值
proj_id:​​项目 ID,用于区分不同的 IPC 资源//函数返回值
成功返回key值
失败返回-1,并设置errno

(2)、msgget

//函数原型
#include <sys/msg.h>
int msgget(key_t key, int msgflg);//函数功能
创建或获取一个消息队列,用于进程间通信;
如果指定的消息队列不存在且 msgflg 包含 IPC_CREAT,则创建一个新的消息队列;
如果消息队列已存在,则直接返回其标识符(msgid)。//函数参数
key:消息队列的唯一标识符
msgflg:控制消息队列的创建和访问权限//函数返回值
成功返回消息队列的标识符(msgid)
失败返回-1,并设置errno

(3)、msgsnd

//函数原型
#include <sys/msg.h>
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);//函数功能
​​向消息队列中发送一条消息​​。
消息队列由 msqid 标识,消息内容由 msgp 指向的结构体提供。
如果消息队列已满,且 msgflg 包含 IPC_NOWAIT,则立即返回错误;否则阻塞等待队列有空闲空间。//函数参数
msqid:消息队列标识符
msgp:指向要发送的消息结构体的指针,结构体必须包含 long mtype 成员(消息类型)
msgsz:消息正文的大小(不包括 mtype 的大小)
msgflg:控制发送行为: 0:默认,如果队列满则阻塞等待,IPC_NOWAIT:如果队列满则立即返回错误(EAGAIN)//函数返回值
成功返回0
失败返回-1,并设置errno

(4)、msgrcv

//函数原型
#include <sys/msg.h>
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
int msgflg);//函数功能
从消息队列中读取数据(接收数据)//函数参数
msqid:消息队列标识符
msgp:指向接收消息的缓冲区(必须是 struct msgbuf 或兼容结构)
msgsz:要接收的消息正文的最大大小(不包括 mtype)
msgtyp:消息类型筛选条件> 0:接收第一条类型为 msgtyp​的消息。= 0:接收​​队列中的第一条消息​(忽略类型)。< 0:读取队列中类型值 ≤ |msgtyp| 的最小类型的消息(优先级队列)。
msgflg:控制接收行为:0:默认,如果队列为空则阻塞等待。IPC_NOWAIT:如果队列为空则立即返回错误(ENOMSG)。 MSG_NOERROR:如果消息正文超过 msgsz,则截断并返回实际大小(否则返回错误 E2BIG)。MSG_EXCEPT:接收​​第一条类型 不等于 msgtyp​​ 的消息(仅当 msgtyp > 0 时有效)。//函数返回值
返回接收到的消息正文的实际大小
失败返回-1,并设置errno

(5)、msgctl

//函数原型
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);//函数功能
​​控制消息队列的行为​​,执行各种管理操作,如获取/设置队列属性、删除队列等。//函数参数
msqid:消息队列标识符
cmd:指定要执行的操作命令IPC_STAT:获取消息队列的当前状态(填充 buf)IPC_SET:设置消息队列的属性(需提供 buf)IPC_RMID:删除消息队列(buf 可为 NULL)
buf:指向 msqid_ds 结构体的指针,用于读取或设置队列属性。//函数返回值
成功返回0
失败返回-1,并设置errno

4、程序举例

        消息队列实现两个进程间互发互收。

(1)、消息队列进程1

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/wait.h>//进程1 :  往 消息队列 中 发送数据//自定义的消息队列的数据结构体
struct msgbuf{long mtype;          //数据的编号/类型char mtext[1024]; //数据的正文
};void signalHandle(int signum)
{printf("进程[%d] 收到信号%d\n", getpid(), signum);exit(0); //退出进程
}int main(int argc, char **argv)
{signal(SIGUSR1,signalHandle);//1、确定 文件路径名 ---获取消息队列的key值key_t key = ftok(".", 100);//2、根据文件路径名,打开文件 文件不存在则创建 得到fd ---根据key值 获取消息队列的ID,如果消息队列不存在则创建int msgid = msgget(key,IPC_CREAT|0666);if(msgid == -1){perror("msgget error");return -1;}printf("消息队列 key:%#x msgid:%d\n",key,msgid); //0xpid_t id = fork();if(id>0)//父进程  ---发送数据   10{//3、往文件中写入数据 --------往消息队列中 发送数据struct msgbuf data;while(1){memset(&data,0,sizeof(data));scanf("%s",data.mtext); //数据的正文data.mtype = 10; //数据的编号/类型int ret = msgsnd(msgid, &data,strlen(data.mtext),0);if(ret == -1){perror("msgsnd error");return -1;}//退出处理if(!strcmp(data.mtext,"exit")){//退出之前,通知子进程也退出 kill(id,SIGUSR1);break;}}            }else if(id == 0)//子进程 ---接收数据   100{struct msgbuf data;while(1){memset(&data, 0, sizeof(data));msgrcv(msgid, &data, sizeof(data.mtext), 100,0);printf("pid:[%d]\tmtype:[%ld]\trecv:%s\n", getpid(), data.mtype, data.mtext);//接收到退出,告诉父进程 也要退出if(!strcmp(data.mtext,"exit")){kill(getppid(), SIGUSR1);break;}}exit(0);}waitpid(-1,NULL, 0);return 0;
}

(2)、消息队列进程2

#include<stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/wait.h>
#include <sys/types.h>//进程2 //自定义的消息队列的数据结构体
struct msgbuf{long mtype;            //数据的编号/类型char mtext[1024];     //数据的正文
};void signalHandle(int signum)
{printf("进程[%d] 收到信号%d\n", getpid(), signum);exit(0); //退出进程
}int main(int argc, char **argv)
{signal(SIGUSR1,signalHandle);//1、确定 文件路径名 ---获取消息队列的key值key_t key = ftok(".", 100);//2、根据文件路径名,打开文件 文件不存在则创建 得到fd ---根据key值 获取消息队列的ID,如果消息队列不存在则创建int msgid = msgget(key, IPC_CREAT|0666);if(msgid == -1){perror("msgget error");return -1;}printf("消息队列 key:%#x msgid:%d\n", key, msgid); //0xpid_t id = fork();if(id>0)//父进程  ---发送数据   100{//3、往文件中写入数据 --------往消息队列中 发送数据struct msgbuf data;while(1){memset(&data, 0, sizeof(data));scanf("%s", data.mtext); //数据的正文data.mtype = 100;          //数据的编号/类型int ret = msgsnd(msgid, &data, strlen(data.mtext), 0);if(ret == -1){perror("msgsnd error");return -1;}//退出处理if(!strcmp(data.mtext,"exit")){//退出之前,通知子进程也退出 kill(id,SIGUSR1);break;}}}else if(id == 0)//子进程 --接收数据   10{struct msgbuf data;while(1){memset(&data, 0, sizeof(data));msgrcv(msgid, &data, sizeof(data.mtext), 10, 0);printf("pid:[%d]\tmtype:[%ld]\trecv:%s\n", getpid(), data.mtype, data.mtext);//接收到退出,告诉父进程 也要退出if(!strcmp(data.mtext, "exit")){kill(getppid(), SIGUSR1);break;}}exit(0);}waitpid(-1, NULL, 0);return 0;
}

(3)、进程消息队列通信演示

http://www.xdnf.cn/news/8232.html

相关文章:

  • DeepSeek:开启IT领域人效管理新时代
  • Java-根据路径获取JSON字符串的value值
  • zabbix 常见问题
  • 深入解析JVM垃圾回收器:原理、实践与调优指南
  • 实用重复文件批量处理工具
  • 关于SQL SERVER中round函数的用法和示例
  • 一台机器怎么部署k8s集群
  • React-fiber架构
  • Python可视化设计原则
  • 【424. 替换后的最长重复字符】
  • docker-compose常用命令介绍
  • 已经 上线 Vue 项目 国际化 i18n 中译英
  • OpenCV 图像对象的创建与赋值
  • Apollo10.0学习——planning模块(9)之参数详解一
  • Vscode +Keil Assistant编译报错处理
  • C++ -- vector
  • 系统性能分析基本概念(5) : 何时开始性能分析
  • 【语法】C++的map/set
  • 平安健康2025年一季度深耕医养,科技赋能见成效
  • Android Service与BroadcastReceiver深度解析:从零到一的实现与优化
  • python实现web请求
  • 力扣小题, 力扣113.路径总和II力扣.111二叉树的最小深度 力扣.221最大正方形力扣5.最长回文子串更加优秀的算法:中心扩展算法
  • 解压软件推荐:功能、优缺点及使用技巧
  • 【题解-洛谷】P11951 [科大国创杯初中组 2023] 数数
  • w~自动驾驶~合集13
  • 杰发科技AC7840——使用内部温度
  • IO多路复用
  • mysql 创建用户,创建数据库,授权
  • Spring Boot 内置工具类汇总与讲解
  • NumPy 2.x 完全指南【十八】数组元素的新增和删除