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

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)、进程消息队列通信演示

相关文章:

  • 同步/异步电路;同步/异步复位
  • 二叉树结构与遍历
  • 2025有铜半孔工艺参数设计规范
  • rt-linux下的底层锁依赖因cgroup cpu功能导致不相干进程的高时延问题
  • 引领“零碳会议”新风尚!第十届国际贸易发展论坛——绿色发展专场,在京举办
  • Linux探秘:驾驭开源,解锁高性能——基础指令(续集)
  • 【RocketMQ 生产者和消费者】- 生产者启动源码-创建 MQClientInstance(2)
  • 硬件,软件和进程
  • 【Linux】进程间通信(四):System V标准(共享内存、消息队列、信息量)
  • 通过Python 在Excel工作表中轻松插入行、列
  • Mysql 刷题Day09
  • nvm版本管理下pnpm 安装失败问题解决
  • 智能赋能与人文滋养:人工智能时代高中数字化教育的范式重构
  • 【视频】使用海康SDK保存的MP4无法在浏览器(html5)中播放
  • 解决前端路由切换导致Keycloak触发页面刷新问题
  • landsat卫星遥感影像下载、处理教程
  • 数据结构 -- B树和B+树
  • 学习vue3:跨组件通信(provide+inject)
  • 50天50个小项目 (Vue3 + Tailwindcss V4) ✨ | Blurry Loading (毛玻璃加载)
  • PostgreSQL 14 pacemaker 高可用集群
  • 怎样学好网站开发/百度推广登陆入口官网
  • 网站推广工具工作室/网络广告文案案例
  • 动态网站结构/网站seo优化推广外包
  • 网站设计推荐/动态网站设计
  • 上饶时时彩网站建设/海南乐秀同城群软件下载
  • 网站模板的制作怎么做/百度账号快速注册