linux多线(进)程编程——(7)消息队列

news2025/5/28 4:16:05

前言

现在修真界大家的沟通手段已经越来越丰富了,有了匿名管道,命名管道,共享内存等多种方式。但是随着深入使用人们逐渐发现了这些传音术的局限性。
匿名管道:只能在有血缘关系的修真者(进程)间使用,局限性大。
命名管道:需要创建出一个文件,并通过文件方式沟通,很不方便。
共享内存:这个就更可怕了,传递速度太快,大量无关信息也可能会涌入脑海,而且两个人如果想法有冲突就会把各自的思路相互覆盖。(这个涉及到到进程间同步的一些知识,后续解决。)
无数心怀远大抱负的修士期待着能解决这些问题,从而一句成名。很快,有人成功了,他提出了一种新的沟通方式(消息队列)。

消息队列

消息队列,消息是定语,修饰队列,也就是装消息的队列,本质上是一个链表(这里涉及到队列的两种实现形式:数组环形实现和链表实现)。
为什么使用链表实现消息队列呢?因为这个队列并不是十分纯粹的先入先出操作。偶尔会涉及到消息的筛选。所以使用链表来实现。
消息队列由进程创建,但是它位于内核空间,由内核管理,用于在不同进程间传递数据。
在这里插入图片描述

创建一个消息队列 / 获取一个已经存在的消息队列id

int msgget(key_t key, int msgflg);

其中各个参数的含义是:
key:键值,操作系统中唯一
msgflg:标志位,用于设置用户权限
返回值:消息队列的id,用来表示这个消息队列。
当消息队列已经存在时,我们会字节根据键值索引得到这个消息队列的id。之后我们操作消息队列都是使用这个id而不是键值。

这里插一句,大家可以发现,在linux中经常有这种一个id代表一个对象的管理手段,无论是文件,还是共享内存,还是今天学的消息队列都是这样。未来这种手段我们还会见到更多,大家要学会把他们联系起来。

先创建一个消息队列:

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

int main() {
    int id = msgget(1, IPC_CREAT);
    return 0;
}

创建成功后我们使用ipcs指令查看系统中的消息队列(这个我指令在共享内存中讲过)。从结果中我们可以看到现在我们的系统上有一个key值为1的消息队列,他的id是0。

lol@hyl:~/work/linux_study/msgque$ ipcs

------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages    
0x00000001 0          hyl        0          0            0           

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status      

------ Semaphore Arrays --------
key        semid      owner      perms      nsems  

大家可以发现,在我输入ipcs指令时我的程序应该已经停止运行了,但是我们的消息队列仍然存在,这个与之前将的共享内存类似。因此如果我们在程序中创建了消息队列而不及时释放,会引起严重的内存泄漏。这种内存泄漏更加严重,因为即使进程停止了它也不会被系统回收。
在这里我们手动删除消息队列,输入ipcrm -q 0(这个指令也是我们之前讲过的,-q代表消息对了 -m代表共享内存。)

lol@hyl:~/work/linux_study/msgque$ ipcrm -q 0
lol@hyl:~/work/linux_study/msgque$ ipcs

------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages    

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status      

------ Semaphore Arrays --------
key        semid      owner      perms      nsems   

创建好了消息队列后我们如何实现进程间通信呢?
发送消息

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

参数的意义:
msqid:消息队列的id
msgp:存放消息的缓冲区,类型为struct msgbuf,定义如下:

#define DATA_SIZE	128
// 在源文件中仿造这个格式自行定义即可
struct msgbuf{
	long mtype;//消息类型(>0)
	char mtext[DATA_SIZE];//消息文本
};

msgsz:发送数据的大小,这里是msgp结构体中mtext的大小DATA_SIZE
msgflg:是否阻塞,为0表示阻塞发送

接收消息:
在struct msgbuf类型的结构体中,mtype成员的作用主要是在接受数据时设置优先级,接收消息的函数定义为:

ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);

其余参数与上面发送函数相同,主要是:
msgtyp:表示接收数据的类型。
当msgtyp = 0时,则返回队头数据,暨最先进入队列的数据。
当msgtyp > 0时,返回队列中消息类型(暨msgp->mtype)等于msgtyp 的数据。
当msgtyp < 0时,返回其类型小于或等于mtype参数的绝对值的最小的一个消息。
有了这个参数我们就可以实现关键消息优先处理的机制,越关键的消息我们就将他的消息类型设置的越小,这样我们就可以使用msgtyp < 0来优先处理高优先级的消息。

关于消息队列的阻塞与非阻塞
对于接收来说,阻塞是指消息队列为空或者没有需要的消息类型是,程序停在msgrcv处等待消息的到来。
对于发送来说,阻塞是指消息队列内部容量满了之后,程序停在msgsnd处等待消息队列内部的位置空出。
以消息接收为例(进程p1):

int main() {
    int id = msgget(1, 0666 | IPC_CREAT);
    printf( "id = : %d\n", id );
    struct msgbuf* buf = (struct msgbuf *)malloc(sizeof(struct msgbuf));
    msgrcv(id, buf, 128, 0, 0);
    return 0;
}

对于这段代码,我创建消息队列后直接调用msgrcv接收消息队列,由于此时消息队列为空,因此系统会被阻塞,无法顺利退出,直到其他进程向消息队列内部发送数据。

lol@hyl:~/work/linux_study/msgque$ ./p1
id = : 0
			# 这里没有输出,因为程序一直被阻塞

这时我们尝试使用另一个进程向该进程发送数据(进程p2)

int main() {
    int id = msgget(1, 0666 | IPC_CREAT);
    printf( "id = : %d\n", id );
    struct msgbuf* buf = (struct msgbuf *)malloc(sizeof(struct msgbuf));
    strcpy(buf->data, "hello, world!");
    msgsnd(id, buf, 128, 0);
    return 0;
}

后台运行p1,这里显示p1正在运行,pid为134070

lol@hyl:~/work/linux_study/msgque$ ./p1&
[1] 134070
id = : 0

运行p2,终端输出了[1]进程运行完毕,证明了进程1确实被阻塞,等待消息队列内的消息

lol@hyl:~/work/linux_study/msgque$ ./p1&
[1] 134070
id = : 0
lol@hyl:~/work/linux_study/msgque$ ./p2
id = : 0
hello, world![1]+  Done                    ./p1

当我们将p1的msgrcv更改为:msgrcv(id, buf, 128, 0, IPC_NOWAIT);

int main() {
    int id = msgget(1, 0666 | IPC_CREAT);
    printf( "id = : %d\n", id );
    struct msgbuf* buf = (struct msgbuf *)malloc(sizeof(struct msgbuf));
    msgrcv(id, buf, 128, 0, IPC_NOWAIT);
    printf("%s", buf->data);
    return 0;
}

运行程序可以看到终端直接再次进入了等待模式,证明消息队列没有阻塞。

lol@hyl:~/work/linux_study/msgque$ ./p1
id = : 0
lol@hyl:~/work/linux_study/msgque$ 

控制消息队列

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

这个接口的最常见的用法作用就是用于删除消息队列,当cmd = IPC_RMID时:

msgctl(msgid, IPC_RMID, NULL);//将队列从系统内核中删除

完整代码

proc1.c:进程1阻塞接收数据

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

struct msgbuf {
    int type;
    char data[128];
};

int main() {
    int id = msgget(1, 0666 | IPC_CREAT);
    printf( "id = : %d\n", id );
    struct msgbuf* buf = (struct msgbuf *)malloc(sizeof(struct msgbuf));
    msgrcv(id, buf, 128, 0, 0);     // 阻塞等待
    printf("%s", buf->data);        // 打印数据
    msgctl(id, IPC_RMID, NULL);     // 删除消息队列
    return 0;
}

proc2.c进程2

小结

本节我们讲解了消息队列的用法。以及如何使用消息队列实现进程间通信。

结束语

随着消息队列的诞生,修士们的沟通越来越安全,越来越方便。

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

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

相关文章

从服务器多线程批量下载文件到本地

1、客户端安装 aria2 下载地址&#xff1a;aria2 解压文件&#xff0c;然后将文件目录添加到系统环境变量Path中&#xff0c;然后打开cmd&#xff0c;输入&#xff1a;aria2c 文件地址&#xff0c;就可以下载文件了 2、服务端配置nginx文件服务器 server {listen 8080…

循环神经网络 - 深层循环神经网络

如果将深度定义为网络中信息传递路径长度的话&#xff0c;循环神经网络可以看作既“深”又“浅”的网络。 一方面来说&#xff0c;如果我们把循环网络按时间展开&#xff0c;长时间间隔的状态之间的路径很长&#xff0c;循环网络可以看作一个非常深的网络。 从另一方面来 说&…

linux运维篇-Ubuntu(debian)系操作系统创建源仓库

适用范围 适用于Ubuntu&#xff08;Debian&#xff09;及其衍生版本的linux系统 例如&#xff0c;国产化操作系统kylin-desktop-v10 简介 先来看下我们需要创建出来的仓库目录结构 Deb_conf_test apt源的主目录 conf 配置文件存放目录 conf目录下存放两个配置文件&…

深度学习之微积分

2.4.1 导数和微分 2.4.2 偏导数 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/17227e00adb14472902baba4da675aed.png 2.4.3 梯度 具体证明&#xff0c;矩阵-向量积

20242817李臻《Linux⾼级编程实践》第7周

20242817李臻《Linux⾼级编程实践》第7周 一、AI对学习内容的总结 第八章&#xff1a;多线程编程 8.1 多线程概念 进程与线程的区别&#xff1a; 进程是资源分配单位&#xff0c;拥有独立的地址空间、全局变量、打开的文件等。线程是调度单位&#xff0c;在同一进程内的线程…

浙江大学:DeepSeek如何引领智慧医疗的革新之路?|48页PPT下载方法

导 读INTRODUCTION 随着人工智能技术的飞速发展&#xff0c;DeepSeek等大模型正在引领医疗行业进入一个全新的智慧医疗时代。这些先进的技术不仅正在改变医疗服务的提供方式&#xff0c;还在提高医疗质量和效率方面展现出巨大潜力。 想象一下&#xff0c;当你走进医院&#xff…

Android基础彻底解析-APK入口点,xml,组件,脱壳,逆向

第一章:引言与背景 Android逆向工程,作为一种深入分析Android应用程序的技术,主要目的就是通过分析应用的代码、资源和行为来理解其功能、结构和潜在的安全问题。它不仅仅是对应用进行破解或修改,更重要的是帮助开发者、研究人员和安全人员发现并解决安全隐患。 本文主要对…

ubuntu 2204 安装 vcs 2018

安装评估 系统 : Ubuntu 22.04.1 LTS 磁盘 : ubuntu 自身占用了 9.9G , 按照如下步骤 安装后 , 安装后的软件 占用 13.1G 仓库 : 由于安装 libpng12-0 , 添加了一个仓库 安装包 : 安装了多个包(lsb及其依赖包 libpng12-0)安装步骤 参考 ubuntu2018 安装 vcs2018 安装该…

逆向|中国产业政策大数据平台|请求体加密

2025-04-11 逆向地址:aHR0cDovL3poZW5nY2UuMmIuY24v 打开开发者工具出现debugger,直接注入脚本过掉无限debugger let aaa Function.prototype.constructor; Function.prototype.constructor function (params) { if(params ‘debugger’){ console.log(params); return null…

游戏引擎学习第226天

引言&#xff0c;计划 我们目前的目标是开始构建“元游戏”结构。所谓元游戏&#xff0c;指的是不直接属于核心玩法本身&#xff0c;但又是游戏体验不可或缺的一部分&#xff0c;比如主菜单、标题画面、存档选择、选项设置、过场动画等。我们正在慢慢将这些系统结构搭建起来。…

Notepad++安装Markdown实时预览插件

具体操作 打开notepad -> 插件 -> 插件管理 -> 可用 -> “Markdown Panel” -> 安装&#xff0c;安装完成后工具栏点击"Markdown Panel"按钮。 注意&#xff1a;由于网络等原因可能安装失败 导致工具栏没出现""Markdown Panel"按钮&am…

Mysql-视图和存储过程

视图 1.介绍 视图(View)是一种虚拟存在的表。视图中的数据并不在数据库中实际存在,行和列数据来自定义视图的查询中使用的表,并且是在使用视图时动态生成的。 通俗的讲,视图只保存了查询的SQL逻辑,不保存查询结果。所以我们在创建视图的时候,主要的工作就落在创建这条…

stm32面试

数据结构相关问题 stm32面试 数据结构相关问题 目录基础数据结构树与图排序与查找算法 Linux相关问题Linux系统基础Linux命令与脚本Linux网络与服务 操作系统相关问题操作系统基础概念操作系统调度算法操作系统同步与通信 STM32相关问题STM32硬件基础STM32编程与开发STM32应用与…

202524 | 分布式事务

分布式事务&#xff08;Distributed Transaction&#xff09; 分布式事务是指跨多个数据库、服务或系统节点的事务操作&#xff0c;要求所有参与方要么全部成功提交&#xff0c;要么全部回滚&#xff0c;保证数据一致性。 1. 为什么需要分布式事务&#xff1f; 在单体应用中&…

在 macOS 上修改 最大文件描述符限制(Too many open files) 和 网络端口相关参数 需要调整系统级配置的详细步骤

在 macOS 上修改 最大文件描述符限制&#xff08;Too many open files&#xff09; 和 网络端口相关参数 需要调整系统级配置。以下是详细步骤&#xff1a; 在 macOS 上修改 最大文件描述符限制&#xff08;Too many open files&#xff09; 和 网络端口相关参数 需要调整系统级…

通过Arduino IDE向闪存文件系统上传文件

注意&#xff1a;适用于Arduino IDE 2.0版本以上。对于Arduino IDE版本在2.0以下的请参考太极创客的教程&#xff1a;http://www.taichi-maker.com/homepage/esp8266-nodemcu-iot/iot-c/spiffs/upload-files/。 1. 下载脚本文件 下载地址&#xff1a;https://github.com/earl…

leetcode 121. Best Time to Buy and Sell Stock

题目描述 本题属于动态规划类问题。 dp数组的含义 dp[i][0]表示从第0天到第i天为止&#xff0c;处于持有股票的状态下&#xff0c;账户里的最大金额。 dp[i][1]表示从第0天到第i天为止&#xff0c;处于不持有股票的状态下&#xff0c;账户里的最大金额。 按照这个定义dp[n-…

【Docker-13】Docker Container容器

Docker Container&#xff08;容器&#xff09; 一、什么是容器&#xff1f; 通俗地讲&#xff0c;容器是镜像的运行实体。镜像是静态的只读文件&#xff0c;而容器带有运行时需要的可写文件层&#xff0c;并且容器中的进程属于运行状态。即容器运行着真正的应用进程。容器有…

蓝宝石狼组织升级攻击工具包,利用新型紫水晶窃密软件瞄准能源企业

网络安全专家发现&#xff0c;被称为"蓝宝石狼"&#xff08;Sapphire Werewolf&#xff09;的威胁组织正在使用升级版"紫水晶"&#xff08;Amethyst&#xff09;窃密软件&#xff0c;对能源行业企业发起复杂攻击活动。此次攻击标志着该组织能力显著提升&am…

关于我的服务器

最近我买了台腾讯云服务器&#xff0c;然后新手小白只会用宝塔。。。 安装完之后默认的端口是8888&#xff0c;打开面板就会提示我有风险。然后 我改了端口之后&#xff0c;怎么都打不开。 于是 学到了几句命令可以使用&#xff1a; //查看端口是否已经修改成功 cat www/se…