管道沒有名字,它的通信只限定於親緣關系間的通信,有名管道實現了無親緣關系間的通信,原理是fifo提供了一個路徑名與之關聯,讓fifo的文件存於系統中,只要知道該文件路徑,就可以進行訪問。fifo指代(fist in, fist out),即按照先進先出的工作。
- fifo 創建
#include <sys/types.h> #include <sys/stat.h> int mkfifo(const char * pathname, mode_t mode);
參數:
pathname 為路徑名,創建管道的名字
mode 為創建fifo的權限
例1,該程序讓子進程執行了ls-l命令,並將執行結果寫入有名管道,父進程從該管道中讀出執行結果,輸出到屏幕,最后刪除該有名管道的文件
#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <fcntl.h> #include <string.h> #include <sys/stat.h> #include <errno.h> #include <unistd.h> #define FIFO2 "/tmp/fifo.2" int main(int argc, int ** argv) { int readfd,writefd; pid_t pid; char buf[1001]; //建立有名管道 S_IFIFO|0666指名創建有名管道且存取權限為0666,即創建者/其同組用戶/其他用戶均可讀可寫 if(mkfifo(FIFO2, S_IFIFO|0666)< 0) { printf("mkfifo2 error"); exit(1); } if((pid = fork()) == 0) //創建進程 子進程寫 { writefd = open(FIFO2, O_WRONLY); //打開管道以寫方式 dup2(1, writefd); //定向輸出的東西到管道輸入 execlp("ls", "ls", "-l", NULL);//子進程執行ls -l 命令, 即執行結果到有名管道中 } readfd = open(FIFO2, O_RDONLY);//打開管道以讀方式 while(read(readfd, buf, 1000) != 0) { printf("%s", buf); //讀出管道東西到屏幕 } waitpid(pid, NULL, 0);//等待子進程結束 close(readfd); close(writefd); unlink(FIFO2);//刪除FIFO2文件 exit(0); } 注:有名管道需要用open函數打開以后使用,如果以讀方式打開,會阻塞到有寫方打開管道,同樣以寫方式打開會阻塞到以讀方式打開,若同時打開讀寫,則不會阻塞,在該程序中一定會先執行子進程,因為父進程讀管道時,管道中沒有數據,也會阻塞read到有write寫入到管道中
例2,上一個程序是親緣關系間的通信,該程序是無親緣關系間的通信,該程序只寫了一個服務器端,寫了shell與其通信,題目是shell腳本中的客戶進程請求服務器返回指定一個文件的內容,請求格式為進程id、空格、路徑名、換行符構成,該程序類型為多個客戶,一個服務器
#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <fcntl.h> #include <string.h> #include <sys/stat.h> #include <errno.h> #include <unistd.h> #define FIFO "/tmp/fifo.serv" #define MAXLINE 80 int main(int argc, int ** argv) { int readfd,writefd,fd; char *ptr, buff[MAXLINE], fifoname[MAXLINE]; pid_t pid; ssize_t n; if(mkfifo(FIFO,S_IFIFO|0666 )< 0)//創建管道 { printf("mkfifo error"); exit(1); } readfd = open(FIFO, O_RDONLY); //以不阻塞的方式打開服務器管道, writefd = open(FIFO, O_WRONLY); while ((n = read(readfd, buff, MAXLINE)) > 0) //讀服務器管道內容,讀出客戶請求 { if(buff[n-1] == '\n') //去掉換行符 { n--; } buff[n] = 0; if((ptr = strchr(buff, ' ')) == NULL) //定位空格 { printf("pathname :%s", buff); continue; } *ptr++ = 0; printf("ptr = %s\n", ptr); //打印客戶要求輸出的文件名 pid = atol(buff); //取出進程id snprintf(fifoname ,sizeof(fifoname), "/tmp/fifo.%ld", (long)pid); //組織客戶管道名稱 printf("fifoname = %s\n", fifoname); //輸出客戶管道名稱 if((writefd = open (fifoname, O_WRONLY, 0)) < 0) //打開客戶管道,以寫方式打開 { printf("can't open %s\n", fifoname); continue; } if((fd = open(ptr, O_RDONLY)) < 0) //打開客戶請求的文件 { snprintf(buff+n, sizeof(buff) - n, ":can't open, %s\n", strerror(errno)); n = strlen(ptr); write(writefd, ptr, n); close(writefd); } else { while((n = read(fd, buff, MAXLINE)) > 0) //將客戶請求的文件寫入到客戶管道中 write(writefd, buff, n); close(fd); close(writefd); } } return 0; }
shell交互內容,我本目錄下寫了一個test.c文件,
pid=$$ 獲取本進程id
mkfifo /tmp/fifo.$pid 創建客戶管道
echo "$pid test.c" > /tmp/fifo.serv 將請求寫入服務器管道
cat < /tmp/fifo.$pid 輸出服務器返回給客戶管道內容
rm /tmp/fifo.$pid 刪除客戶管道
該程序中因為while循環中的read會阻塞到有客戶進程往服務器管道中寫如數據的時候,即實現了只要有客戶進程訪問數據服務器就會給出應答,否則一直等待。