Linux---进程间通信

进程间通信的目的

  • 实现数据传输,一个进程将自己的数据发送给另一个进程。
  • 资源的共享。多个进程共用同一份资源。
  • 传递信息。例如:子进程退出需将自己的退出原因告知父进程。
  • 进程控制。有的进程希望完全控制另一个进程。
  • 共享数据。多个进程之间共享数据,当一个进程对数据进行修改,其他进程应该立即看到。

通信方式

  • 管道
  • systemV标准:共享内存、消息队列、信号量。

为什么操作系统要为进程提供通信方式,而不是进程之间直接通信

  • 因为进程之间具有独立性。
  • 每个进程都有自己的虚拟地址空间,每个进程的操作都是在自己的虚拟地址上进行,因此进程之间无法通信。

操作系统如何让进程之间进行通信

  • 操作系统在每个进程之间提供一个媒介资源,使得每个进程都能访问到,根据场景的不同,提供具体的操作也不同。常见的方式有,管道、共享内存、消息队列、信号量等。

管道

将系统内核的一块缓冲区作为公共媒介,让多个进程可以访问从而实现进程通信。在一个进程中创建一个管道,操作系统为了能让该进程对其创建出来的管道进行读写操作,会向该进程返回两个操作句柄(文件描述符),一个用于读,一个用于写。
在这里插入图片描述

  • 匿名管道
    系统内核没有为该缓冲区设置标识符(系统没有为该管道起一个名字),所以普通的进程没法知道该管道的具体信息,因此只能用于具有亲缘关系的进程间通信(父子进程)。在父进程创建一个管道,同时创建了一个子进程,子进程会复制父进程的pcb,同时也会复制两个操作句柄(文件的描述符),并且父子进程控制的是同一管道。
int pipe(int pipfd[2]);用于创建一个匿名管道,并且通过参数返回两个管道的操作句柄。
pipfd[0];用于从管道中读取数据
pipfd[1];用于从管道中写入数据
  • 命名管道
    系统内核为该管道缓冲区设有标识符(有名字),因此同一台主机上的进程可以通过该标识符对该管道进行操作,从而实现进程之间通信。
makffio(char *filename,mode_t mode);
filename//命名管道的标识符
mode//管道的权限信息
//example
makffio("make",0664);//创建一个名称为make的管道,权限信息为664

管道的读写特性

  • 如果管道中没有数据,则read端操作会被阻塞,如果管道中有数据,并且该缓冲区已满,则write端会被阻塞。
  • 如果管道中write端操作被关闭,则read端操作读完管道中已有的数据后,就会返回0,不再阻塞。(read)返回0主要表示管道中所有的write端被关闭。
  • 如果管道中read端被关闭,write端写入数据时会触发异常,进程退出。
  • 如果管道被以只读打开,则会被阻塞,直到管道被另一个进程以只写方式打开。
  • 如果管道被以只写打开,则会被阻塞,直到管道被另一个进程以只读方式打开。
  • 没有数据read阻塞,数据写满write阻塞。

管道本质
是操作系统内核中的一块缓冲区,多个进程通过访问同一个缓冲区实现通信。

管道的特性

  • 半双工通信。一端写入数据(write),另一端读数据(read),反之也可以。
  • 生命周期随进程。哪一个进程创建的管道,等该进程销毁时,管道也随之销毁。
  • 自带同步与互斥。同步:通过条件判断,实现对数据资源访问的合理性。互斥:通过同一时间的唯一访问,实现对数据资源访问的安全性。

共享内存

将一块物理地址映射到进程的虚拟地址空间中,进程通过自己的虚拟地址空间可以访问到该共享内存,从而实现通信。
在这里插入图片描述
每个虚拟地址中都有一个共享区,用来存放共享物理内存的虚拟地址,通过自己的页表映射到共享的物理内存,从而可以对该内存进行操作,同时另一个进程的虚拟地址空间也映射到该物理内存中,也可以对其进行操作,从而实现通信。

最快的通信方式

  • 普通的通信方式:先将数据拷贝到内核中,再从内核中将数据拷贝出来。
    在这里插入图片描述
  • 共享内存直接通过虚拟地址空间访问物理内存,少了两次的数据拷贝

共享内存创建函数

共享内存的创建与打开

#include<sys/ipc.h>
#include<sys/shm.h>

int shmget(key_t key,size_t size,int shmflg);
key:共享内存的标识符,多个进程通过表示符,才能访问同一块共享内存
size:要创建共享内存的大小
shmflg:
 IPC_PRIVATE 私有,只能用于亲属关系的进程间通信
 IPC_CREAT 共享内存不存在则创建,存在打开
 IPC_EXCL 与IPC_CREAT同时使用,不存在则创建,存在则报错
返回值:共享内存在代码中的操作句柄

进程间通信标key值的生成。

#define proj_id 0x1234678
key_t ftok(const char *pathname,int proj_id);
pathname:是文件信息,目录路径

shmat用于将创建好的共享内存,映射到进程的虚拟地址空间中。

void *shmat(int shmid,const void *shmaddr,int shmflg);
shmid:创建内存时返回的操作句柄
shmaddr:共享内存映射在虚拟地址空间中的首地址,通常情况下为NULL
shmflg:在共享内存具有权限的基础上,可以设置在代码中的操作权限
 SHM_RDONLY:只读权限
 默认为0,可读可写
返回值:返回共享内存在虚拟地址空间的首地址,通过该地址可以访问共享内存

shmctl共享内存管理

int shmctl(int shmid,int cmd,struct shmid_ds *buf);
shmid:操作句柄
cmd:对共享内存想要进行的操作
  IPC_RMID:标记共享内存为将要销毁,实际上只是将链接数-1
buf:在管理共享内存的时候,通过该结构体返回数据,或者设置新的数据

共享内存的本质
不同的进程通过链接同一块物理内存,实现数据传输,从而实现通信。

特性

  • 最快的通信方式
  • 生命周期随内核
  • 不安全,多个进程同时操作可能会造成数据二义性

ipcs 查看进程中通信资源

  • ipcs -m 共享内存
  • ipcs -q 消息队列
  • ipcs -s 信号量

消息队列

本质
在操作系统内核中,创建一个优先级队列,多个进程间通过向队列中添加数据块或者获取数据块,从而实现通信。

特性

  • 生命周期随内核。
  • 消息队列自带互斥与排斥。
  • 消息队列所能存储的数据是有最大长度限制的。

信号量

信号量本身并不是实现通信的,而是用于实现进程的同步与互斥。(保护进程间访问临界资源的时候不会出现数据二义性)
同步:通过一定条件判断,实现进程间对临界资源访问的时序合理性。
互斥:通过同一时间的唯一访问,实现进程间对临界资源访问的安全性。
临界资源:进程间都能访问的资源。
临界区:对临界资源进行操作的代码区。

信号量的本质
计数器+pcb等待队列

  • 计数器:统计资源的数量,通过数量判断当前的访问是否合理。
  • pcb等待队列:存放执行操作时不能获取资源而挂起的进程。

信号量同步的实现
数据是资源,初始化的时候有多少数据资源,计数器就会初始化相应的数量。
进程A去获取资源,若计数器>0,表示有资源,可以直接获取,同时计数-1;若计数器<=0,表示没有资源,则需要调度使得进程陷入休眠的接口,让进程等待-1。
进程B产生一个资源,判断若计数器<0,则调用唤醒一个休眠进程的接口,唤醒一个等待进程A,计数器+1;若计数器>=0,表示没有人等待资源,计数器直接+1即可。

信号量互斥的实现
当数据资源唯一时,在进程A访问临界资源时,将计数从1减为0(其他进程这时如果继续访问就会陷入等待队列中)
当等待进程A中的资源访问完毕时,将计数器进行+1(唤醒等待队列中的进程,各进程之间重新开始抢夺资源)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值