管道没有名字,它的通信只限定于亲缘关系间的通信,有名管道实现了无亲缘关系间的通信,原理是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会阻塞到有客户进程往服务器管道中写如数据的时候,即实现了只要有客户进程访问数据服务器就会给出应答,否则一直等待。