进程通信
操作系统中每个进程地址空间相互独立,进程间通信必须经过内核。
广义进程通信方式
- 文件
- 管道
- 内存映射
- 共享内存
- 信号
- 套接字
- 消息队列
- 剪切板
- 远程过程调用
…
单机环境中常见的进程通信方式
管道通信
管道通信也成为匿名管道。管道通信使用方式最简单,用于有血缘关系的进程间通信。
主要特点:
- 使用两个文件描述符,一端表示读,一端表示写
- 两个进程都终结管道才终结
- 读端以及写端默认是阻塞的
- 本质是一个内核缓冲区,使用循环队列实现,
- 不可反复读取
函数原型:
/**
* 参数:
* 数组中[0]、[1]分别代表读端和写端
* 返回值:
* 成功返回0
* 失败返回-1,并设置errno值
*/
int pipe(int [2]);
命名管道
区别于管道通信,命名管道(FIFO)可以进行没有关联的进程间通信。
主要特点:
- FIFO是Unix/Linux基础文件类型的一种,文件类型用p标识
- FIFO在文件磁盘上数据快大小为0,仅用来标识内核中的通道
使用方式:
-
创建管道文件
管道文件严格遵循先进先出的方式,读取从开始处返回数据,写入将数据添加到末尾。
- 使用命令创建管道文件
mkfifo 管道名
- 函数创建
/** *参数: * 1:文件名 * 2:权限 * 返回值: * 文件描述符 */ int mkfifo(const char *, mode_t);
- 操作文件
管道文件与普通文件使用方式相同,系统的文件操作函数都可用于管道文件。
内存映射
存储映射是使磁盘中的文件与内存中的缓冲区相映射。
在缓冲区中读取数据,相当于读取文件中的数据;将数据写入缓冲区,相当于写入文件。
这样就可以只使用指针来完成I/O操作,可以用于无关联的进程之间的通信。
使用方式:
-
打开文件
-
函数调用
/** * 参数: * 1:映射起始地址,一般设置NULL,由操作系统指定 * 2:映射长度 * 3:读写模式 * 4:写入映射区是否写入到文件中(MAP_ANONYMOUS匿名映射) * 5:步骤1中打开的文件描述符(当文件描述符为-1使用匿名映射) * 6:文件偏移长度 * 返回: * 映射区首地址(内存块,与数组相似) */ void * mmap(void *, size_t, int, int, int, off_t)
-
释放资源
/** * 参数: * 1:地址 * 2:映射长度 * 返回值: * 成功返回0 * 失败返回-1 */ int munmap(void *, size_t)
信号
信号是信息的载体。当一个进程收到信号后,暂停运行,去执行信号处理函数,类似于中断。
信号的三种状态:
-
产生
信号的产生包含按键、系统调用、软件条件、异常以及命令等方式。 -
未决
产生和递达之间的状态,主要由于阻塞或者屏蔽产生。 -
递达
送达到进程中。
使用方式:
- 信号处理函数
/** * 信号处理函数 * 函数名自定义 */ void handle(int)
- 注册信号处理函数
方式一
方式二/** * signal函数参数: * 1:信号编号 * 2:信号处理函数 */ void(*signal(int, void (*)(int)))(int);
/** * sigaction函数参数: * 1:信号编号 * 2:传入参数:新的信号处理方式 * 3:传出参数:旧的信号处理方式 * 返回值: * 是否成功 */ int sigaction(int, const struct sigaction * __restrict, struct sigaction * __restrict);
❗❗合理使用信号阻塞能够解决进程间的同步问题。
共享内存
共享内存的实质是将内核的一块内存映射到进程中,操作本地的内存相当与操作共享内存,用于无关联的进程之间。
使用方式:
-
创建共享内存
/** * 函数参数: * 1:共享内存唯一key * 2:共享内存大小 * 3:创建模式以及权限 * 返回值: * 成功:返回共享内存ID(与key不一定相同) * 失败:返回-1 */ int shmget(key_t, size_t, int);
-
关联共享内存
/** * 函数参数: * 1:共享内存ID * 2:设置当前进程中的内存地址(与共享内存地址不一定相同),一般为NULL * 3:读写模式 * 返回值: * 成功:当前内存地址 * 失败:返回-1(void *) */ void *shmat(int, const void *, int);
-
读写内存
与数组操作基本一致,操作共享内存地址。
-
断开共享内存
/** * 函数参数: * 当前进程相关联的内存地址 * 返回值: * 成功与否 */ int shmdt(const void *);
-
删除共享内存
/** * 函数参数: * 1:共享内存ID * 2:操作(获取状态信息、设置信息、删除) * 3:内容 * 返回值: * 成功与否 */ int shmctl(int, int, struct shmid_ds *)
😄 此文档只作抛砖引玉,详细原理仍需要深入研究。