- 管道(pipe)
1 #include2 #include 3 #include 4 #include 5 #include 6 #include 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 #include2 #include 3 #include 4 #include 5 #include 6 #include 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 #include2 #include 3 #include 4 #include 5 #include 6 #include 7 #include 8 #include 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 //创建FIFO22 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 #include3 #include 4 #include 5 #include 6 #include 7 #include 8 #include 9 #include 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 //创建FIFO11 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 //创建FIFO11 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环境高级编程》