Linux/Unix 系统中的命名管道或 FIFO 是什么?
管道旨在用于相关进程之间的通信。我们不能将管道用于不相关进程的通信。然后,要实现不相关进程的通信,简单的答案就是命名管道。即使这适用于相关进程,但将命名管道用于相关进程的通信也没有意义。
与管道不同,我们可以使用单个命名管道来进行双向通信(服务器和客户端之间的通信,以及客户端和服务器在同一时间进行通信),因为命名管道支持双向通信。
命名管道的另一个名称是**FIFO(先进先出)**。让我们看看创建命名管道的系统调用(mknod()),它是一种特殊文件。
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> int mknod(const char *pathname, mode_t mode, dev_t dev);
此系统调用将创建特殊文件或文件系统节点,例如普通文件、设备文件或 FIFO。系统调用的参数是路径名、模式和设备。路径名以及模式和设备信息的属性。如果未指定目录,则路径名是相对的,它将在当前目录中创建。指定的模式是文件的模式,它指定文件类型,例如文件类型和文件模式,如以下表格中所述。dev 字段用于指定设备信息,例如主设备号和次设备号。
文件类型 | 描述 | 文件类型 | 描述 |
---|---|---|---|
S_IFBLK | 块特殊文件 | S_IFREG | 普通文件 |
S_IFCHR | 字符特殊文件 | S_IFDIR | 目录 |
S_IFIFO | FIFO 特殊文件 | S_IFLNK | 符号链接 |
文件模式 | 描述 | 文件模式 | 描述 |
---|---|---|---|
S_IRWXU | 所有者具有读、写、执行/搜索权限 | S_IWGRP | 组具有写权限 |
S_IRUSR | 所有者具有读权限 | S_IXGRP | 组具有执行/搜索权限 |
S_IWUSR | 所有者具有写权限 | S_IRWXO | 组具有执行/搜索权限 |
S_IXUSR | 所有者具有执行/搜索权限 | S_IROTH | 其他人具有读权限 |
S_IRWXG | 组具有读、写、执行/搜索权限 | S_IWOTH | 其他人具有写权限 |
S_IRGRP | 组具有读权限 | S_IXOTH | 其他人具有执行/搜索权限 |
文件模式也可以用八进制表示法表示,例如 0XYZ,其中 X 表示所有者,Y 表示组,Z 表示其他人。X、Y 或 Z 的值可以介于 0 到 7 之间。读、写和执行的值分别为 4、2、1。如果需要读、写和执行的组合,则相应地添加值。
例如,如果为 0640,则表示所有者具有读写权限 (4 + 2 = 6),组具有读权限 (4),其他人没有权限 (0)。
如果成功,此调用将返回零;如果失败,则返回 -1。要了解失败的原因,请检查 errno 变量或 perror() 函数。
#include <sys/types.h> #include <sys/stat.h> int mkfifo(const char *pathname, mode_t mode)
此库函数创建一个 FIFO 特殊文件,用于命名管道。此函数的参数是文件名和模式。文件名可以是绝对路径或相对路径。如果未给出完整路径名(或绝对路径),则文件将在正在执行的进程的当前文件夹中创建。文件模式信息如 mknod() 系统调用中所述。
如果成功,此调用将返回零;如果失败,则返回 -1。要了解失败的原因,请检查 errno 变量或 perror() 函数。
让我们考虑一个在终端上运行服务器并在另一个终端上运行客户端的程序。该程序只会执行单向通信。客户端接受用户输入并将消息发送到服务器,服务器在输出上打印消息。该过程将持续进行,直到用户输入字符串“end”。
让我们通过一个例子来理解这一点 -
**步骤 1** - 创建两个进程,一个是 fifoserver,另一个是 fifoclient。
**步骤 2** - 服务器进程执行以下操作 -
创建名为“MYFIFO”的命名管道(使用系统调用 mknod()),如果尚未创建。
以只读方式打开命名管道。
这里,创建的 FIFO 对所有者具有读写权限,对组具有读权限,对其他人没有权限。
无限期地等待来自客户端的消息。
如果从客户端接收到的消息不是“end”,则打印该消息。如果消息是“end”,则关闭 fifo 并结束进程。
**步骤 3** - 客户端进程执行以下操作 -
1) 以只写方式打开命名管道。
2) 接受用户的字符串。
3) 检查用户是否输入“end”或除“end”之外的其他内容。无论哪种方式,它都会向服务器发送消息。但是,如果字符串是“end”,则关闭 FIFO 并结束进程。
4) 无限期地重复,直到用户输入字符串“end”。
使用命名管道进行双向通信
管道之间的通信应该是单向的。通常,管道仅限于单向通信,并且需要至少两个管道才能进行双向通信。管道仅用于相关进程。管道不能用于不相关进程的通信,例如,如果我们想在一个终端上执行一个进程,在另一个终端上执行另一个进程,则管道无法实现。命名管道旨在用于两个或多个不相关进程之间的通信,并且还可以进行双向通信。
我们已经看到了命名管道之间的单向通信,即从客户端到服务器的消息。现在,让我们看一下双向通信,即客户端向服务器发送消息,服务器接收该消息并使用相同的命名管道将另一条消息发送回客户端。
以下是一个示例 -
**步骤 1** - 创建两个进程,一个是 fifoserver_twoway,另一个是 fifoclient_twoway。
**步骤 2** - 服务器进程执行以下操作 -
在 /tmp 目录中创建名为“fifo_twoway”的命名管道(使用库函数 mkfifo()),如果尚未创建。
以读写方式打开命名管道。
这里,创建的 FIFO 对所有者具有读写权限,对组具有读权限,对其他人没有权限。
无限期地等待来自客户端的消息。
如果从客户端接收到的消息不是“end”,则打印该消息并反转字符串。反转后的字符串将发送回客户端。如果消息是“end”,则关闭 fifo 并结束进程。
**步骤 3** - 客户端进程执行以下操作 -
以读写方式打开命名管道。
接受用户的字符串。
检查用户是否输入“end”或除“end”之外的其他内容。无论哪种方式,它都会向服务器发送消息。但是,如果字符串是“end”,则关闭 FIFO 并结束进程。
如果发送的消息不是“end”,则等待来自客户端的消息(反转后的字符串)并打印反转后的字符串。
无限期地重复,直到用户输入字符串“end”。