管道和FIFO
- 管道(pipe)
1 #include <unistd.h> 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <sys/types.h> 5 #include <errno.h> 6 #include <string.h> 7 8 int main() 9 { 10 int fd[2]; 11 pid_t childpid; 12 char buf[100]; 13 14 memset(buf,0,100); 15 //创建一个管道 16 if(pipe(fd) == -1) 17 { 18 perror("pipe() error"); 19 exit(-1); 20 } 21 //创建一个子进程 22 childpid = fork(); 23 if(childpid == 0) 24 { 25 printf("server input a message : "); 26 scanf("%s",buf); 27 //关闭读端 28 close(fd[0]); 29 write(fd[1],buf,strlen(buf)); 30 exit(0); 31 } 32 if(childpid == -1) 33 { 34 perror("fork() error"); 35 exit(-1); 36 } 37 //父进程关闭写端 38 close(fd[1]); 39 read(fd[0],buf,100); 40 printf("client read a message: %s\n",buf); 41 waitpid(childpid,NULL,0); 42 return 0; 43 }
程序执行结果如下:
上面程序的细节问题在于子进程需要关闭读端,父进程需要关闭写端。因为管道最早提出时候是单向,虽然现在有些系统提供全双工的管道。那么如何采用管道实现双向通信呢?很显然我们需要两个管道,控制两个不同的数据流向。现在有模拟一个Client和Server双向通信的过程,Client与Server之间可以相互发送和接收信息。此时需要两个管道进行模拟,管道1模拟Server写Client读数据流向,管道2模拟Client写Server读数据流向。代码如下所示:
1 #include <unistd.h> 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <sys/types.h> 5 #include <errno.h> 6 #include <string.h> 7 8 int main() 9 { 10 int fd1[2],fd2[2]; 11 pid_t childpid; 12 char buf[100]; 13 14 memset(buf,0,100); 15 16 if(pipe(fd1) == -1) 17 { 18 perror("pipe() error"); 19 exit(-1); 20 } 21 if(pipe(fd2) == -1) 22 { 23 perror("pipe() error"); 24 exit(-1); 25 } 26 childpid = fork(); 27 if(childpid == 0) 28 { 29 printf("Server input a message : "); 30 gets(buf); 31 close(fd1[0]); 32 close(fd2[1]); 33 write(fd1[1],buf,strlen(buf)); 34 read(fd2[0],buf,100); 35 printf("Server received message from client:%s\n",buf); 36 exit(0); 37 } 38 if(childpid == -1) 39 { 40 perror("fork() error"); 41 exit(-1); 42 } 43 close(fd1[1]); 44 close(fd2[0]); 45 read(fd1[0],buf,100); 46 printf("Client receive a message from server: %s\n",buf); 47 printf("Client input a message : "); 48 gets(buf); 49 write(fd2[1],buf,strlen(buf)); 50 waitpid(childpid,NULL,0); 51 return 0; 52 }
程序执行结果如下:
2 FIFO(first in first out)
FIFO又名有名管道,相对于上述管道而言。管道没有名字,因此只能在具有共同祖先进程的各个进程之间通信,无法在无亲缘关系的两个进程之间创建一个管道进行通信。为此有了FIFO,类似管道,也是一个单向(半双工)数据流,每个FIFO有一个路径名与之关联,从而允许无亲缘关系的进程访问同一个FIFO。FIFO有mkfifo函数创建。
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname,mode_t mode); 成功返回0,出错返回-1。pathname是一个普通的路径名,是FIFO的名字,mode指定文件的权位。
在创建FIFO后,必须打开来读或者打开来写,不能打开来既读既写(因为FIFO是半双工)。现在采用FIFO实现上面的第二个例子,代码如下:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <unistd.h> 4 #include <sys/types.h> 5 #include <sys/stat.h> 6 #include <errno.h> 7 #include <string.h> 8 #include <fcntl.h> 9 10 #define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) 11 12 #define FIFO1 "/tmp/fifo.1" 13 #define FIFO2 "/tmp/fifo.2" 14 15 int main() 16 { 17 int readfd,writefd; 18 pid_t childpid; 19 char buf[100]; 20 memset(buf,0,100); 21 //创建FIFO 22 if((mkfifo(FIFO1,FILE_MODE) < 0) && (errno != EEXIST)) 23 { 24 perror("mkfifo() error"); 25 exit(-1); 26 } 27 if((mkfifo(FIFO2,FILE_MODE) < 0) && (errno != EEXIST)) 28 { 29 unlink(FIFO1); 30 perror("mkfifo() error"); 31 exit(-1); 32 } 33 //创建子进程 34 childpid = fork(); 35 if(childpid == 0) 36 { 37 readfd = open(FIFO1,O_RDONLY,0); 38 writefd = open(FIFO2,O_WRONLY,0); 39 printf("Server input a message: "); 40 gets(buf); 41 write(writefd,buf,strlen(buf)); 42 read(readfd,buf,100); 43 printf("Server received a message from Client: %s\n",buf); 44 exit(0); 45 } 46 if(childpid == -1) 47 { 48 perror("frok() error"); 49 exit(-1); 50 } 51 //防止死锁,注意顺序 52 writefd = open(FIFO1,O_WRONLY,0); 53 readfd = open(FIFO2,O_RDONLY,0); 54 read(readfd,buf,100); 55 printf("Client received a message form Server: %s\n",buf); 56 printf("Client input a mesage: "); 57 gets(buf); 58 write(writefd,buf,strlen(buf)); 59 waitpid(childpid,NULL,0); 60 close(readfd); 61 close(writefd); 62 unlink(FIFO1); 63 unlink(FIFO2); 64 return 0; 65 }
运行结果如下:
上面的程序当中父进程打开FIFO的顺序不能颠倒,否则会造成死锁。因为在当前没有任何进程打开某个FIFO来写的时候,打开该FIFO来读的进程将会阻塞。交换父进程中两个open的调用顺序后,父子进程都将打开同一个FIFO进行读,而当前没有任何进程来打开该文件进行写,于是父子进程都阻塞,造成死锁。
下面采用FIFO实现无亲缘关系的两个进程之间的通信。Client与Server是两个独立的进程。
1 //公共同文件fifo.h 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <unistd.h> 5 #include <sys/types.h> 6 #include <sys/stat.h> 7 #include <errno.h> 8 #include <string.h> 9 #include <fcntl.h> 10 11 #define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) 12 13 #define FIFO1 "/tmp/fifo.1" 14 #define FIFO2 "/tmp/fifo.2"
1 //server进程 server.c 2 #include "fifo.h" 3 4 int main() 5 { 6 int readfd,writefd; 7 pid_t childpid; 8 char buf[100]; 9 memset(buf,0,100); 10 //创建FIFO 11 if((mkfifo(FIFO1,FILE_MODE) < 0) && (errno != EEXIST)) 12 { 13 perror("mkfifo() error"); 14 exit(-1); 15 } 16 if((mkfifo(FIFO2,FILE_MODE) < 0) && (errno != EEXIST)) 17 { 18 unlink(FIFO1); 19 perror("mkfifo() error"); 20 exit(-1); 21 } 22 readfd = open(FIFO1,O_RDONLY,0); 23 writefd = open(FIFO2,O_WRONLY,0); 24 printf("Server input a message: "); 25 gets(buf); 26 write(writefd,buf,strlen(buf)); 27 read(readfd,buf,100); 28 printf("Server received a message from Client: %s\n",buf); 29 return 0; 30 }
1 //client进程 client。c 2 #include "fifo.h" 3 4 int main() 5 { 6 int readfd,writefd; 7 pid_t childpid; 8 char buf[100]; 9 memset(buf,0,100); 10 //创建FIFO 11 if((mkfifo(FIFO1,FILE_MODE) < 0) && (errno != EEXIST)) 12 { 13 perror("mkfifo() error"); 14 exit(-1); 15 } 16 if((mkfifo(FIFO2,FILE_MODE) < 0) && (errno != EEXIST)) 17 { 18 unlink(FIFO1); 19 perror("mkfifo() error"); 20 exit(-1); 21 } 22 23 //防止死锁,注意顺序 24 writefd = open(FIFO1,O_WRONLY,0); 25 readfd = open(FIFO2,O_RDONLY,0); 26 read(readfd,buf,100); 27 printf("Client received a message form Server: %s\n",buf); 28 printf("Client input a mesage: "); 29 gets(buf); 30 write(writefd,buf,strlen(buf)); 31 close(readfd); 32 close(writefd); 33 unlink(FIFO1); 34 unlink(FIFO2); 35 return 0; 36 }
先执行server进程,然后执行client进程:结果如下:
以上介绍了管道和FIFO的操作方法。
参考资料:
《Unix环境高级编程》