前言
在多进程编程中,进程间通信(Inter-Process Communication,IPC)是一种重要的技术手段,它使得不同进程可以安全、可靠地进行数据交换和共享资源。
一、常见的进程间通信方式
- 管道(Pipe):管道是一种基于字节流的进程间通信机制。它将一个进程的输出连接到另一个进 的输入,实现了它们之间的单向通信。管道分为匿名管道和命名管道两种。
(使用最简单)
- 命名管道(Named Pipe):命名管道(也称为FIFO)是一种有名的管道,它允许不相关的进程通过指定的命名管道文件进行通信。不同于匿名管道,命名管道可以用于不相关的进程之间的通信。
- 共享内存(Shared Memory):共享内存是一种高效的进程间通信方式。它允许多个进程共享同一块内存区域,从而避免了复制大量数据的开销。进程可以通过读写共享内存来进行通信和数据交换。
(无血缘关系)
- 信号量(Semaphore):信号量是一种用于进程同步和互斥的系统对象。它用于控制对共享资源的访问,多个进程可以根据信号量的值来进行等待、唤醒和临界区的互斥操作。
(开销最小)
- 套接字(Socket):套接字是一种用于在网络中进行进程间通信的机制。它可以用于在同一台机器上的进程间通信(本地套接字)或不同机器上的进程间通信(网络套接字)。
(最稳定)
二、如何实现管道通信
父进程 在写端 向管道写入数据,子进程 在 读端 从管道读出数据。(父进程 也可以连接 读端,子进程也可以连接 写端)
管道 是一种最基本的 IPC 机制,作用于有血缘关系的进程之间,完成数据传递。
- 管道的特性:
- 本质是一个伪文件。
- 由 2 个文件描述符引用,一个表示
读端
,一个表示写端
。 - 规定数据从管道的写端流入管道,从 读端流出。
- 管道的局限性:
- 数据不能 该进程自己写,自己读。
- 管道中
数据不可反复读取
。一旦读走,管道中不在存在。
- 采用半双工通信方式,数据只能在
单方向
上流动。 - 只能在共有祖先的基础上使用管道。
- pipe 函数 创建,打开 管道
使用 pipe() 函数进行进程间通信时,需要进程具有血缘关系,通常是父子进程之间。如无血缘关系的进程或完全独立的线程,无法直接使用 pipe() 函数进行通信。
pipe 参数为一个数组, 里面有两个句柄(读 写,分别对应管道的读端,写端)。规定: fd[0] --> r ; fd[1] --> w 。
#include <unistd.h> int pipe(int pipefd[2]);
返回值: 成功返回 0,失败返回 -1。
三、示例代码解析
STDOUT_FILENO:这是一个预定义的常量,表示标准输出的文件描述符。
void sys_error(const char *str) { perror(str); // 将最近一次发生的系统调用错误输出到标准错误流 exit(1); // 正常终止程序 } int main(void) { int fd[2]; int ret; pid_t pid; char buff[20] = "hello pipe!\n"; char str[20]; ret = pipe(fd); // 创建,打开管道 if(ret == -1) { sys_error("pipe error!"); } pid = fork(); //创建子进程 if(pid == 0) { close(fd[0]); //子进程 关闭读端 write(fd[1], buff,sizeof(buff)); //向管道写入数据 close(fd[1]); } else { close(fd[1]); //父进程 关闭写端 ret = read(fd[0],str,sizeof(str)); //从管道读出数据 write(STDOUT_FILENO, str,sizeof(str)); // 将 str 写入标准输出中 close(fd[0]); } }
四、管道的读写行为
读管道:
- 管道有数据,read 返回实际独到的字节数。
- 管道无数据:
- 无写端, read 返回 0。
- 有 写端, read阻塞等待。
写管道:
- 管道 无 读端, 异常终止。
- 管道 有 读端:
- 管道 已满,阻塞等待。
- 管道未满,返回写出的字节个数。
总结
进程间管道通信是一种基本而有效的进程间通信机制,在多进程编程中扮演着重要角色。