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