进程间通信 | 匿名管道 | 命名管道 | 共享内存 | 消息队列 | 信号 | Socket | 代码实现

1.进程间通信

如果一个系统是单进程的,那就无法使用并发能力。进程间通信是实现多进程协同的手段,目的是完成进程间的数据传输、消息通知、同步执行流等。

进程具有独立性! 虚拟空间 + 页表的方式保证了进程的独立性。进程间通信的本质,前提是要进程看到同一块"内存",这个"内存"不隶属那个进程,是进程间共享的!

每个进程的用户地址空间都是独立的,一般而言是不能互相访问的,但内核空间是每个进程都共享的,所以进程之间要通信必须通过内核。

在这里插入图片描述

2.管道
$ ps axj | grep mysql

Linux指令中「|」竖线就是一个管道,它的功能是将前一个命令(ps auxf)的输出,作为后一个命令(grep mysql)的输入。可以看出管道传输数据是单向的,即半双工

从一个进程连接到另一个进程的一个数据流称为一个"管道",它是Unix中最古老的进程间通信的形式。管道分为匿名管道 和 命名管道

2.1.匿名管道

完整代码链接

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

在这里插入图片描述

站在内核角度-管道本质

在这里插入图片描述

子进程 fork 来复制父进程 fd 文件描述符,让父子进程通过文件看到同一块空间。将资源(数据)放到内存中(缓冲区中)没有必要将数据写到磁盘中,这样能提高效率。

管道读写规则

  1. 写端快读端慢:管道写满不能再写
  2. 写端慢读端快:管道没有数据时,读端必须等待
  3. 写端关闭读端read返回0,标识读到文件结尾
  4. 读端关闭写端继续写,操作系统会终止写端进程

管道特点

  1. 管道只能用于具有共同祖先的进程(具有亲缘关系的进程)之间进行通信;通常用于父子进程通信
  2. 管道通过控制读写端的速度,提供了访问控制
  3. 管道提供的是面向流式的通信服务,即管道是面向字节流的
  4. 管道是基于文件的,而文件的生命周期随进程,所以管道的生命周期随进程的
  5. 管道是单向通信的,是半双工的特殊情况
2.2.命名管道

对于匿名管道,它的通信是具有血缘关系进程间通信。通常通过 fork 来复制父进程 fd 文件描述符,来达到通信的目的。而对于命名管道,它可以在不相关的进程间也能相互通信。因为命令管道,提前创建了一个类型为管道的设备文件,在进程里只要使用这个设备文件,就可以相互通信。

不管是匿名管道还是命名管道,进程写入的数据都是缓存在内核中,另一个进程读取数据时候自然也是从内核中获取,同时通信数据都遵循先进先出原则。

在命令行中使用:

使用命令mkfifo命名管道:

xiyan@VM-20-5-ubuntu:~/code/namePipe$ mkfifo fifo.ipc

在这里插入图片描述

代码中使用:

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

定义一个路径,服务端创建命名管道,并以读的方式打开对应的路径文件,而客户端以写的方式和服务器打开相同路径下的文件(即:命名管道)

完整代码链接

3.共享内存

完整代码实现

在这里插入图片描述

操作系统在物理地址创建一块共享内存,进程通过页表建立映射,将共享内存的地址映射到自己虚拟地址空间的共享区中,然后通过地址加偏移量的方式来访问同一块内存。

如果多对进程创建了多个共享内存,操作系统需要管理共享内存,如果重新理解共享内存 = 共享内存块 + 对于共享内存的内核数据结构

共享内存的方式,进程能从同一块空间访问,没有拷贝,是进程通信中速度最快!但是缺乏访问控制,但是可以通过加上管道的方式进行控制。

3.1.使用系统调用创建共享内存

基本步骤:

在这里插入图片描述

系统调用

创建公共key值

key_t ftok(const char *pathname, int proj_id);

shmServer端和shmClient端传入的参数要是一致的!

ftok会根据pathname的inode和proj_id生成一个唯一的数,这和hash的原理或者是MD5的加密算法原理是相同的。

创建共享内存

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

参数说明:

key :内核中共享内存的标识符(内核层),多个进程通过相同的标识符才能打开同一个共享内存,我们通过ftok函数获取。

size:共享内存的大小,以内存页(4096byte)为单位的整数倍进行分配;

shmflg:对于shmServer端传入的参数是**(IPC_CREAT | IPC_EXCL | 0666),IPC_CREAT单独表示如果共享内存存在,获取返回,如果不存在创建返回;IPC_CREAT | IPC_EXCL的整体表示如果共享内存存在,出错返回,如果不存在创建返回。对于0666表示设置的文件权限。对于shmClient端传入的参数(key,SHM_SIZE,0)**

返回值:

成功返回一个非负整数,即该共享内存段的标识符(用户层),类似文件标识符fd;失败返回-1

建立连接

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

参数说明:

shmid:共享内存标识 ,即:shmget函数的正确的返回值

shmaddr:指定连接的地址

shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY 。

返回值:

返回值:返回共享内存映射在虚拟地址空间的首地址,可以使用首地址进行内存的操作,这就类似malloc函数的操作。 失败返回-1

断开连接

int shmdt(const void *shmaddr);

参数说明:

shmaddr:映射在虚拟地址空间的首地址,即shmat的正确返回值。

删除共享内存

int shmctl(int shmid, int cmd, struct shmid_ds *buf);

参数说明:

参数 shmid:由shmget返回的共享内存标识码

cmd:将要采取的动作,传入 IPC_RMID表示要删除共享内存段

buf:指向一个保存着共享内存的模式状态和访问权限的数据结构

返回值:成功返回0;失败返回-1

3.2.Linux操作进程通信资源命令
xiyan@VM-20-5-ubuntu:~/code/shm$ ./server
[Info][2024-3-19 10:18:43] create ftok success : key is 0x:66020028
Segmentation fault (core dumped)

查看共享内存:
`ipcs` 查看进程间通信资源
	`-m` 查看共享内存
	`-q` 查看消息队列
	`-s` 查看信号量
xiyan@VM-20-5-ubuntu:~/code/shm$ ipcs -m
删除
ipcrm -m/q/s shmid
xiyan@VM-20-5-ubuntu:~/code/shm$ ipcrm -m shmid

总结:用了共享内存通信方式,带来新的问题,那就是如果多个进程同时修改同一个共享内存,很有可能就冲突了。这时候要可以用到信号量的PV操作,保证原子性。多进程版的PV操作用得少 。

4.消息队列

管道的通信方式是效率低的,因此管道不适合进程间频繁地交换数据。

通信的进程将数据放到消息队列。如:A 进程要给 B 进程发送消息,A 进程把数据放在对应的消息队列后就可以正常返回了,B 进程需要的时候再去读取数据就可以了。而所谓的消息队列是保存在内核中的消息链表。消息队列的生命周期随内核,需要释放。

消息队列不适合比较大数据的传输,因为在内核中每个消息体都有一个最大长度的限制,同时所有队列所包含的全部消息体的总长度也是有上限。在 Linux 内核中,会有两个宏定义 MSGMAXMSGMNB,它们以字节为单位,分别定义了一条消息的最大长度和一个队列的最大长度。

消息队列通信过程中,存在用户态与内核态之间的数据拷贝开销,因为进程写入数据到内核中的消息队列时,会发生从用户态拷贝数据到内核态的过程,同理另一进程读取内核中的消息数据时,会发生从内核态拷贝数据到用户态的过程。

5.信号

正常的情况下,我们可以是使用管道和共享内存等的方式来进行进程间的通信。**对于异常情况下的工作模式,需要用「信号」的方式来通知进程。**比如,我们要结束一个前台进程可以使用热键[Ctrl + c]的方式来产生信号来结束进程。

后续分享~

6.Soket

管道、消息队列、共享内存、信号都是在同一台主机上进行进程间通信,那要想**跨网络与不同主机上的进程之间通信,就需要 Socket 通信了。**通过域间套接字,Socket也可以在同一台主机完成通信。

后续分享~

  • 22
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值