进程通信指的是进程间的信息交换 ,IPC(Inter-Process Communication,进程间通信)
进程通信就相当于一种工作方式、沟通形式,进程通信主要指的就是操作系统提供的进程通信工具(“封装好的方法”)用来进程间的信息交换。
IPC的方式通常有管道(包括无名管道和命名管道(FIFO))、消息队列、信号灯、共享内存等。
1.匿名管道PIPE:速度慢,容量有限,只有父子进程能通讯
2.命名管道FIFO:任何进程间都能通讯,但速度慢
3.消息队列:容量受到系统限制,且要注意第一次读的时候,要考虑上一次没有读完数据的问题
4.信号量:不能传递复杂消息,只能用来同步
5.共享内存区:能够很容易控制容量,速度快,但要保持同步,比如一个进程在写的时候,另一个进程要注意读写的问题,相当于线程中的线程安全,当然,共享内存区同样可以用作线程间通讯,不过没这个必要,线程间本来就已经共享了同一进程内的一块内存。
6.客户服务器系统
管道
管道:传输资源。本质上是内核的一块缓冲区。
特点:
1. 它是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端。
2. 它只能用于具有亲缘关系的进程之间的通信(也是父子进程或者兄弟进程之间)。
3. 它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write 等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。
匿名管道
没有名字的管道(仅用于具有亲缘关系(父子,兄弟等)的进程间通信)。
匿名管道原理:以父子进程为例:创建一个子进程,子进程复制了父进程的描述符表,因此子进程也有描述符表,并且他们指向的是同一个管道,由于父子进程都能访问这个管道,就可以通信。
因为管道是半双工单向通信,因此在通信前要确定数据流向:即关闭父子进程各自一端不用的读写。如果一方是读数据就关闭写的描述符。
匿名管道特性:
1.只能用于具有亲缘关系的进程间通信;
2.管道是半双工单向通信;(两个文件描述符,用一个,另一个不用,不用的文件描述符就要close)
3.管道的生命周期随进程(打开管道的所有进程退出,管道释放);
4.管道是面向字节流传输数据。(面向字节流:数据无规则,没有明显边界,收发数据比较灵活:对于用户态,可以一次性发送也可以分次发送,当然接受数据也如此;而面向数据报:数据有明显边界,数据只能整条接受 )
5.内核会对管道操作进行同步与互斥;
匿名管道读写规则:
1.管道无数据读取(read): 如果描述符是默认的阻塞特性,读取将会阻塞挂起等待,直到管道有数据;
2.管道被写满(write):如果描述符是默认的阻塞特性,写入操作会阻塞挂起等到,直到有数据被取走;如果描述符被设置为非阻塞特性,写入操作不具备条件,直接报错返回-1,错误码:EAGAIN。
3.如果所有管道写入端对应的文件描述符被关闭,则读取完管道中数据,然后read返回0;
4.如果所有管道读取端对应的文件描述符被关闭,则write操作会触发异常(因为没有人读数据),操作系统会给进程发送SIGPIPE,进程收到这个进程会退出;
5.当要写入的数据量不大于PIPE_BUF(512字节)时,linux将保证写入的原子性(操作不会被打断,一步完成);
6.当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。
命名管道
命名管道:文件系统可见,是一个特殊类型(管道类型)文件,命名管道可以应用于同一主机上任意进程间通信。
命名管道打开特性:
1. 如果用只读打开命名管道,open函数将阻塞等待直至有其他进程以写的方式打开这个命名管道,如果没有进程以写的方式发开这个命名管道,程序将停在此处
2. 如果用只写打开命名管道,open函数将阻塞等到直至有其他进程以读的方式打开这个命名管道,如果没有进程以读的方式发开这个命名管道,程序将停在此处;
3. 如果用读写打开命名管道,则不会阻塞(但是管道是单向)
命名管道和匿名管道区别和联系:
1.区别:① 匿名管道用int pipe(int pipefd[2]); 创建并打开匿名管道返回描述符
命名管道用mkfifo或者 int mkfifo(const char *pathname, mode_t mode);创建,并没有打开,如果打开需要open;
② 匿名管道是具有亲缘关系进程间通信的媒介,而命名管道作为同一主机任意进程间通信的媒介;
③ 匿名管道不可见文件系统,命名管道可见于文件系统,是一个特殊类型(管道类型)文件。
2.联系:匿名管道和命名管道都是内核的一块缓冲区,并且都是单向通信;
另外当命名管道打开(open)后,所有特性和匿名管道一样(上文匿名管道读写规则与管道特性):两者自带同步(临界资源访问的时序性)与互斥(临界资源同一时间的唯一访问性),管道生命周期随进程退出而结束
消息队列
消息队列实际上是操作系统在内核为我们创建的一个队列,通过这个队列的标识符key,每一个进程都可以打开这个队列,每个进程都可以通过这个队列向这个队列中插入一个结点或者获取一个结点来完成不同进程间的通信。
如何传输数据:
用户组织一个带有类型的数据块,添加到队列中,其他的进程从队列中获取数据块,即消息队列发送的是一个带有类型的数据块;消息队列是一个全双工通信,可读可写(可以发送数据,也可以接受数据)
消息队列生命周期随内核,如果没有释放消息队列或者没有关闭操作系统,消息队列会一直存在。
消息队列是存放在内核中的消息链表,每个消息队列由消息队列标识符表示。
与管道不同的是消息队列存放在内核中,只有在内核重启(即,操作系统重启)或者显示地删除一个消息队列时,该消息队列才会被真正的删除。(无名管道:只存在于内存中的文件;命名管道:存在于实际的磁盘介质或者文件系统)
另外与管道不同的是,消息队列在某个进程往一个队列写入消息之前,并不需要另外某个进程在该队列上等待消息的到达
消息队列特点总结:
1 消息队列是消息的链表,具有特定的格式,存放在内存中并由消息队列标识符标识.
2 消息队列允许一个或多个进程向它写入与读取消息
3 管道和消息队列的通信数据都是先进先出的原则。
4 消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取.比FIFO更有优势。
5 消息队列克服了信号承载信息量少,管道只能承载无格式字 节流以及缓冲区大小受限等缺点。
6 目前主要有两种类型的消息队列:POSIX消息队列以及System V消息队列,System V消息队列目前被大量使用。System V消息队列是随内核持续的,只有在内核重起或者人工删除时,该消息队列才会被删除。
信号量
本质:具有一个等待队列的计数器(现在是否有资源可以使用)
同步:保证对临界资源访问的时序可控性(保证有序);
互斥:对临界资源同一时间的唯一访问性(保证安全)。
多个进程同时,操作一个临界资源的时候就需要通过同步与互斥机制开实现对临界资源的安全访问;
同步:只有信号量资源计数从0变为1的时候,会通知别人打断阻塞等待,去操作临界资源,也就是释放了资源(计数器+1)之后才能获取资源(计数器-1),然后进行操作;
互斥:信号量如果想要实现互斥,那么它的计数器只能是0/1(一元信号量),我获取的计数器的资源,那么别人就无法获取。
P操作:获取信号量资源及计数器-1操作,如果计数器为0,需要等待别人释放资源。
V操作:释放信号量资源及计数器+1操作;
信号量计数器大于0,代表信号量有资源,可以操作;
信号量计数器等于0,代表信号量没有资源,需要等待。
信号量作为进程间通信方式,意味着大家都能访问到信号量,信号量实际也是一个临界资源,当然信号量的这个临界资源的操作是不会出问题的,因为信号量的操作是一个原子操作。
共享内存
1 共享数据结构
2 共享存储区
特点:
1 共享内存是最快的一种 IPC,因为进程是直接对内存进行存取。
2 因为多个进程可以同时操作,所以需要进行同步。
3 信号量+共享内存通常结合在一起使用,信号量用来同步对共享内存的访问。
客户服务器系统
客户机一服务器系统的通信机制,在网络环境的各种应用领域已成为当前主流的通信实现机制
1 套接字
2 远程过程调用
3 远程方法调用
主要讲一下套接字(SOCKET)
套接字是一种通信机制,凭借这种机制,客户/服务器(即要进行通信的进程)系统的开发工作既可以在本地单机上进行,也可以跨网络进行。也就是说它可以让不在同一台计算机但通过网络连接计算机上的进程进行通信。
套接字是支持TCP/IP的网络通信的基本操作单元,可以看做是不同主机之间的进程进行双向通信的端点,简单的说就是通信的两方的一种约定,用套接字中的相关函数来完成通信过程。
套接字特性
套接字的特性由3个属性确定,它们分别是:域、端口号、协议类型。
套接字通信的建立
- 服务端
- 首先服务器应用程序用系统调用socket来创建一个套接字,它是系统分配给该服务器进程的类似文件描述符的资源,它不能与其他的进程共享。(socket)
- 服务器进程会给套接字起个名字,我们使用系统调用bind来给套接字命名。然后服务器进程就开始等待客户连接到这个套接字。(bind)
- 系统调用listen来创建一个队列并将其用于存放来自客户的进入连接。(listen)
- 服务器通过系统调用accept来接受客户的连接。它会创建一个与原有的命名套接不同的新套接字,这个套接字只用于与这个特定客户端进行通信,而命名套接字(即原先的套接字)则被保留下来继续处理来自其他客户的连接(建立客户端和服务端的用于通信的流,进行通信)。(accept--read/write)
- 客户端
- 客户应用程序首先调用socket来创建一个未命名的套接字。(socket)
- 将服务器的命名套接字作为一个地址来调用connect与服务器建立连接。(connect)
- 一旦连接建立,我们就可以像使用底层的文件描述符那样用套接字来实现双向数据的通信(通过流进行数据传输)(read/write)
其中五种通讯方式总结
1.管道:速度慢,容量有限,只有父子进程能通讯
2.FIFO:任何进程间都能通讯,但速度慢
3.消息队列:容量受到系统限制,且要注意第一次读的时候,要考虑上一次没有读完数据的问题
4.信号量:不能传递复杂消息,只能用来同步
5.共享内存区:能够很容易控制容量,速度快,但要保持同步。
比如一个进程在写的时候,另一个进程要注意读写的问题,相当于线程中的线程安全,当然,共享内存区同样可以用作线程间通讯,不过没这个必要,线程间本来就已经共享了同一进程内的一块内存
感谢:https://www.cnblogs.com/noteless/p/10354581.html#4
https://blog.csdn.net/wh_sjc/article/details/70283843