AI智能
改变未来

APUE——IPC_管道


1. 匿名管道

1.1 匿名管道原理

pipe()创建管道,可以使用的单向数据通道 用于进程间通信。数组pipefd用于返回引用管道末端的两个文件描述符。 pipefd [0] 是指管道的读取端。 pipefd [1]是指写管道的末端。写入管道写入端的数据是由内核缓冲,直到从读取端读取管道。
pipe函数

#include <unistd.h>int pipe(int pipefd[2]);#define _GNU_SOURCE             /* See feature_test_macros(7) */#include <fcntl.h>              /* Obtain O_* constant definitions */#include <unistd.h>int pipe2(int pipefd[2], int flags);


fork的时候,两个PCB有两个fd_array,复制一份后,指向同一个file,然后指向同一块内存,pipe应该原理类似,指向同一个内核缓存

1.2 管道指令使用原理

管道指令的使用(过滤)

ls | grep y


执行顺序

  1. int pipe(int fildes[2]); 创建匿名管道,fork
  2. int dup2(int fildes, int fildes2); 将write文件描述符指针复制到STDOUT_FILENO
  3. char *getcwd(char *buf, size_t size);获取当前路径, 搭配glob或者readdir函数,获取当前目录所有文件名
  4. 第三步将数据写入STDOUT_FILENO,实际写入管道所指向FILE
  5. 子进程从管道中读取所有数据,可用glob函数,提取出过滤完的数据
  6. 将数据打印到STDOUT_FILENO

1.3 匿名管道读写规则

  1. 当没有数据可读时
    a. O_NONBLOCK disable时:read阻塞,等到数据到来
    b. O_NONBLOCK enable时:read返回-1,errno值为EAGAIN
  2. 如果管道write端对应fd被关闭,则read返回0(读到文件尾)
  3. 如果管道read端对应fd被关闭,则write产生SIGPIPE
  4. 如果写入数据量不大于PIPE_BUF(4k),保证原子性,否则不行
  5. 管道内核缓存buf大小为65535

1.4 例子分析

1.4.1 模拟管道ls -l | wc -c

#include <stdio.h>#include <stdlib.h>#include <signal.h>#include <unistd.h>#include <sys/select.h>/* According to earlier standards */#include <sys/time.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>int main(){int rp,pid;int pipefd[2];char recfbuf[20] = \"helloworld\";struct sigaction siga,oldsiga;char writebuf[1024];siga.sa_handler = SIG_IGN;siga.sa_flags = SA_NOCLDWAIT;sigemptyset(&siga.sa_mask);sigaction(SIGCHLD,&siga,&oldsiga);void fun(int i){puts(\"no read\\n\");}signal(SIGPIPE,fun);rp = pipe(pipefd);pid = fork();if(pid<0){perror(\"fork\");exit(1);}if(pid == 0){close(pipefd[0]);dup2(pipefd[1],STDOUT_FILENO);execlp(\"ls\",\"ls\",\"-l\",NULL);close(pipefd[1]);exit(0);}// close(pipefd[0]);close(pipefd[1]);dup2(pipefd[0],STDIN_FILENO);execlp(\"wc\",\"wc\",\"-c\",NULL);// open(\"890.c\",O_WRONLY|O_CREAT|O_TRUNC,0644);close(pipefd[0]);puts(\"end\");exit(0);}
xili271948@er04180p:~/tcp$ ./7892452xili271948@er04180p:~/tcp$ ls -l | wc -c2452

1.4.2 非阻塞read,通过管道发送文件

#include <stdio.h>#include <stdlib.h>#include <signal.h>#include <unistd.h>#include <sys/select.h>#include <errno.h>/* According to earlier standards */#include <sys/time.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <string.h>int main(){int rp,pid,fd,num,numr,frd,rflags;int pipefd[2];struct sigaction siga,oldsiga;char writebuf[1024];char readbuf[1024];siga.sa_handler = SIG_IGN;siga.sa_flags = SA_NOCLDWAIT;sigemptyset(&siga.sa_mask);sigaction(SIGCHLD,&siga,&oldsiga);rp = pipe(pipefd);rflags = fcntl(pipefd[0],F_GETFL);fcntl(pipefd[0],F_SETFL,rflags|O_NONBLOCK);pid = fork();if(pid<0){perror(\"fork\");exit(1);}if(pid == 0){//sleep(2);close(pipefd[0]);fd = open(\"123.c\",O_RDONLY);while(1){num = read(fd,writebuf,1024);if(num <0){perror(\"read\");exit(1);}else if(num > 0)write(pipefd[1],writebuf,num);elsebreak;}puts(\"end of send\");close(pipefd[1]);close(fd);exit(0);}else{//sleep(1);frd = open(\"456.c\",O_WRONLY|O_CREAT|O_TRUNC,0644);close(pipefd[1]);numr = 1;while(numr){numr = read(pipefd[0],readbuf,num);if(numr<0){puts(strerror(errno));}write(frd,readbuf,numr);}puts(\"end of recv\");close(pipefd[0]);close(frd);}exit(0);}
xili271948@er04180p:~/tcp$ ./noblockpipeResource temporarily unavailableResource temporarily unavailableResource temporarily unavailableResource temporarily unavailableResource temporarily unavailableend of sendResource temporarily unavailableend of recv

1.4.3 测试read已关闭,write发送SIGPIPE

#include <stdio.h>#include <stdlib.h>#include <signal.h>#include <unistd.h>#include <sys/select.h>/* According to earlier standards */#include <sys/time.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>int main(){int rp,pid;int pipefd[2];char recfbuf[20] = \"helloworld\";struct sigaction siga,oldsiga;char writebuf[1024];siga.sa_handler = SIG_IGN;siga.sa_flags = SA_NOCLDWAIT;sigemptyset(&siga.sa_mask);sigaction(SIGCHLD,&siga,&oldsiga);void fun(int i){puts(\"no read\\n\");}signal(SIGPIPE,fun);rp = pipe(pipefd);pid = fork();if(pid<0){perror(\"fork\");exit(1);}if(pid == 0){close(pipefd[0]);write(pipefd[1],recfbuf,sizeof(recfbuf));puts(recfbuf);close(pipefd[1]);exit(0);}close(pipefd[0]);close(pipefd[1]);puts(\"end\");exit(0);}
xili271948@er04180p:~/tcp$ ./pip_sigpipeendno readhelloworld

2. 命名管道FIFO

2.1 FIFO原理

#include <sys/types.h>#include <sys/stat.h>int mkfifo(const char *pathname, mode_t mode);#include <fcntl.h>           /* Definition of AT_* constants */#include <sys/stat.h>int mkfifoat(int dirfd, const char *pathname, mode_t mode);filname是指文件名,而mode是指定文件的读写权限。mknod是比较老的函数,而使用mkfifo函数更加简单和规范,所以建议用mkfifo。
  1. FIFO其适用于进程间通信,其可以用于父子进程通信
  2. FIFO由mkfifo函数创建,并需要使用open函数打开,除了创建方式不同于匿名管道的pipe()之外,其他基本一致
  3. FIFO实际创建了pipe类型的文件,有inode,但是没有block,所以没有所谓的临时文件,匿名管道没有pipe类型文件
open(const char *path, O_RDONLY);//1open(const char *path, O_RDONLY | O_NONBLOCK);//2open(const char *path, O_WRONLY);//3open(const char *path, O_WRONLY | O_NONBLOCK);//4

需要搭配O_RDONLY或者O_WRONLY使用,因为管道为单向
同时,mkfifo()创建的为pipe文件,通过ls -l或者stat查看,其如下,不能使用vim cat等查看

2.2 FIFO读写规则

  1. 如果是为读而open(与pipe不同,pipe考虑的是read write)
    a. O_NONBLOCK disable时:open阻塞,知道有进程是为写open该FIFO
    b. O_NONBLOCK enable时:open立即返回
  2. 如果是为写而open
    a. O_NONBLOCK disable时:open阻塞,知道有进程是为读open该FIFO
    b. O_NONBLOCK enable时:open立即返回,错误码为ENXIO,即打开fd失败
  3. 如果管道write端对应fd被关闭,则read返回0(读到文件尾)
  4. 如果管道read端对应fd被关闭,则write产生SIGPIPE

2.3 FIFO代码

2.3.1 普通fifo读写

fifo读

#include <stdio.h>#include <stdlib.h>#include <sys/types.h>#include <sys/stat.h>#include <unistd.h>#include <fcntl.h>#define BUFFSIZE 50int main(){char rdbuf[BUFFSIZE];int mk,fo;fo = open(\"mkdfifo\",O_RDONLY);int nw;nw = read(fo,rdbuf,BUFFSIZE);if(nw < 0){perror(\"read\\n\");exit(1);}puts(\"read all\");puts(rdbuf);exit(0);}

fifo写

#include <stdio.h>#include <stdlib.h>#include <sys/types.h>#include <sys/stat.h>#include <unistd.h>#include <fcntl.h>#define BUFFSIZE 50int main(){int mk,fo;mk = mkfifo(\"./mkdfifo\",0600);fo = open(\"mkdfifo\",O_WRONLY);char *wrbuf = \"helloworld\";int nw;nw = write(fo,wrbuf,BUFFSIZE);puts(\"write all\\n\");if(nw < 0){perror(\"write\\n\");exit(1);}puts(\"send all\");exit(0);}
xili271948@er04180p:~/tcp$ ./wfifowrite allsend allxili271948@er04180p:~/tcp$ ./rfiforead allhelloworld

2.3.2 非阻塞fifo读写

#include <stdio.h>#include <stdlib.h>#include <sys/types.h>#include <sys/stat.h>#include <unistd.h>#include <fcntl.h>#include <signal.h>#define BUFFSIZE 50int main(){char rdbuf[BUFFSIZE];int mk,fo;fo = open(\"mkdfifo\",O_RDONLY|O_NONBLOCK);int nw;nw = read(fo,rdbuf,BUFFSIZE);if(nw < 0){perror(\"read\\n\");exit(1);}puts(\"read all\");puts(rdbuf);exit(0);}

#include <stdio.h>#include <stdlib.h>#include <sys/types.h>#include <sys/stat.h>#include <unistd.h>#include <fcntl.h>#include <signal.h>#define BUFFSIZE 50void fun(int argc){puts(\"SIGPIPE\");}int main(){int mk,fo;signal(SIGPIPE,fun);//mk = mkfifo(\"./mkdfifo\",0600);fo = open(\"mkdfifo\",O_WRONLY|O_NONBLOCK);if(fo < 0){perror(\"open\");exit(1);}char *wrbuf = \"helloworld\\n\";sleep(10);int nw;nw = write(fo,wrbuf,BUFFSIZE);puts(\"write all\");if(nw < 0){perror(\"write\\n\");exit(1);}puts(\"send all\");exit(0);}

父子进程之间pipe

#include <stdio.h>#include <stdlib.h>#include <sys/types.h>#include <sys/stat.h>#include <unistd.h>#include <fcntl.h>#include <string.h>#define BUFFSIZE 50int main(){char rdbuf[BUFFSIZE];char* wrbuf = \"helloworld\";int mk,pid;mk = mkfifo(\"./testmkfifo\",0600);int nw;pid = fork();if(pid == 0){int fr;fr = open(\"testmkfifo\",O_WRONLY);write(fr,wrbuf,strlen(wrbuf)+1);exit(0);}else{int fw;fw = open(\"testmkfifo\",O_RDONLY);nw = read(fw,rdbuf,BUFFSIZE);if(nw < 0){perror(\"read\\n\");exit(1);}}puts(\"read all\");puts(rdbuf);exit(0);}
xili271948@er04180p:~/tcp$ ./fifotestread allhelloworld
赞(0) 打赏
未经允许不得转载:爱站程序员基地 » APUE——IPC_管道