文章目录
- 前言
- 一、常见的进程间通信方式
- 二、如何实现管道通信
- 三、示例代码解析
- 四、管道的读写行为
- 总结
前言
在多进程编程中,进程间通信(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 阻塞等待。
写管道:
- 管道 无 读端, 异常终止。
- 管道 有 读端:
- 管道 已满,阻塞等待。
- 管道未满,返回写出的字节个数。
总结
进程间管道通信是一种基本而有效的进程间通信机制,在多进程编程中扮演着重要角色。