引入
上一篇文章介绍了 Linux 中通过pipe
创建匿名管道,并实现父子进程间通信的功能;当时我就提到了 Linux 中的另一种管道通信方式——命名管道,下面就来详细介绍一下;
命名管道
什么是命名管道
命名管道(Named Pipe),也叫FIFO(First In First Out),是一种用于进程间通信(IPC)的机制。与匿名管道不同,命名管道是存在于文件系统中的特殊文件,具有持久性,可以用于不相关的进程间的数据传递;
命名管道的特点:
- 持久性:命名管道存在于文件系统中,一旦创建,即使创建管道的进程结束,管道仍然存在,直到手动删除。
- 半双工通信:在一个时刻数据只能单向传输,发送和接收通常需要在两个不同的进程中操作。
- 阻塞行为:当读取端没有准备好时,写入命名管道的操作会被阻塞,反之亦然,除非使用非阻塞方式。
- 文件系统挂载点:命名管道是通过文件系统的路径标识的,可以像普通文件一样打开、读写,但它不存储数据,只是用来传递数据。
- 跨进程通信:命名管道支持同一主机上的不同进程之间的通信,甚至可以在父子进程之外的进程中通信。 这篇文章会详细介绍命名管道作为文件进行跨进程通信的功能;
基本操作
创建
Linux 系统中,可以使用mkfifo
命令创建命名管道:
mkfifo my_pipe
# 创建一个命名管道
使用(文件属性)
之前说过,命名管道具有文件属性,所以它适用于文件的相关操作,比如:
- 写入进程:
echo "hello world" > my_pipe
# 向创建的命名管道中写入数据
- 读取进程
cat < my_pipe
# 使用 cat 读取文件内容
在这种情况下,echo 命令会把数据写入管道,而 cat 命令会从管道中读取数据。由于管道是 FIFO 的,所以数据按写入顺序被读取。
代码演示
框架搭建
下面我们搭建一个简单的模型,用于演示在命名管道下进程间的通信:
- 首先我们需要了解命名管道可以用于没有相关性的进程之间的通信,所以我们创建两个可执行文件,用于模拟两个不同进程;
- 随后调用系统函数
write
,open
,read
等对信息写入、读取等工作; - 对于
mkfifo
函数,可以查到它的接口:
int mkfifo(const char* pathname, mode_t mode);
// pathname,即路径名称,用于指定管道文件的创建位置
// mode,模式选项,这里指文件的权限信息
所以我们需要一个路径名称和权限选项(八进制); 综合上述说明,我们就得到了一个简易的程序框架,下面就是代码实现;
代码实现
这个代码实现和上一篇文章的匿名管道类似,这里不再贴出代码,具体代码细节可以访问我的GitHub主页,位于pipe文件夹下;
匿名和命名管道之间的区别
最后我们再来总结一下这两者之间的区别,便于大家更好的理解管道的内容:
- 命名方式
- 匿名管道:没有名字,存在于内存中,由创建它的进程通过文件描述符来引用。匿名管道只能用于相关的进程之间(例如,父子进程)。
- 命名管道:有名字,存在于文件系统中。不同的进程可以通过路径名来引用它,可以用于不相关的进程之间的通信。
- 创建方式
- 匿名管道:在 C 语言中,使用 pipe() 系统调用创建匿名管道,它会返回两个文件描述符,一个用于读,一个用于写。例如:
int pipefd[2];
pipe(pipefd); // 创建匿名管道
- 命名管道:在 C 语言中,可以通过 mkfifo() 系统调用创建命名管道,它会在文件系统中创建一个特殊的 FIFO 文件。例如:
mkfifo("/tmp/mypipe", 0666); // 创建命名管道
- 通信进程的关系
- 匿名管道:仅限于相关进程之间的通信,如父进程和子进程,因为它们继承了同样的文件描述符。匿名管道不适合不相关的进程之间进行通信。
- 命名管道:可以用于不相关进程之间的通信,因为它通过文件系统路径命名,任何有权限的进程都可以通过路径名打开这个管道进行通信。
- 生命周期
- 匿名管道:管道的生命周期与进程相同,一旦创建管道的进程退出,管道也会销毁。
- 命名管道:命名管道的生命周期与文件系统中的文件类似,管道文件在手动删除之前会一直存在,即使创建它的进程已经退出。
- 可见性
- 匿名管道:对用户和文件系统不可见,完全存在于内存中。
- 命名管道:在文件系统中可见,可以通过命令 ls 等查看,也可以通过路径进行操作。
- 典型使用场景
- 匿名管道:用于父子进程或兄弟进程间的简单通信,通常是在进程创建时立即建立通信通道。常用于短期通信,通常不会持久存在。
- 命名管道:用于需要在不相关进程间进行通信的场景,尤其是当进程可能独立运行,彼此需要通过一个预定义的管道进行通信时。
- 跨用户或网络通信
- 匿名管道:仅限于在同一台计算机上的进程通信,且必须是直接相关的进程(父子或兄弟进程)。
- 命名管道:尽管主要用于本地进程间通信,但可以通过网络文件系统(NFS)等实现跨网络的间接通信,但并不常用于此类场景。
区别总结表:
特性 | 匿名管道 | 命名管道 |
---|---|---|
命名方式 | 无名称,通过文件描述符标识 | 有名称,通过文件系统路径标识 |
创建方式 | pipe() 系统调用 | mkfifo() 系统调用 |
使用进程 | 父子进程、兄弟进程 | 不相关的进程都可以使用 |
通信范围 | 仅限相关进程 | 不相关的进程可通过文件路径通信 |
生命周期 | 与创建进程相同,进程结束即销毁 | 在文件系统中持续存在,直到手动删除 |
可见性 | 不可见,完全在内存中 | 可见,在文件系统中 |
典型场景 | 父子进程间的简单通信 | 不相关进程间的长时间通信 |