【Linux】Linux进程间通信

server/2024/10/25 7:45:21/

1.进程间通信

进程之间具有独立性,进程间如果要发生通信,就需要打破这种独立性。进程间通信必定需要一块公共的区域来作为信息的存放点,操作系统需要直接或间接给通信进程双方提供内存空间,例如这块内存空间就是文件系统提供的,那么就是管道通信,通信的本质就是让不同的进程看到同一份内存空间。

进程间通信的目的

  • 数据传输:一个进程需要将它的数据发送给另一个进程
  • 资源共享:多个进程之间共享同样的资源。
  • 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。
  • 进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。

进程间通信的发展

  • 管道
  • System V进程间通信
  • POSIX进程间通信

进程间通信的方式

管道

匿名管道pipe

命名管道

System V IPC

System V 消息队列

System V 共享内存

System V 信号量

POSIX IPC

消息队列

共享内存

信号量

互斥量

条件变量

读写锁

管道

什么是管道?

管道是Unix中最古老的进程间通信的形式。

我们把从一个进程连接到另一个进程的一个数据流成为一个“管道”。

匿名管道

#include <unistd.h>
功能 : 创建一无名管道
原型 
int pipe(int fd[2]);
参数
fd:文件描述符数组, 其中fd[0] 表示读端,fd[1] 表示写端返回值 : 成功返回0,失败返回错误代码

实例代码:

/**** @Author: jyx* @Date: 2024-10-23 14:47:21* @LastEditors: jyx* @Description:管道和进程配合的使用*/
#include <unistd.h>
#include <iostream>
#include <string.h>
#include <stdlib.h>
using namespace std;
int main()
{int fd[2];int ret = pipe(fd);if (ret == -1){std::cout << "管道创建失败!" << endl;}cout << "fd[0]:" << fd[0] << endl<< "fd[1]:" << fd[1] << endl;pid_t pid = fork();if (pid < 0){cout << "子进程创建失败!" << endl;exit(1);}// 子进程来读else if (pid == 0){close(fd[1]);char buf[1024];int len = strlen(buf);read(fd[0], buf, sizeof(buf));cout << "子进程读:" << buf << endl;}// 父进程来写else if (pid > 0){close(fd[0]);char str[1024];fgets(str, 1024, stdin);write(fd[1], str, strlen(str));}return 0;
}

用fork来共享管道原理

站在文件描述符角度-深度理解管道

站在内核角度-管道本质

所以,看待管道,就如同看待文件一样!管道的使用和文件一致,迎合了“Linux一切皆文件思想”。

管道读写规则

当没有数据可读时

O_NONBLOCK disable read 调用阻塞,即进程暂停执行,一直等到有数据来到为止。
O_NONBLOCK enable read 调用返回 -1 errno 值为 EAGAIN
当管道满的时候
O_NONBLOCK disable write 调用阻塞,直到有进程读走数据
O_NONBLOCK enable :调用返回 -1 errno 值为 EAGAIN
如果所有管道写端对应的文件描述符被关闭,则 read 返回 0
如果所有管道读端对应的文件描述符被关闭,则 write 操作会产生信号 SIGPIPE, 进而可能导致 write 进程退出
当要写入的数据量不大于 PIPE_BUF 时, linux 将保证写入的原子性。
当要写入的数据量大于 PIPE_BUF 时, linux 将不再保证写入的原子性。

管道特点

  • 只能用于具有共同祖先的进程(具有亲缘关系的进程)之间进行通信;通常,一个管道由一个进程创建,然后该进程调用fork,此后父、子进程之间就可应用该管道。
  • 管道提供流式服务
  • 一般而言,进程退出,管道释放,所以管道的生命周期随进程
  • 一般而言,内核会对管道操作进行同步与互斥
  • 管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道

命名管道

概念

管道应用的一个限制就是只能在具有共同祖先(具有亲缘关系)的进程间通信。
如果我们想在不相关的进程之间交换数据,可以使用 FIFO 文件来做这项工作,它经常被称为命名管道。
命名管道是一种特殊类型的文件

创建命名管道

命名管道可以从命令行上创建,命令行方式:

​​​​​​​$ mkfifo filename

命名管道也可以从程序里创建:

int mkfifo(const char *filename, mode_t mode);

创建命名管道:

int main()
{mkfifo("p2", 0644);return 0;
}

匿名管道和命名管道的区别

匿名管道由 pipe 函数创建并打开。
命名管道由 mkfififo 函数创建,打开用 open
FIFO (命名管道)与 pipe (匿名管道)之间唯一的区别在它们创建与打开的方式不同,一但这些工作完成之后,它们具有相同的语义。

命名管道打开规则

如果当前打开操作是为读而打开 FIFO
        O_NONBLOCK disable:阻塞直到有相应进程为写而打开该 FIFO
        O_NONBLOCK enable:立刻返回成功
如果当前打开操作是为写而打开 FIFO
        O_NONBLOCK disable:阻塞直到有相应进程为读而打开该 FIFO
        O_NONBLOCK enable:立刻返回失败,错误码为 ENXIO

System V 共享内存

共享内存区是最快的 IPC 形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据。

原理:

  1. 首先在物理内存当中创建了一块内存
  2. 不同的进程通过页表映射, 将同一块物理内存映射到自己的虚拟地址空间
  3. 不同的进程, 操作进程虚拟地址, 通过页表的映射, 就相当于操作同一块内存,从而完成了数据交换

共享内存函数

shmget函数

int shmget(key_t key, size_t size, int shmflg);

功能:用来创建共享内存

参数
        key:这个共享内存段名字
        size:共享内存大小
        shmflg:由九个权限标志构成,它们的用法和创建文件时使用的 mode 模式标志是一样的
返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回-1

shmat函数

void *shmat(int shmid, const void *shmaddr, int shmflg);

功能:将共享内存段连接到进程地址空间

参数
        shmid: 共享内存标识
        shmaddr:指定连接的地址
        shmflg:它的两个可能取值是 SHM_RND SHM_RDONLY
返回值:成功返回一个指针,指向共享内存第一个节;失败返回 -1
说明:
shmaddr NULL ,核心自动选择一个地址
shmaddr 不为 NULL shmflg SHM_RND 标记,则以 shmaddr 为连接地址。
shmaddr 不为 NULL shmflg 设置了 SHM_RND 标记,则连接的地址会自动向下调整为 SHMLBA 的整数倍。公式: shmaddr -
(shmaddr % SHMLBA)
shmflg=SHM_RDONLY ,表示连接操作用来只读共享内存

shmdt函数

itn shmdt(const void *shmaddr);

功能:将共享内存段与当前进程脱离

参数

        shmaddr: 由 shmat 所返回的指针
返回值:成功返回 0 ;失败返回 -1
注意:将共享内存段与当前进程脱离不等于删除共享内存段

shmctl函数

int shmctl(int shmid, int cmd, struct shmid_ds *buf);
功能:用于控制共享内存
参数
        shmid:由 shmget 返回的共享内存标识码
        cmd:将要采取的动作(有三个可取值)
        buf:指向一个保存着共享内存的模式状态和访问权限的数据结构
返回值:成功返回 0 ;失败返回 -1

System V 消息队列

消息队列提供了一个从一个进程向另外一个进程发送一块数据的方法
每个数据块都被认为是有一个类型,接收者进程接收的数据块可以有不同的类型值
特性方面
IPC 资源必须删除,否则不会自动清除,除非重启,所以 system V IPC 资源的生命周期随内核

System V 信号量

信号量主要用于同步和互斥的,下面先来看看什么是同步和互斥。

由于各进程要求共享资源,而且有些资源需要互斥使用,因此各进程间竞争使用这些资源,进程的这种关系为进程的互斥
系统中某些资源一次只允许一个进程使用,称这样的资源为临界资源或互斥资源。
在进程中涉及到互斥资源的程序段叫临界区
特性方面
IPC 资源必须删除,否则不会自动清除,除非重启,所以 system V IPC 资源的生命周期随内核

http://www.ppmy.cn/server/134646.html

相关文章

【 xiangshan分支预测单元结构及时序介绍】

xiangshan分支预测单元结构及时序介绍 1.1 子预测器接口信号1 in2 out3 流水线控制信号4 update更新请求5 redirect重定向请求 1.2 预测器接口连接1.3 分支预测单元时序介绍1.3.1 一周期无空泡预测 1.3.2 预测结果重定向1.4 相关使能信号 本节主要介绍xiangshan BPU内部是如何整…

Django+Vue全栈开发项目入门(一)

Vue项目搭建过程 1、使用脚手架工具搭建项目 2、准备静态资源 3、调整生成项目结构 使用脚手架工具搭建项目 网络请求库axios Axios是一个基于Promise的HTTP库&#xff0c;适用于浏览器和node.js环境&#xff0c;用于发送网络请求。 特点 跨平台性&#xff1a;Axios既可…

简单工厂(Simple Factory)

简单工厂&#xff08;Simple Factory&#xff09; 在创建一个对象时不向客户暴露内部细节&#xff0c;并提供一个创建对象的通用接口。 说明&#xff1a; 简单工厂把实例化的操作单独放到一个类中&#xff0c;这个类就成为简单工厂类&#xff0c;让简单工厂类来决定应该用哪…

网络爬虫中的几种数据存储方式(中篇)

在上一篇文章中&#xff0c;带大家了解了文本的存储方式和json的存储方式&#xff0c;而这篇文章是要大家掌握其余的数据存储方式。 CSV存储文件存储 CSV&#xff0c;全称为 Comma-Separated Values&#xff0c;中文可以叫作逗号分隔值或字符分隔值&#xff0c;其文件以纯文本…

解决后端给前端的返回数据过大的问题(压缩)

解决后端给前端的返回数据过大的问题&#xff08;压缩&#xff09; 问题 后端返回数据过大导致的前端接收数据慢的问题 方案 注解方式压缩后端给前端的返回数据&#xff0c;注解Gzip压缩后端返回数据&#xff08;前端不用做任何处理&#xff09; import com.fasterxml.jack…

基于ElementPlus的Form组件封装

前言 我们在项目开发过程中遇到最多就是表单页面的开发&#xff0c;那么使用频率比较高的就是Form组件&#xff0c;无论是vue亦或者是react&#xff0c;我们在项目中使用到UI库都会有Form组件。多数情况下都是用到了Form组件&#xff0c;我们先根据UI库或者其他类似的页面直接…

自动驾驶系列—图像数据在自动驾驶中的关键角色及其实际应用场景探讨

&#x1f31f;&#x1f31f; 欢迎来到我的技术小筑&#xff0c;一个专为技术探索者打造的交流空间。在这里&#xff0c;我们不仅分享代码的智慧&#xff0c;还探讨技术的深度与广度。无论您是资深开发者还是技术新手&#xff0c;这里都有一片属于您的天空。让我们在知识的海洋中…

微信小程序美团点餐

引言&#xff1a;外卖已经成为了都市人的必备&#xff0c;在无数个来不及&#xff08;懒得&#xff09;做饭的时刻拯救孤单寂寞的胃。美团外卖无疑是外卖届的领头羊&#xff0c;它的很多功能与设计都值得我们学习。本文将从五个方面&#xff0c;对美团外卖展开产品分析&#xf…