操作系统学习笔记 面试准备(进程通信)

进程通信

进程同步与进程通信不一样,区别在于:

  • 进程同步:控制多个进程按一定顺序执行。
  • 进程通信:进程间传输信息。

进程通信是一种手段,而进程同步是一种目的。也可以说,为了能够达到进程同步的目的,需要让进程之间进行通信,传输一些进程同步所需要的信息。

关于进程间的通信有三个问题:

  1. 一个进程如何传递消息给其他进程
  2. 如何确保两个或多个进程间不会相互干扰,例如:两个航空公司都试图为不同的顾客抢购飞机上的最后一个座位。
  3. 数据的先后顺序问题,如果进程A产生数据并且进程B打印数据,则进程B打印数据之前需要先等A产生数据后才能够打印。

进程间通信的七种方式

管道/匿名管道

在Linux中有一种命令类似下面这种

$ ps auxf | grep mysql

上面命令行中的 |就是一个管道,表示将前一个命令(ps auxf)的输出作为下一个命令(grep mysql)的输入,可以看出管道传输是单向的。

上面|表示的管道是没有名字的,所以称为匿名管道,用完就销毁。

管道还有另外一种类型叫命名管道,也被叫做FIFO,因为数据时先进先出的传输方式。

匿名管道的创建需要通过下面这个系统调用:

int pipe(int fd[2])

表示创建一个匿名管道并返回两个描述符,一个是管道的读取端描述符fd[0],一个是管道的写入端描述符fd[1]。这个匿名管道是特殊的文件,只存在于内存,不存于文件系统中。

image-20211128113028275

这两个描述符都是在一个进程里面,不能起到进程间通信的作用,因此可以使用fork创建子进程,创建的子进程就会复制父进程的文件描述符,这样两个进程各有两个(fd[0]和fd[1])两个进程通过各自的fd写入读取同一个管道文件实现跨进程通信。

因此匿名管道只能用于父子进程或者兄弟进程之间。

管道只能一端写入,另一端读出,所以通常

  • 一个进程关闭读取的fd[0],只保留写入的fd[1]。
  • 另一个进程关闭写去的fd[1],只保留读取的fd[0]。

管道是半双工的,数据只能向一个方向流动,双方互相通信时需要建立两个管道。

image-20211128113819855

管道/命名管道

匿名管道,由于没有名字用完就销毁,只能用于亲缘关系的进程间通信,为了克服这个缺点,提出了命名管道

  • 命名管道遵循先进先出的方式。读总是从开始处返回数据,对他们的写则把数据添加到末尾。
  • 命名管道以文件形式存在于文件系统中,即使与命名管道的创建进程不存在亲缘关系,只要访问路径,就能够彼此通过命名管道相互通信。
  • 命名管道的名字存在于文件系统中,内容存放在内存中。
信号

信号是一种比较复杂的通信方式,用于通知接收进程某个时间已经发生。

在Linux操作系统中,为了响应各种各样的事件,提供了很多种信号,分别代表不同的意义。可以通过 kill -l命令查看。

如果该进程当前并未处于执行状态,则该信号就由内核保存起来,直到该进程恢复执行并处递给它为止。

如果在后台运行,可以通过kill命令的方式给进程发送信号,但前提需要知道运行中的进程PID号

举个栗子:kill -9 1050,表示给PID为1050的进程发送SIGKILL信号,用来立即结束该进程。

信号是进程间通信机制中唯一的异步通信机制,可以在任何时候发送信号给某个进程,一旦信号产生就会有以下不同的情况

  1. 执行默认操作。Linux对每种命令都规定了默认操作,例如SIGTERM信号就是终止进程的意思。
  2. 捕捉信号。可以为信号定义一个信号处理函数,当信号发生时就执行相应的信号处理函数。
  3. 忽略信号。当不希望处理某些信号时就可以忽略该信号,不做任何处理。
消息队列

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

消息队列的通信模式可以解决这个问题。A进程要给B进程发送消息,A进程把数据放在对应的消息队列后就可以返回了,B进程需要的时候区队列中读取数据就可以了。同理B进程也是如此。某个进程往消息队列中写入消息之前,并不需要另外某个进程在该队列上等待消息的到达。

消息队列是保存在内核中的消息链表,在发送数据时,会分成一个一个的数据单元(消息体),消息体是用户自定义的数据类型,消息发送方和接收方约定好消息体的数据类型,如果进程从消息队列中读取了消息体,内核就会把这个消息体删除。

因为消息队列存放在内核中,只有内核重启或者显示的删除一个消息队列时该消息队列才会被真正删除。

消息队列不适合大数据的传输,因为每个消息体都有一个最大长度的限制,所有队列所包含的全部消息体的总长度也是有上限的。

消息队列通信过程中,存在用户态和内核态之间的数据拷贝开销。因为消息队列存在内核中,一个进程向消息队列中存放消息时会发生从用户态拷贝数据到内核态的过程。

共享内存

消息队列的读取和写入会有用户态到内核态的开销,而共享内存的方式可以解决这个问题。

现代操作系统,对于内存管理采用的是虚拟内存技术,也就是每个进程都有自己独立的虚拟内存空间,不同进程的虚拟内存映射到不同的物理内存中。

共享内存的机制就是拿出一块虚拟地址空间,映射到相同的物理内存中。这样这个进程写入东西,另外一个进程马上就能看到了。不需要拷来拷去。

image-20211128143523546

信号量

使用了共享内存通信方式,如果多个进程同时修改同一个个共享内存,很有可能就冲突了。两个进程同时写一个地址,那先写的那个进程会发现内容被别人覆盖了。为了防止多进程竞争共享共享资源,而造成的数据错乱,所以就需要保护机制,而信号量就实现了这个保护机制。

信号量是一个整型的计数器,主要用于实现进程间的互斥与同步,而不是用于内存进程间通信的数据。

信号量在进程同步中已经介绍过了。

套接字

前面的通信方式都是在同一台主机上进行通信,如果想要跨网络与不同主机上的进程之间通信,就需要Socket通信。

Socket不仅可以跨网络与不同主机的进程间通信,还可以在不同主机上进程间通信。

Socket的系统调用

int socket(int domain,int type,int protocal)

用套接字中的相关函数完成通信过程。

参考文章

凉了!某丙没答好「进程间通信」,被面试官挂了…

[进程间通信IPC (InterProcess Communication) - 简书 (jianshu.com)](

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值