https://blog.youkuaiyun.com/jmy5945hh/article/details/7350564
这个太多了,没整理完
https://blog.youkuaiyun.com/wh_sjc/article/details/70283843#commentBox
进程是一个程序的一次执行过程。进程用户空间是相互独立的,一般而言是不能相互访问的。 但很多情况下进程间需要互相通信,来完成系统的某项功能。进程通过与内核及其它进程之间的互相通信来协调它们的行为。
多进程:
首先,先来讲一下fork之后,发生了什么事情。
由fork创建的新进程被称为子进程
(child process)。该函数被调用一次,但返回两次
。两次返回的区别是子进程的返回值是0,而父进程的返回值则是新进程(子进程)的进程 id。将子进程id返回给父进程的理由是
:因为一个进程的子进程可以多于一个,没有一个函数使一个进程可以获得其所有子进程的进程id。对子进程来说,之所以fork返回0给它,是因为它随时可以调用getpid()来获取自己的pid;也可以调用getppid()来获取父进程的id。(进程id 0总是由交换进程使用,所以一个子进程的进程id不可能为0 )。
fork之后,操作系统会复制一个与父进程完全相同的子进程,虽说是父子关系,但是在操作系统看来,他们更像兄弟关系,这2个进程共享代码空间
,但是数据空间是互相独立的
,子进程数据空间中的内容是父进程的完整拷贝,指令指针也完全相同,子进程拥有父进程当前运行到的位置(两进程的程序计数器pc值相同
,也就是说,子进程是从fork返回处开始执行的),但有一点不同,如果fork成功,子进程中fork的返回值是0,父进程中fork的返回值是子进程的进程号,如果fork不成功,父进程会返回错误。
可以这样想象,2个进程一直同时运行,而且步调一致,在fork之后,他们分别作不同的工作,也就是分岔了。这也是fork为什么叫fork的原因。
至于那一个最先运行,可能与操作系统(调度算法)有关,而且这个问题在实际应用中并不重要,如果需要父子进程协同,可以通过原语的办法解决。
进程通信的应用场景
-
数据传输
:一个进程需要将它的数据发送给另一个进程
,发送的数据量在一个字节到几兆字节之间。 -
共享数据
:多个进程想要操作共享数据
,一个进程对共享数据的修改,别的进程应该立刻看到
。 -
通知事件
:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程
)。 -
资源共享
:多个进程之间共享同样的资源
。为了作到这一点,需要内核提供锁和同步机制
。 -
进程控制
:有些进程希望完全控制另一个进程的执行
(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。
六种通信方式
1、管道
:管道只能承载无格式字节流
以及缓冲区大小受限
。管道是半双工的
,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道。不需要额外同步机制。
无名管道:
可用于具有亲缘关系
(父子进程、或者拥有相同祖先的两个子进程之间)进程间的通信。无名管道是基于fork机制产生的
有名管道FIFO:
允许无亲缘关系进程间的通信
2、信号
(Signal):信号是比较复杂的通信方式,用于通知接收进程有某种事件发生
,除了用于进程间通信外,进程还可以发送信号给进程本身
;
-
信号
可以在任何时候发给某一进程
,而无需知道该进程的状态
。 -
如果该进程
处于未执行状态
,则该信号就有内核保存起来,直到该进程恢复执行
并传递给它为止。 -
如果一个信号被进程设置为阻塞,则该信号的传递被延迟,直到其阻塞被取消时信号才被传递给进程。
3、消息队列
:是消息的链接表
,包括Posix消息队列system V消息队列。有足够权限的进程可以向队列中添加消息
,被赋予读权限的进程则可以读走队列中的消息
。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺点。
消息队列与管道通信相比
,其优势是对每个消息指定特定的消息类型
,接收的时候不需要按照队列次序
,而是可以根据自定义条件接收特定类型的消息
。可以把消息看做一个记录,具有特定的格式以及特定的优先级
。
4、共享内存
:使得多个进程可以访问同一块内存空间
,是最快的
可用IPC形式,需要额外的同步机制
。是针对其他通信机制运行效率较低而设计的。往往与其它通信机制,如信号量
结合使用,来达到进程间的同步及互斥。容量受到系统限制,且要注意第一次读的时候,要考虑上一次没有读完数据的问题
。
5、信号量
(semaphore):主要作为进程间
以及同一进程不同线程之间
的同步手段
。信号量是一个计数器
,可以用来控制多个进程对共享资源的访问
。它常作为一种锁机制
,防止某进程正在访问共享资源时,其他进程也访问该资源。不能传递复杂消息,只能用来同步
。
6、套接口
(Socket):可用于不同机器之间
的进程间通信。起初是由Unix系统的BSD分支开发出来的,但现在一般可以移植到其它类Unix系统上:Linux和System V的变种都支持套接字。
管道原理
无名管道的介绍
管道是由内核管理的一个缓冲区
,相当于我们放入内存中的一个纸条。管道的一端连接一个进程的输出
。这个进程会向管道中放入信息
。管道的另一端连接一个进程的输入
,这个进程取出被放入管道的信息
。一个缓冲区不需要很大,它被设计成为环形的数据结构,以便管道可以被循环利用。
当管道中没有信息的话,从管道中读取的进程会等待
,直到另一端的进程放入信息。当管道被放满信息的时候,尝试放入信息的进程会等待
,直到另一端的进程取出信息。当两个进程都终结的时候,管道也自动消失。
无名管道如何创建
从原理上,管道利用fork机制建立
,从而让两个进程可以连接到同一个PIPE上
。最开始的时候,上面的两个箭头都连接在同一个进程Process 1上(连接在Process 1上的两个箭头)。当fork复制进程的时候,会将这两个连接也复制到新的进程(Process 2)。随后,每个进程关闭自己不需要的一个连接 (两个黑色的箭头被关闭; Process 1关闭从PIPE来的输入连接,Process 2关闭输出到PIPE的连接),这样,剩下的红色连接就构成了如上图的PIPE。
无名管道创建后其对应的数据结构
在 Linux 中,管道的实现并没有使用专门的数据结构,而是借助了文件系统的file结构
和VFS的索引节点inode
。通过将两个 file 结构指向同一个临时的 VFS 索引节点inode
,而 VFS inode 索引节点又指向一个物理页面而实现的
。如下图:
有两个 file 数据结构,但它们定义
文件操作进程地址是不同的
,其中一个是向管道中写入数据的例程地址,而另一个是从管道中读出数据的例程地址。这样,用户程序的系统调用仍然是通常的文件操作,而内核却利用这种抽象机制实现了管道这一特殊操作。
有名管道 FIFO (First in, First out)
实现原理
FIFO 为一种特殊的文件类型,它在文件系统中有对应的路径
。当一个进程以读®的方式打开该文件,而另一个进程以写(w)的方式打开该文件,那么内核就会在这两个进程之间建立管道
,所以FIFO实际上也由内核管理
,不与硬盘打交道。之所以叫FIFO,是因为管道本质上是一个先进先出的队列数据结构
,最早放入的数据被最先读出来,从而保证信息交流的顺序
。写模式的进程向FIFO文件中写入,而读模式的进程从FIFO文件中读出。
当删除FIFO文件时,管道连接也随之消失。 FIFO的 好处
在于我们可以 通过文件的路径来识别管道
,从而让没有亲缘关系的进程之间建立连接
。
套接字特性
套接字的特性由3个属性确定,它们分别是:域、端口号、协议类型。
1、套接字的域
它指定套接字通信中使用的网络介质
,最常见的套接字域有两种
:
-
一是AF_INET,
指的是Internet网络
。当客户使用套接字进行跨网络的连接时
,它就需要用到服务器计算机的IP地址
和端口
来指定一台联网机器上的某个特定服务
,所以在使用socket作为通信的终点,服务器应用程序必须在开始通信之前绑定一个端口,服务器在指定的端口等待客户的连接
。 -
另一个域AF_UNIX(AF_LOCAL),
表示UNIX文件系统
,它就是文件输入/输出,而它的地址就是文件名
。
2、套接字的端口号
每一个基于TCP/IP网络通讯的程序(进程)都被赋予了唯一的端口和端口号
,端口是一个信息缓冲区
,用于保留Socket中的输入/输出信息
,端口号是一个16位无符号整数,范围是0-65535,以区别主机上的每一个程序(端口号就像房屋中的房间号),低于256的端口号保留给标准应用程序
,比如pop3的端口号就是110,每一个套接字都组合进了IP地址、端口
,这样形成的整体就可以区别每一个套接字。
3、套接字协议类型(3种)
(1)流套接字
(SOCK_STREAM)
流套接字在域中通过TCP/IP连接实现
,同时也是AF_UNIX中常用的套接字类型。流套接字提供的是
一个有序、可靠、双向字节流的连接
,因此发送的数据可以确保不会丢失、重复或乱序到达,而且它还有一定的出错后重新发送
的机制。
(2)数据报套接字
(SOCK_DGRAM)
它们在域中通常是通过UDP/IP协议实现的
, 不需要建立连接和维持一个连接。它对可以发送的数据的长度有限制
,数据报作为一个单独的网络消息被传输,它可能会丢失、重复或错乱到达
,UDP不是一个可靠的协议,但是它的速度比较高
。
(3)原始套接字
(SOCK_RAW)
原始套接字允许对较低层次的协议直接访问
,比如IP、 ICMP协议,它常用于检验新的协议实现
,或者访问现有服务中配置的新设备,因为RAW SOCKET可以自如地控制Windows下的多种协议,能够对网络底层的传输机制进行控制
,所以可以应用原始套接字来操纵网络层和传输层应用
。比如,我们可以通过RAW SOCKET来接收发向本机的ICMP、IGMP协议包,或者接收TCP/IP栈不能够处理的IP包,也可以用来发送一些自定包头或自定协议的IP包。网络监听技术很大程度上依赖于SOCKET_RAW。
原始套接字与标准套接字的区别在于:
原始套接字可以读写内核没有处理的IP数据包
,而流套接字只能读取TCP协议的数据
,数据报套接字只能读取UDP协议的数据
。因此,如果要访问其他协议发送数据必须使用原始套接字。