进程间通信的方式及原理

article/2025/10/9 13:15:31

# 进程间通信的方式

文章目录

  • # 进程间通信的方式
    • 消息队列
      • 使用步骤

管道

消息队列

信号

信号量

socket

消息队列

首先消息队列就是内核维护的一块链表区域,只要是有足够权限的进程都可以向队列中添加消息,只要是有读权限的进程都可以在里面拿出消息

  • 克服了信号承载信息少,以及管道只能承载无格式字节流以及有限缓冲区大小的缺点
  • 消息队列是随着内核可持续的
  • 消息队列信息的传递是基于拷贝的,所以效率低下
  • 消息队列只有内核重启或者人工删除的时候才会消失,所以不用的时候请关闭

进程通过key可以获取或者创建(系统维护)一个msqid_ds结构体。然后操作系统会返回key对应的一个id。后续进程通过id对msqid_ds进行存取。msqid_ds是表示一个消息队列的管理者。各个进程使用一个msqid_ds进行通信。操作系统会有权限控制,大小控制等。

每个消息由两部分组成,分别是消息编号(消息类型)和消息正文。
1)消息编号:识别消息用
2)消息正文:真正的信息内容

参考链接

消息队列收发数据

发送消息

①进程先封装一个消息包

这个消息包其实就是如下类型的一个结构体变量,封包时将消息编号和消息正文
写到结构体的成员中。

struct msgbuf
{long mtype;             /* 放消息编号,必须> 0 */char mtext[msgsz]; /* 消息内容(消息正文) */
}; 

②调用相应的API发送消息

调用API时通过“消息队列的标识符”找到对应的消息队列,然后将消息包发送给消息队列,消息包(存放消息的结构体变量)会被作为一个链表节点插入链表。

接收消息

调用API接收消息时,必须传递两个重要的信息

(a)消息队列标识符
(b)你要接收消息的编号

有了这两个信息,API就可以找到对应的消息队列,然后从消息队列中取出你所要编号的消息,如此就收到了别人所发送的信息。

消息队列使用步骤

①使用msgget函数创建新的消息队列、或者获取已存在的某个消息队列,并返回唯一标识消息队列的标识符(msqID),后续收发消息就是使用这个标识符来实现的。

②收发消息

发送消息:使用msgsnd函数,利用消息队列标识符发送某编号的消息
接收消息:使用msgrcv函数,利用消息队列标识符接收某编号的消息

③使用msgctl函数,利用消息队列标识符删除消息队列

对于使用消息队列来通信的多个进程来说,只需要一个进程来创建消息队列就可以了,对于其它要参与通信的进程来说,直接使用这个创建好的消息队列即可。为了保证消息队列的创建,最好是让每一个进程都包含创建消息队列的代码,谁先运行就由谁创建,后运行的进程如果发现它想用的那个消息队列已经创建好了,就直接使用,当众多进程共享操作同一个消息队列时,即可实现进程间的通信。

API

msgget

函数原型

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>                  
int msgget(key_t key, int msgflg); 

功能

利用key值创建、或者获取一个消息队列。key值也能够唯一的标识消息队列。

如果key没有对应任何消息队列,那就创建一个新的消息队列

如果key已经对应了某个消息队列,说明你要的消息队列已经存在了,那就获取这个消息队列来使用

参数

key值:

用于为消息队列生成(计算出)唯一的消息队列ID。

我们可以指定三种形式的key值:

①指定为IPC_PRIVATE宏,指定这个宏后,每次调用msgget时都会创建一个新的消息队列。如果你每次使用的必须是新消息队列的话,就可以指定这个,不过这个用的很少。因为一般来说,只要有一个消息队列可以用来通信就可以了,并不需要每次都创建一个全新的消息队列。

②可以自己指定一个整形数,但是容易重复指定。本来我想创建一个新的消息队列,结果我所指定的这个整形数,之前就已经被用于创建某个消息队列了,当我的指定重复时,msgget就不会创建新消息队列,而是使用的是别人之前就创建好的消息队列。很少使用这种方式

③使用ftok函数来生成key

#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);

ftok通过指定路径名和一个整形数,就可以计算并返回一个唯一对应的key值,只要路径名和整形数不变,所对应的key值就唯一不变的。不过由于ftok只会使用整形数(proj_id)的低8位,因此我们往往会指定为一个ASCII码值,因为ASCII码值刚好是8位的整形数。

msgflg

指定创建时的原始权限,比如0664

创建一个新的消息队列时,除了原始权限,还需要指定IPC_CREAT选项。

msgid = msgget(key, 0664|IPC_CREAT);

如果key值没有对应任何消息队列,就会创建一个新的消息队列,此时就会用到msgflg参数,但是如果key已经对应了某个早已存在消息队列,就直接返回这个已存在消息队列的ID(标识符),此时不会用到msgflg参数。

返回值

成功:返回消息队列标识符(消息队列的ID)对于每一个创建好的消息队列来说,ID是固定的。

失败:失败返回-1,并设置error

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:消息队列的标识符。

msgp:存放消息的缓存的地址,类型struct msgbuf类型。这个缓存就是一个消息包(存放消息的结构体变量)。

struct msgbuf
{long mtype;         /* 放消息编号,必须 > 0 */char mtext[msgsz];  /* 消息内容(消息正文) */
}; 

msgsz:消息正文大大小。

msgflg

0:阻塞发送消息。也就是说,如果没有发送成功的话,该函数会一直阻塞等,直到发送成功为止。

IPC_NOWAIT:非阻塞方式发送消息,不管发送成功与否,函数都将返回也就是说,发送不成功的的话,函数不会阻塞。

返回值

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

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:消息队列的标识符。

msgp:缓存地址,缓存用于存放所接收的消息。类型还是struct msgbuf,同上

msgsz:消息正文的大小

msgtyp:你要接收消息的编号

int msgflg:

0:阻塞接收消息。也就是说如果没有消息时,阻塞接收(msgrcv函数会休眠)。

IPC_NOWAIT:非阻塞接收消息。也就是说没有消息时,该函数不阻塞。他会因为读取失败而错误返回

返回值

成功:返回消息正文的字节数。失败:返回-1,errno被设置

msgctl

函数原型

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>                            
int msgctl(int msqid, int cmd, struct msqid_ds *buf); 

*功能*

根据cmd指定的要求,去控制消息队列。可以有那些控制?

获取消息队列的属性信息

修改消息队列的属性信息

删除消息队列

等等

我们调用msgctl函数的最常见目的就是删除消息队列,事实上,删除消息队列只是各种消息队列控制中的一种。

*参数*

msqid:消息队列标识符

cmd:控制选项,其实cmd有很多选项,我这里只简单介绍三个

①IPC_STAT:将msqid消息队列的属性信息,读到第三个参数所指定的缓存。

②IPC_SET:使用第三个参数中的新设置去修改消息队列的属性

+ 定一个struct msqid_ds buf。
+ 将新的属性信息设置到buf中
+ cmd指定为IPC_SET后,msgctl函数就会使用buf中的新属性去修改消息队列原有的属性。

③IPC_RMID:删除消息队列.删除消息队列时,用不到第三个参数,用不到时设置为NULL。

buf:存放属性信息

有的时候需要给第三个参数,有时不需要,取决于cmd的设置。buf的类型为struct msqid_ds。

结构体中的成员都是用来存放消息队列的属性信息的。

struct msqid_ds 
{struct ipc_perm  msg_perm; /* 消息队列的读写权限和所有者 */time_t  msg_stime;             /* 最后一次向队列发送消息的时间*/time_t  msg_rtime;             /* 最后一次从消息队列接收消息的时间 */time_t  msg_ctime;             /* 消息队列属性最后一次被修改的时间 */unsigned  long __msg_cbytes; /* 队列中当前所有消息总的字节数 */msgqnum_t  msg_qnum;     /* 队列中当前消息的条数*/msglen_t msg_qbytes;        /* 队列中允许的最大的总的字节数 */pid_t  msg_lspid;                /* 最后一次向队列发送消息的进程PID */pid_t  msg_lrpid;                 /* 最后一次从队列接受消息的进程PID */
};
struct ipc_perm 
{key_t          __key;       /* Key supplied to msgget(2):消息队列的key值 */uid_t          uid;         /* UID of owner :当前这一刻正在使用消息队列的用户 */gid_t          gid;         /* GID of owner :正在使用的用户所在用户组 */uid_t          cuid;        /* UID of creator :创建消息队列的用户 */gid_t          cgid;        /* GID of creator :创建消息队列的用户所在用户组*/unsigned short mode;        /* Permissions:读写权限(比如0664) */unsigned short __seq;       /* Sequence number :序列号,保障消息队列ID不被立即重复使用 */
};

*返回值*

成功:返回0。失败:返回-1,errno被设置

使用步骤

1. 通过ftok(路径,整形)来生成一个唯一对应的key
2. msgget(key,flag)来创建或者获取一个消息队列,flag是指定权限用的
3. msgsnd(msqid,msgp,msgz,msgflag) 发送数据
4. msgrcv(msqidmnsgpmnsgz,msgtype,msgflag) 接收消息, flag可以设置阻塞和非阻塞接收

http://chatgpt.dhexx.cn/article/slhaFACd.shtml

相关文章

C | 进程间通信的方式

C | 进程间通信的方式 1.无名管道 无名管道是实现亲缘间进程通信的一种方式&#xff0c;属于半双工通信。 无名管道的实现是队列&#xff0c;不能使用lseek对读写指针偏移。 无名管道有两个端&#xff1a;数据流入端和数据流出端&#xff0c;也叫写端和读端。它们是两个固定…

面试题:进程间通信的方式

liunx六大进程间通信方式 管道&#xff0c;消息队列&#xff0c;共享内存&#xff0c;信号量&#xff0c;socket&#xff0c;信号&#xff0c;文件锁 1&#xff0c;管道 1&#xff0c;匿名管道&#xff1a; 概念&#xff1a;在内核中申请一块固定大小的缓冲区&…

进程间通信的几种方式浅谈(上)

程序员必须让拥有依赖关系的进程集协调&#xff0c;这样才能达到进程的共同目标。可以使用两种技术来达到协调。第一种技术在具有通信依赖关系的两个进程间传递信息。这种技术称做进程间通信&#xff08;interprocess communication&#xff09;。第二种技术是同步&#xff0c;…

进程间通信的几种方式

一、管道 在Linux 中&#xff0c;管道是一种使用非常频繁的通信机制。从本质上说&#xff0c;管道也是一种文件&#xff0c;但它又和一般的文件有所不同&#xff0c;管道可以克服使用文件进行通信的两个问题&#xff0c;具体表现如下所述。 • 限制管道的大小。实际上&#x…

【进程间通信】进程间通信方式汇总

个人主页&#xff1a;董哥聊技术 我是董哥&#xff0c;嵌入式领域新星创作者 创作理念&#xff1a;专注分享高质量嵌入式文章&#xff0c;让大家读有所得&#xff01; 文章目录 1、管道模型1.1 匿名管道1.2 命名管道 2、消息队列2.1 创建消息队列2.2 发送消息2.3 接收消息 3、共…

android中进程间通信的几种方式

进程间通信&#xff08;IPC&#xff09;方式 使用Bundle使用文件共享使用Messenger使用AIDL使用COntentProvider使用Socket 一、使用Bundle 我们都知道Android中三大组件Activity&#xff0c;Service&#xff0c;Receiver都支持在Intent中传递Bundle数据&#xff0c;而Bundle…

操作系统——进程间通信

文章目录 其他文章管道消息队列共享内存信号量信号Socket总结 个人博客网站&#xff1a; https://xingkongdiyiren.github.io/myblog/,完整的Java知识体系&#xff0c;包括408&#xff0c;架构&#xff0c;业务&#xff0c;产品&#xff0c;软技能等 其他文章 操作系统——概…

进程间的通信方式(六种)

进程之间的通信 参考文章&#xff1a;https://blog.csdn.net/qq_34827674/article/details/107678226 前提知识&#xff1a;每个进程都有自己的用户空间&#xff0c;而内核空间是每个进程共享的。因此进程之间想要进行通信&#xff0c;就需要通过内核来实现。 管道&#xff1…

【操作系统】进程间通信的五种方式

引言1.进程对白&#xff1a;管道、记名管道、套接字1.管道2.虫洞&#xff1a;套接字3.信号 4.信号旗语&#xff1a;信号量5.进程拥抱&#xff1a;共享内存 引言 进程作为人类的发明&#xff0c;自然免不了脱离人类的习性&#xff0c;也有通信需求。如果进程之间不进行任何通信…

进程之间的通信方式

进程之间的通信方式包括管道&#xff0c;消息队列&#xff0c;共享内存&#xff0c;信号&#xff0c;信号量&#xff0c;socket六种方式&#xff0c;下面来对这6种方式分别进行介绍。 一、管道 管道的结构示意图如上所示&#xff0c;管道包含一个输入端和一个输出端&#xff0…

进程间通信的六种常见方式

目录 进程间通信&#xff08;IPC&#xff09;&#xff1a; 一、管道 二、FIFO 三、消息队列 四、共享内存 五、信号 六、信号量 七、进程间通信方式总结&#xff1a; 进程间通信&#xff08;IPC&#xff09;&#xff1a; 进程间通信的方式有很多&#xff0c;这里主要…

idea数据库管理工具配置连接数据库

idea数据库管理工具配置连接数据库 —————————————————————————————————————————————————————— 在cmd中操作数据库太麻烦了&#xff0c;还好idea为我们提供了很方便的数据库管理工具&#xff0c;下面看看如何用idea连接…

idea连接数据库失败解决办法

一.IDEA连接Mysql报错&#xff1a; 未找到驱动程序类 ‘com.mysql.cj.jdbc.Driver‘.  Change driver class 报错详细内容&#xff1a;未找到驱动程序类 ‘com.mysql.cj.jdbc.Driver’. Change driver class 报错原因&#xff1a;Mysql版本为5.0&#xff0c;找不到com.mysql.…

IDEA中如何连接数据库并显示数据库信息。

我的相关博客&#xff1a; java代码程序中连接mysql数据库的方法及代码 mysql数据库并发上锁问题&#xff0c;java代码 关于IDEA中怎么连接mysql数据库 相信部分朋友在使用IDEA操作数据库的时候会出现有关数据库信息的报错。 显示没有此表&#xff0c;或者无数据库等错误信息…

idea连接数据库失败原因及解决方案

这是因为安装mysql的时候时区设置的不正确 mysql默认的是美国的时区&#xff0c;而我们中国大陆要比他们迟8小时&#xff0c;采用8:00格式。使用的数据库是MySQL&#xff0c;在你没有指定MySQL驱动版本的情况下它自动依赖的驱动是8.0.12很高的版本&#xff0c;这是由于数据库和…

IDEA如何连接数据库 / IDEA连接数据库 新手,图解

如果在下面操作中遇到了问题&#xff0c;可以查看我的这篇笔记 https://blog.csdn.net/qq_44627608/article/details/115442815 1. 打开IDEA的数据库设置 2. 新建数据库链接 左上角的便是你链接的数据库了&#xff0c;第一次打开时左上角应该是空的&#xff0c;需要你从下面的…

IDEA使用Database连接数据库

一&#xff0c;连接数据库 1.点击右侧Database后,点击左上角按钮&#xff0c;然后选中Data Source &#xff0c;无论使用的是MariaDB还是MySQL都选中MySQL 2.点击非常隐蔽的 … 按钮 3.选中你需要使用的数据库 4.填写数据的账号&#xff0c;密码&#xff0c;数据库名称&…

idea代码连接mysql数据库操作

此文章仅为作者学习上的问题记录&#xff0c;如有错误&#xff0c;欢迎指正。 首先是准备工作 先创建一个Module 之后在此Module下创建一个lib包 然后将下载的连接包复制到lib包下&#xff0c;连接包下载地址&#xff1a; https://cdn.mysql.com//Downloads/Connector-J/mysq…

IDEA中配置数据库连接

1.点击IDEA右边框的 Database &#xff0c;在展开的界面点击 选择 Data Source &#xff0c;再选择 MySQL 2.在弹出的界面进行基本信息的填写 3.填写完后&#xff0c;点击Test Connection 测试一下 这样就是填写的没问题&#xff0c;如果是第一次点击这个&#xff0c;需要下载…

Idea连接MySQL数据库教程 (简单明了)

使用Idea连接数据库 具体步骤&#xff1a;点击右侧DataBase → 点击号 → 点击Data Source 选择MySQL → 输入用户名、密码、连接的数据库名称&#xff08;连接路径会自动生成&#xff09; → 可点击下面的Test Connection来测试连接 注意事项一&#xff1a; 第一次连接需要下…