使用 mkfifo
创建一个 FIFO
特殊文件,可用于进程间通信。这种特殊文件,不占磁盘空间,但是可以像普通文件一样读写。
mkfifo 函数简介
mkfifo 的原型和所需头文件如下:
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
mkfifo 的返回值
成功返回 0,失败返回 -1,失败码可从 errno
查询。
mkfifo 功能
mkfifo()
函数创建一个名为 pathname 的 FIFO
特殊文件,mode 参数用于指定权限。创建的 FIFO
特殊文件与管道是类似的,都可以用于进程间通信。这种特殊的FIFO
文件可以被文件系统加载,因此可以像普通文件一样读写和删除。
使用mkfifo
函数创建了FIFO
特殊文件后,任何进程都可以像普通文件一样打开之,并读写。通常,读取FIFO
特殊文件会被阻塞,直到有进程写入数据到FIFO
文件。
mkfifo 函数的简单使用 demo
以下的 demo,首先创建了一个FIFO
特殊文件,然后 fork
出一个子进程。父进程延时1秒后,往FIFO
文件写入 "https://blog.popkx.com\n",然后使用waitpid
函数等待子进程结束。子进程则从FIFO
文件读取数据,并且将读出的数据打印到终端。
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#define FIFO "blog_popkx_com"
int main()
{
char buffer[80];
int fd;
char s[] = "https://blog.popkx.com\n";
unlink(FIFO);
mkfifo(FIFO, 0666);
switch(fork())
{
case -1:
printf("fork failed\n");
break;
case 0:
fd = open(FIFO, O_RDONLY);
int len = read(fd, buffer, 80);
printf("\nlen: %d, %s\n", len, buffer);
close(fd);
break;
default:
sleep(1);
fd = open(FIFO, O_WRONLY);
write(fd, s, sizeof(s));
close(fd);
int childRet = 0;
waitpid(-1, &childRet, 0);
unlink(FIFO);
break;
}
return 0;
}
编译之,执行结果如下:
$ gcc fifo.c
$ ./a.out
len: 24, https://blog.popkx.com
子进程的读取操作被阻塞,直到父进程往FIFO
特殊文件写入数据,子进程才从中读出数据,并且打印到终端。
对 FIFO 特殊文件的进一步测试
在子进程中加入延时,如下:
...
switch(fork())
{
case -1:
printf("fork failed\n");
break;
case 0:
sleep(10); // 加入适当延时,便于其他测试
fd = open(FIFO, O_RDONLY);
int len = read(fd, buffer, 80);
printf("\nlen: %d, %s\n", len, buffer);
...
这样一来,就会有一段时间FIFO
文件里有父进程写入的数据,而子进程还没有将之读出。我们在这段时间里,先查看其占用磁盘空间:
$ ls -ahl
total 24K
drwxrwxr-x 2 lcc lcc 4.0K 10月 18 19:50 .
drwxrwxr-x 14 lcc lcc 4.0K 10月 18 18:50 ..
-rwxrwxr-x 1 lcc lcc 9.0K 10月 18 19:50 a.out
prw-rw-r-- 1 lcc lcc 0 10月 18 19:50 blog_popkx_com # 占用磁盘空间为 0
-rw-rw-r-- 1 lcc lcc 754 10月 18 19:50 fifo.c
$
$
$ cat blog_popkx_com
https://blog.popkx.com
但是可以使用 cat
命令将FIFO
文件中的内容读出。接着,发现进程一直阻塞,这是因为FIFO
文件中的内容为空了,子进程的读取操作会一直阻塞到别的进程再往FIFO
文件写入内容,才会返回,并且将其中的内容读出。而父进程则会一直 wait 到子进程返回。
$ ps -a|grep a.out
59923 pts/19 00:00:00 a.out
59924 pts/19 00:00:00 a.out