Linux之系统编程之进程通信

  • 进程通信:操作系统为用户提供的几种进程间通信方式(管道、共享内存、消息队列、信号量)
  • 为什么操作系统要提供进程间通信方式给用户?-----因为进程之间具有独立性(每个进程只能访问自己的虚拟地址,无法直接沟通,因此操作系统要提供公共媒介)。
  • 通信场景:数据传输/数据共享/进程控制。
  • system V标准的共享内存,消息队列,信息量只适用于uinux。
  • posix标准的共享内存,消息队列,信号量可跨平台。
  • 进程间通信目的:
    ①数据传输:一个进程需要将它的数据发送给另一个进程。
    ②资源共享:多个进程之间共享相同的资源。
    ③通知事件:一个进程需要向另一个或一组进程发送消息,通知它发生了某种事件(如进程终止时要通知父进程。)
    ④进程控制:有些进程希望完全控制另一个进程的执行,此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。

一、管道(用buf进行两次用户态和内核态之间的数据拷贝)

  • 从一个进程连接到另一个进程的一个数据流即一个“管道”。
    在这里插入图片描述
  • 本质:内核中的一块缓冲区,多个进程通过访问同一块缓冲区实现通信。
  • 种类有:匿名管道和命名管道
  • 管道的操作句柄:两个文件描述符,一个用于从管道读取数,一个用于向管道写入数据。pipefd[0]是用于从管道中读取数据,pipefd[1]是用于向管道中写入数据。
  • 特性:管道是半双工通信。
  • 生命周期:随进程。
  • 管道提供字节流传输服务。(字节流服务可能出现数据粘连,就是两次发送的数据当作一条一次性接收了。)

1.匿名管道

  • 没有标识符,其余进程都找不到它,只有操作句柄。只要复制管道操作句柄就能访问,故匿名管道一定要创建在子进程之前。因为其他进程没有这个句柄所以找不到内核中的这块缓冲区。而父子进程数据独有,各自有自己的数据,各有各的文件描述符,但是是指向同一个管道
  • 一个进程通过系统调用在内核中创建了一个管道(缓冲区),并且调用返回管道的操作句柄,但是内核中的这块缓冲区没有其它标识符只能通过操作句柄访问,因此匿名管道只能用于具有亲缘关系的进程间通信,因为只能通过子进程复制父进程的方式获取同一管道的操作句柄(文件描述符),进而访问同一块缓冲区。
  • 文件描述符是一个数组下标,是pcb中的struct files_struct中的struct file* fd_array[]的下标。标识符是内核中管道的一个名字----能通过名字找到对应的管道。
  • 创建:int pipe(int pipefd[2]);
    参数:pipefd:文件描述符数组,其中pipefd[0]表示读端,pipefd[1]表示写端。
    返回值:成功返回0,失败返回错误代码。

2.读写特性

  • 若管道中没有数据,则read会阻塞(同步),若管道中数据满了,则write会阻塞。(管道自带同步与互斥)
  • 若管道所有写端被关闭了,则read会读完数据后返回0。若管道所有读端被关闭,则write会触发异常,导致进程退出。
  • 对管道进行读写时,若读写数据大小不超过PIPE_BUF = 4096大小,则可以保证操作的原子性。(实现互斥)(操作的原子性:这个操作不会被打断。)

3.管道的同步与互斥

  • 互斥:同一时间只有一个执行流能够操作临界资源,实现数据的安全操作,为了保证数据的操作安全性。
  • 同步:通过一种条件的判断来实现对临界资源访问的时序合理性。
  • | 为管道符:连接两个命令,将前边命令得输出结果,交给后边命令进行处理。

4.命名管道

  • 内核中的缓冲区是有一个标识符的,这个标识符是一个可见于文件系统的管道文件。因此命名管道可用于同一主机上的任意进程间通信。
  • 若命名管道以只读方式打开,则会阻塞管道直到管道文件被其他进程以写的方式打开。
  • 若命名管道以只写方式打开,则会阻塞管道直到管道文件被其他进程以读的方式打开。
  • 创建:int mkfifo(const char* filename, mode_t mode);

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

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

二、共享内存(用于进程间的数据共享,不需要数据拷贝)

  • 共享内存是一块物理内存空间,多个进程可以将同一块物理内存空间映射到自己的虚拟地址空间中,然后访问同一块物理内存,以通信。
  • 特性:最快的进程间通信方式。‘
  • 生命周期:随内核
  • 共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的虚拟地址空间中,这些进程间数据传递不再涉及到内核,也就是说进程不再执行进入内核的系统调用来传递彼此的数据。
    在这里插入图片描述

1.实现原理

  • 在物理内存中开辟一块空间。这块空间在内核中是具有标识的。
  • 将这块内存空间通过页表映射到自己的虚拟地址空间中。
  • 通过虚拟地址进行内存操作。
  • 解除映射关系。
  • 删除共享内存。

2.最快进程间通信方式原理

  • 共享内存相较于其它进程间通信方式,在通信过程中少了两次用户态和内核态之间的数据拷贝。
  • 共享内存没有进行同步与互斥。

三、消息队列(用于进程间的数据块传输)

  • 本质:内核中的一个优先级队列,多个进程通过向同一个队列放置队列节点或获取节点实现通信。
  • 生命周期:随内核。

1.实现原理

  • 在内核中创建消息队列
  • 向队列中添加节点
  • 从队列中获取节点
  • 删除消息队列

2.特性

  • 自带同步与互斥
  • 传输有类型的数据块
  • 数据不会粘连(两个数据不会粘连到一块作为一个数据)

四、信号量

  • 信号量有System V信号量和Posix信号量。
  • System V信号量在内核中维护,可以用于进程间或者线程间的同步和互斥。
  • 信号量一般有两种,二值信号量和计数信号量。(二值信号量:其值只有两种0或1,当值为1的时候表示自愿可以使用,当值为0时,表示自愿不可以使用。计数信号量:其值是在0到某个限定值之间,不限定资源数只在0或1之间)
  • 但是Posix信号量一般是二值信号量;单个计数信号量。
  • System V信号量指的是计数信号量集,是一个或多个信号量的集合,其中每个都是计数信号量。
  • 本质:实现进程间的同步与互斥,但是不传输数据。内核中的一个原子操作的计数器,还有一个PCV等待队列。
  • 互斥:通过同一时间对临界资源只有一个进程能够访问,实现数据的安全操作。(数据访问的安全性)
  • 同步:通过一些条件判断实现对临界资源的有序访问。(数据访问的合理性)

1.实现

  • 互斥:通过自身的一个只有0/1的计数器实现(二值信号量)。通过一个状态标记临界资源管理当前的访问状态;对临界资源进行访问之前先判断一下这个标记。若状态为可以访问,则这个状态修改文不可访问,然后去访问数据,访问完毕之后再将状态修改为可访问状态。
  • 同步:通过自身的一个计数的判断以及等待与唤醒功能的实现。(计数信号量)通过一个计数器对资源进行计数,当想要获取临界资源的时候,先判断计数,是否有资源可以提供访问。若有,则计数-1,获取一个资源进行操作,若没有(计数<=0),则进行等待,等待其它进程生产数据后计数进行+1,然后在唤醒等待的进程。

2.信号量的PV原语。

  • P操作:对操作进行判断,然后进行-1,若没有资源则等待。
  • V操作:对计数进行+1,唤醒等待队列中挂起的进程。

3.信号量和共享内存

  • 信号量和共享内存是相互独立的,通过信号量的计数器对共享内存中的资源数量进行统计,要求进程在访问内向内存之前,先通过信号量判断是否能够访问共享内存,如果不能就等待。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值