管道和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環境高級編程》
管道和FIFO