进程间的通信(IPC)
为什么要提供进程间的通信呢?
因为进程具有独立性,每一个进程都有自己独立的虚拟地址空间,所有的操作都在自己的虚拟地址空间内完成,因此进程之间无法直接通信,所以就有了管道的出现。
如何提供通信方式呢?
给多个进程之间提供一个大家都能够进行访问的传播介质,并且该介质也会根据不同的通信场景提供不同的通信方式,大家就能够进行通信了。
通道:
本质:是内核中的一块缓冲区,若干进程可访问同一块缓冲区,进行通信。
作用:数据传输的资源管道
特点:管道是非覆盖写的数据传输方式。
而管道又分为 匿名管道 和 命名管道。
匿名管道
匿名管道主要用于具有亲缘关系的进程间的通信(父子进程、兄弟进程)。
特点:
1> 匿名管道是半双工,数据只能向一个方向流动,一端输入,另一端输出。
2> 使用匿名管道时,进程不需要关系管道在内存中位置,但是要通过进程的亲缘关系来实现进程与管道的连接。
3> 管道所传输的数据是无格式的,这要求管道的读写方式必须事先约定好,如多少字节算一个消息等。
4> 管道不是普通的文件,不属于某个系统文件,其只存在于内存中,是不可见文件。
5> 从管道读取数据是一次性操作,数据一旦被读走,它就从管道中被抛弃,释放空间以便于写其他数据。
特性:
1> 若管道中暂无数据,则调用 read 会阻塞。
2> 若管道中数据已满,则调用 write 会阻塞。
3> 若通道中的所有写端 (pipefd[1]) 被关闭,则继续调用 read 会先将管道中的数据全部读取然后返回 0,之后再次调用则会阻塞。
4> 若通道中的所有读端 (pipefd[0]) 被关闭,则继续调用 write 会出现异常并退出。
5> 若管道中从来都没写入过数据,若关闭所有写端,调用 read 会直接返回,不再阻塞; 若仅仅关闭部分写端,调用 read 会阻塞
注意点:
1> 匿名管道一定要创建在子进程之前,这样才能保证子进程能够通过复制父进程得到匿名管道的操作句柄。
2> 父、子进程都具有读写端,因为匿名管道是半双工通信,为了保证进程在运行完后直接退出,而不是继续等待,所以不使用的读写端需要关闭。
3> 一个进程在退出的时候会自动关闭自身的读写端。
创建格式
#include<unistd.h>
int pipe(int pipefd[2])
参数: pipefd[0] --- 用管道读写数据
pipefd[1] --- 用管道写入数据
返回值: 成功 --- 0 失败 --- -1
具体操作使用:
1.父子进程创建并使用匿名管道
- 用匿名管道实现 ls -l | grep shm
命名管道
命名管道又被称为 FIFO,它作为一种特殊的文件类型,在文件系统中有对应的路径。 当一个进程以写 ( w ) 的方式打开,而另一个进程以读 ( r ) 的方式打开该文件,那么内核就能在两个进程之间建立通道。
与匿名管道不同,命名管道是一个可见文件,所以同一主机下的任意进程都可以通过打开该文件实现通信。
特点:
1> 若文件以只读( w ) 打开,则会阻塞,直到该文件被另一个进程以写的方式打开。
2> 若文件以只写( r ) 打开,则会阻塞,直到该文件被另一个进程以读的方式打开。
2> 命名管道严格遵守 ‘ 先进先出 ‘ 原则,当对其进行写操作时,数据会被添加到文件末尾;当对其进行读操作时,文件头部的数据先返回。
特性与匿名管道相同
创建格式:
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
int mkfifo(const char* path, mode_t mode)
参数:
path: 管道文件创建的路径
mode: 管道的操作权限
返回值: 成功 --- 0 失败 --- -1
具体操作使用: