一、管道
管道:管道是一種半雙工的通信方式,數據只能單方向流動,而且只能在具有親緣關系的進程間使用,因為管道
傳遞數據的單向性,管道又稱為半雙工管道。進程的親緣關系通常是指父子進程關系。
管道的特點決定了其使用的局限性:
- 數據只能由一個進程流向另一個進程(其中一個為寫管道,另一個為讀管道);如果要進行全雙工通信,需要
建立兩個管道。
- 管道只能用於父子進程或者兄弟進程間的通信,也就是說管道只能用於具有親緣關系的進程間的通信,無親緣
關系的進程不能使用管道。
管道的創建:
Linux下創建管道可以通過函數pipe來完成。該函數如果調用成功則返回0,並且數組中將包含兩個新的文件描述符;
如果有錯誤發生,返回-1,該函數返回兩個文件描述符:pipefd[0]和pipefd[1]。前者打開來讀,后者打開來寫。該函
數的原型為:
#include <fcntl.h>
#include <unistd.h>
int pipe(int pipefd[2]);
1. 下面是通過建立管道和創建父子進程間的通信:
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <fcntl.h> #include <string.h> #include <sys/wait.h> int main() { int fd[2]; pid_t pid; int ret; ret= pipe(fd); if(ret== -1) { perror("pipe.\n"); exit(1); } pid= fork(); if(pid== -1) { perror("fork.\n"); exit(1); } else if(pid== 0) { char buff[256]; close(fd[1]); read(fd[0],buff,256); printf("Form parent say: %s\n",buff); close(fd[0]); } else { char *say= "Hello Linux."; close(fd[0]); write(fd[1],say,strlen(say)+ 1); close(fd[1]); int status; wait(&status); } return 0; }
這是父子進程間利用管道進行通信的一個例子。
2. 下面是通過建立兩個管道來實現父子進程間全雙工通信:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <fcntl.h> #include <sys/wait.h> int main() { char *parent_talk[]= {"Hello","Can you tell me what time is it?","Ok,I must go,Bye",NULL}; char *child_talk[]= {"Hi","No problem","See you,Bye",NULL}; int fd1[2],fd2[2]; pid_t pid; int ret; ret= pipe(fd1); if(ret== -1) { perror("pipe1.\n"); exit(1); } ret= pipe(fd2); if(ret== -1) { perror("pipe2.\n"); exit(1); } pid= fork(); if(pid== -1) { perror("fork.\n"); exit(1); } else if(pid== 0) { char buff[256]; close(fd1[1]); close(fd2[0]); int i= 0; char *talk= child_talk[i]; while(talk!= NULL) { read(fd1[0],buff,256); printf("Parent say: %s\n",buff); write(fd2[1],talk,strlen(talk)+ 1); talk= child_talk[++i]; } close(fd1[0]); close(fd2[1]); } else { char buff[256]; close(fd1[0]); close(fd2[1]); int i= 0; char *talk= parent_talk[i]; while(talk!= NULL) { write(fd1[1],talk,strlen(talk)+ 1); read(fd2[0],buff,256); printf("Child say: %s\n",buff); talk= parent_talk[++i]; } close(fd1[1]); close(fd2[0]); int status; wait(&status); } return 0; }
這是通過建立兩個管道來實現父子進程間全雙工通信的例子。
二、有名管道
管道的有一個不足之處是沒有名字,因此,只能用於具有親緣關系的進程間通信,在有名管道(FIFO)提出后,該
限制得到了克服。FIFO不同於管道之處在於它提供一個路徑名與之關聯,以FIFO的文件形式存儲於文件系統中。有名
管道是一個設備文件,因此,即使進程與創建FIFO的進程不存在親緣關系,只要可以訪問該路徑,就能夠通過FIFO相
互通信。需要注意的是,FIFO總是按照先進先出的原則工作,第一個被寫入的數據將首先從管道中讀出。
有名管道的創建:
Linux下有兩種方式創建有名管道。一是在Shell下交互地建立一個有名管道,二是在程序中使用系統函數建立有
名管道。Shell方式下可使用mkfifo或mknod命令。創建有名管道的系統函數有兩個:mkfifo和mknod。兩個函數均定義
在頭文件sys/stat.h中,函數的原型為:
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname,mode_t mode);
int mknod(const char *pathname,mode_t mode,dev_t dev);
函數mkfifo參數中pathname為創建的有名管道的全路徑名;mode為創建的有名管道的模式,指明其存取權限;函
數mknod參數中pathname為創建的有名管道的全路徑名;mode為創建的有名管道的模式,指明其存取權限;dev為設
備值,該值取決於文件創建的種類,它只在創建設備文件時才會用到。這兩個函數調用成功都返回0,失敗都返回-1。
下面是使用有名管道建立一個客戶端/服務器的例子:
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <sys/wait.h> #define BUFFER_SIZE 256 const char *write_fifo= "write_fifo"; const char *read_fifo= "read_fifo"; int main() { if(access(write_fifo,F_OK)) { int ret= mkfifo(write_fifo,0755); if(ret== -1) { perror("mkfifo"); exit(1); } } int write_fd= open(write_fifo,O_WRONLY); if(write_fd== -1) { perror("open write_fifo."); exit(1); } int read_fd; while(1) { read_fd= open(read_fifo,O_RDONLY); if(read_fd== -1) { sleep(1); continue; } break; } char sendbuf[BUFFER_SIZE]; char recvbuf[BUFFER_SIZE]; while(1) { printf("Ser:"); scanf("%s",sendbuf); if(strcmp(sendbuf,"quit")== 0) { unlink(write_fifo); break; } write(write_fd,sendbuf,strlen(sendbuf)+ 1); read(read_fd,recvbuf,BUFFER_SIZE); printf("Cli:%s\n",recvbuf); } close(write_fd); close(read_fd); return 0; }
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <sys/wait.h> #define BUFFER_SIZE 256 const char *write_fifo= "write_fifo"; const char *read_fifo= "read_fifo"; int main() { int read_fd= open(write_fifo,O_RDONLY); if(read_fd== -1) { perror("open write_fifio."); exit(1); } if(access(read_fifo,F_OK)) { int ret= mkfifo(read_fifo,0755); if(ret== -1) { perror("mkfifo"); exit(1); } } int write_fd= open(read_fifo,O_WRONLY); if(write_fd== -1) { perror("open read_fifo."); exit(1); } char sendbuf[BUFFER_SIZE]; char recvbuf[BUFFER_SIZE]; while(1) { read(read_fd,recvbuf,BUFFER_SIZE); printf("Ser:%s\n",recvbuf); printf("Cli:"); scanf("%s",sendbuf); if(strcmp(sendbuf,"quit")== 0) { unlink(read_fifo); break; } write(write_fd,sendbuf,strlen(sendbuf)+ 1); } close(write_fd); close(read_fd); return 0; }
