管道(pipe)应用的一大局限是没有名字,只能用于具有亲缘关系进程之间的通信。而命名管道,也称FIFO,实质是一种文件类型,通过FIFO可以用于任何两个进程间的通信。
命名管道的创建
命令方式
在shell中可以使用mkfifo
命令创建一个命名管道,格式为:
其中option选项用于选择创建FIFO的模式,使用形式为-m mode
,mode为八进制模式,创建示例:
创建之后可以在当前文件间看到新建的文件。
函数方式
FIFO管道可通过mkfifo()函数创建,函数原型为:
1 2 3
| #include <sys/types.h> #include <sys/stat.h> int mkfifo(const char *pathname, mode_t mode);
|
创建成功返回0,出错返回1。函数第一个参数为普通的路径名,即创建后FIFO文件的名字,第二个参数与打开普通文件的open函数中的mode参数相同。
如果要创建的FIFO文件已经存在,则会返回EEXIST错误,因此在创建前应先检查是否创建成功,若文件已存在,只要调用打开FIFO的函数即可。
编程示例
创建一个命名管道,create_FIFO.c:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| #include <sys/types.h> #include <sys/stat.h> #include <errno.h> #include <stdio.h> #include <stdlib.h>
int main(int argc, char *argv[]) { mode_t mode = 0666; if(argc != 2) { printf("USEMSG:create_FIFO{FIFO name}\n"); exit(1); }
if((mkfifo(argv[1], mode))<0) { perror("failed to mkfifo!\n"); exit(1); } else { printf("you successfully create a FIFO name is: %s\n", argv[1]); } return 0; }
|
编译执行:
1 2
| $ ./create_FIFO testFIFO you successfully create a FIFO name is: testFIFO
|
上述程序使用mkfifo函数创建了一个名为“testFIFO”的命名管道。
此时再次执行:
1 2 3
| C$ ./create_FIFO testFIFO failed to mkfifo! : File exists
|
由于要创建的FIFO已经存在,再次创建会提示创建失败。
命名管道的读写
一般的文件I/O函数均可用于FIFO操作,如open、close、read、write等,若要删除一个命名管道,则使用系统调用unlink。
编程示例
FIFO写入
向FIFO写入数据,write_fifo.c:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| #include <sys/types.h> #include <sys/stat.h> #include <errno.h> #include <stdio.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> #include <limits.h> #include <time.h> #define BUFES PIPE_BUF
int main(void) { int fd; int n,i; char buf[BUFES]; time_t tp; char fifo_name[]="fifo1";
printf("My process ID: %d\n", getpid());
if(mkfifo(fifo_name, 0666) < 0) { printf("mkfifo failed, maybe there exist a same fifo file\n"); } else printf("mkfifo ok\n");
printf("...\n");
if((fd=open(fifo_name, O_WRONLY))<0) { printf("Open failed!\n"); exit(1); } else printf("Open fifo ok!\n");
for(i=0;i<5;i++) { time(&tp); n=sprintf(buf,"write_fifo %d seconds %s", getpid(), ctime(&tp)); printf("Send msg: %s", buf); if((write(fd, buf, n+1)) < 0) { printf("Write failed!\n"); close(fd); exit(1); } sleep(2); }
close(fd);
unlink(fifo_name);
exit(0); }
|
该程序首先创建一个名为fifo1的FIFO,如果已存在则直接调用open()函数打开,之后通过write()函数写入当前的时间内容到FIFO,最后使用close()函数关闭FIFO,并用unlink(函数删除FIFO。
FIFO读取
从FIFO读取数据 ,read_fifo.c:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <limits.h> #include <fcntl.h> #include <unistd.h> #define BUFES PIPE_BUF
int main(void) { int fd; int len; char buf[BUFES]; mode_t mode = 0666;
printf("My process ID:%d\n", getpid()); if((fd=open("fifo1", O_RDONLY))<0) { printf("Open failed!\n"); exit(1); }
while((len=read(fd, buf, BUFES))>0) printf("Read_fifo read:%s", buf);
close(fd);
exit(0); }
|
该程序首先调用open()函数打开FIFO,之后通过read()函数循环读取FIFO的内容,每次读取PIPE_BUF个字节,最后使用close()函数关闭FIFO。
测试
分别编译上述两个程序。测试之前先使用mkfifo命令创建一个名为fifo1的命名管道:
然后打开两个shell窗口,依次运行write_fifo和read_fifo两个程序。
其中一个shell中运行写入程序:
1 2 3 4 5 6 7 8 9 10 11
| $ ./write_fifo My process ID: 2278 mkfifo ok ... Open fifo ok! Send msg: write_fifo 2278 seconds Tue Nov 26 09:49:09 2019 Send msg: write_fifo 2278 seconds Tue Nov 26 09:49:11 2019 Send msg: write_fifo 2278 seconds Tue Nov 26 09:49:13 2019 Send msg: write_fifo 2278 seconds Tue Nov 26 09:49:15 2019 Send msg: write_fifo 2278 seconds Tue Nov 26 09:49:17 2019
|
另一个shell中运行读出程序:
1 2 3 4 5 6 7 8
| $ ./read_fifo My process ID:2282 Read_fifo read:write_fifo 2278 seconds Tue Nov 26 09:49:09 2019 Read_fifo read:write_fifo 2278 seconds Tue Nov 26 09:49:11 2019 Read_fifo read:write_fifo 2278 seconds Tue Nov 26 09:49:13 2019 Read_fifo read:write_fifo 2278 seconds Tue Nov 26 09:49:15 2019 Read_fifo read:write_fifo 2278 seconds Tue Nov 26 09:49:17 2019
|
首先运行的write_fifo程序在执行到FIFO的open时会进入等待,直到read_fifo进程也执行的到open时,两进程间的FIFO通信开始。
参考:《精通Linux C编程》- 程国钢