信息共享地址空间分布
内核级的通信介质不需要显式地互斥访问机制,内部通过互斥锁已经实现了。(具体可以看《unix网络编程 卷2:进程间通信》5.8小节,给我们讲了posix消息队列实现机制,有兴趣可以看看)
但是有些消息队列、信号量不一定都是有内核维护结构的。比如Posix消息队列在内核中,System V消息队列就不在内核中;Posix基于内存的信号量就不在内核中维护,System V 信号量就在内核中维护。所以,具体要看系统的实现。
IPC对象的持续性
IPC是进程间通信的简称(inter process communication)。IP C对象的持续性分为三种:随进程,随内核和随文件系统
随进程持续的IPC对象:一直存在到打开着IPC对象的最后一个进程关闭该对象为止
随内核持续的IPC对象:一直存在到内核重新自举或者显示删除IPC对象为止
随文件系统:一直存在到显示删除IPC对象为止
持续性和所在地址空间是没有关系的。
IPC类型 | 持续性 |
管道 FIFO |
随进程 随进程 |
Posix消息队列 | 随内核 |
信号
信号的本质是一种在软件层次上对中断的模拟。
进程如何发现和接收信号?
信号的接收不是进程完成的,是内核帮我们代理接收的。内核为每个进程维护一个信号向量表和一个信号队列。进程P1给P2发送一个信号,只有当P2再次陷入内核时,才会检查信号对列,并根据相应的信号调用相应的信号处理函数。
信号检测和响应时机?
当进程再次陷入内核执行完要执行的功能时,才会检查信号对列并对信号作出响应。那什么情况下会陷入内核呢?系统调用,中断或者异常,睡眠都会使进程进入内核。
如何进入信号处理函数并返回的?
在内核中查信号向量表,找到相应的信号处理函数。但是信号处理函数在用户态,也就是说我们要进行内核态到用户态的切换才能进入信号处理函数,处理完后再次返回内核态。
但是我们不要忽略一种情况,就是当这个系统调用阻塞期间捕捉到一个信号,进程会被唤醒。这个时候就要考虑系统调用能不能被重新唤起。
信号分类:可靠(实时信号,信号ID在SIGRTMIN~SIGRTMAX之间)和不可靠(信号ID小于SIGRTMIN)
可靠:信号支持排队。当一个信号被阻塞了,产生几次信号,就会入队几次,接触阻塞时,所有的信号都会被递交。
不可靠:信号不支持排队。当一个信号被阻塞了,解除阻塞时,只会递交一次,无论在阻塞过程中产生几次信号。
信号阻塞:信号有一个产生,信号要得到处理称为递达。当信号被屏蔽了之后,实际上是递达被屏蔽掉了,也就是说信号处于产生和递达之间,是一个未决信号。被阻塞的信号处于未决状态,只有解除阻塞之后才完成递达的动作。
阻塞和非阻塞、同步和异步
1、同步和异步
同步和异步关注的是消息通信机制(synchronous communication/ asynchronous communication)。
同步是发出这个调用之后,没有得到结果就不返回。异步是发出一个调用,直接返回。换句话说,就是异步调用发出后,并不会马上得到结果,而是被调用者通过状态等信息来通知调用者。
例子:
打电话给书店老板要买一本书,书店老板说你稍等,然后就去查到底有没有这本书,可能费时5分钟也可能更长,你就在电话这边一直等待,等老板查好了,告诉你结果。
打电话买书,给老板说书名,老板说你稍等,挂完电话就开始找,找到后就给你打电话通知你结果。
linux内核中异步机制的实现包括:信号——进程间通信的异步机制;IO也有异步机制。
2、阻塞和非阻塞
阻塞和非阻塞关注的是进程在等待结果时的状态。进程到底有没有继续往下执行。
阻塞式调用会把自己一直挂起,直到得到这个结果。非阻塞式调用,就是不把自己挂起,先去干别的事。
父子进程
父进程调用fork()创建一个子进程的时候,子进程会采取写时复制技术(基于共享的理念,不能共享的就重新为子进程开辟内存页)。在没有采取写时复制技术之前,暴力复制父进程的内存页给子进程,写时复制技术就解决了这个矛盾,只有子进程需要修改某页时才会去复制内存页。
因为是在进程用户空间内存中维护stdio缓冲区,因此父进程通过fork()创建子进程的时候,子进程会复制父进程的缓冲区。