首页 > 代码库 > Linux管道(匿名PIPE)
Linux管道(匿名PIPE)
管道基本概念
管道是Unix中最古老的进程间通信的形式。
我们把从一个进程连接到另一个进程的一个数据流称为一个“管道”
如:ps aux | grep httpd | awk ‘{print $2}‘
管道示意图
管道的本质
固定大小的内核缓冲区
管道限制
1)管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道;
2)匿名管道只能用于具有共同祖先的进程(如父进程与fork出的子进程)之间进行通信;[通常,一个管道由一个进程创建,然后该进程调用fork,此后父子进程共享该管道]
匿名管道pipe
SYNOPSIS #include <unistd.h> int pipe(int pipefd[2]);
功能
创建无名管道
参数
Pipefd:文件描述符数组,其中pipefd[0]表示读端,pipefd[1]表示写端
管道创建示意图
//自己实现管道 void err_exit(string str); int main() { int pipefd[2]; if (pipe(pipefd) == -1) err_exit("pipe error"); pid_t pid; if ((pid = fork()) < 0) err_exit("fork error"); if (pid == 0) //In Child, Write pipe { close(pipefd[0]); //使得STDOUT_FILENO也指向pipefd[1],亦即ls命令的输出将打印到管道中 dup2(pipefd[1],STDOUT_FILENO); //此时可以关闭管道写端 close(pipefd[1]); execlp("/bin/ls","/bin/ls",NULL); //如果进程映像替换失败,则打印下面出错信息 fprintf(stderr,"Child: execlp error"); exit(0); } //In Parent close(pipefd[1]); //使得STDIN_FILENO也指向pipefd[2],亦即wc命令将从管道中读取输入 dup2(pipefd[0],STDIN_FILENO); //此时可以关闭管道读端 close(pipefd[0]); execlp("/usr/bin/wc","/usr/bin/wc","-w",NULL); //如果进程映像替换失败,则打印下面出错信息 fprintf(stderr,"Parent: execlp error"); return 0; } void err_exit(string str) { perror(str.c_str()); exit(EXIT_FAILURE); }
示例:管道编程实践
void err_exit(string str); int main() { int pipefd[2]; int ret; if ((ret = pipe(pipefd)) != 0) { err_exit("pipe error"); } pid_t pid = fork(); if (pid == -1) { err_exit("fork error"); } if (pid == 0) //In Child, Write pipe { close(pipefd[0]); //Close Read pipe string str("I Can Write Pipe from Child!"); write(pipefd[1],str.c_str(),str.size()); //Write to pipe close(pipefd[1]); exit(0); } //In Parent, Read pipe close(pipefd[1]); //Close Write pipe char buf[1024]; memset(buf,0,sizeof(buf)); read(pipefd[0],buf,sizeof(buf)); //Read from pipe cout << "Read from pipe: " << buf << endl; close(pipefd[0]); return 0; } void err_exit(string str) { perror(str.c_str()); exit(EXIT_FAILURE); }
匿名管道读写规则
1)管道空时
O_NONBLOCK disable:read调用阻塞,即进程暂停执行,一直等到有数据来到为止。
O_NONBLOCK enable:read调用返回-1,errno值为EAGAIN。
2)管道满时
O_NONBLOCK disable: write调用阻塞,直到有进程读走数据
O_NONBLOCK enable:调用返回-1,errno值为EAGAIN
3)管道不停被写,写满
O_NONBLOCK disable: write调用阻塞(Block)
O_NONBLOCK enable:调用返回-1,errno值为EAGAIN
//示例:设置父进程Unblock读PIPE int main() { int pipefd[2]; int ret; if ((ret = pipe(pipefd)) != 0) { err_exit("pipe error"); } pid_t pid = fork(); if (pid == -1) { err_exit("fork error"); } if (pid == 0) //In Child, Write pipe { sleep(10); close(pipefd[0]); //Close Read pipe string str("I Can Write Pipe from Child!"); write(pipefd[1],str.c_str(),str.size()); //Write to pipe close(pipefd[1]); exit(0); } //In Parent, Read pipe close(pipefd[1]); //Close Write pipe char buf[1024]; memset(buf,0,sizeof(buf)); //Set Read pipefd UnBlock! int flags = fcntl(pipefd[0],F_GETFL); flags |= O_NONBLOCK; ret = fcntl(pipefd[0],F_SETFL,flags); if (ret != 0) { err_exit("Set UnBlock error"); } int readCount = read(pipefd[0],buf,sizeof(buf)); //Read from pipe if (readCount < 0) { //read立刻返回,不再等待子进程发送数据 err_exit("read error"); } cout << "Read from pipe: " << buf << endl; close(pipefd[0]); return 0; }
4)如果所有管道写端对应的文件描述符被关闭,则read返回0
int main() { int pipefd[2]; int ret; if ((ret = pipe(pipefd)) != 0) { err_exit("pipe error"); } pid_t pid = fork(); if (pid == -1) { err_exit("fork error"); } if (pid == 0) //In Child { //close all close(pipefd[0]); close(pipefd[1]); exit(0); } //In Parent sleep(1); close(pipefd[1]); //Close Write pipe, Now all pipefd[1] Closed!!! char buf[1024]; memset(buf,0,sizeof(buf)); int readCount = read(pipefd[0],buf,sizeof(buf)); //Read from pipe if (readCount == 0) { cout << "OK, read 0 byte" << endl; } close(pipefd[0]); return 0; }
5)如果所有管道读端对应的文件描述符被关闭,则write操作会产生信号SIGPIPE
void onSignalAction(int signalNumber) { switch(signalNumber) { case SIGPIPE: cout << "receive signal SIGPIPE: " << signalNumber << endl; break; default: cout << "other signal" << endl; break; } } int main() { if (signal(SIGPIPE,onSignalAction) != 0) { err_exit("signal error"); } int pipefd[2]; int ret; if ((ret = pipe(pipefd)) != 0) { err_exit("pipe error"); } pid_t pid = fork(); if (pid == -1) { err_exit("fork error"); } if (pid == 0) //In Child, Write pipe { //Wait Parent Close pipefd[0] sleep(1); close(pipefd[0]); string str("I Can Write Pipe from Child!"); write(pipefd[1],str.c_str(),str.size()); //Write to pipe close(pipefd[1]); exit(0); } //In Parent, Close All Pipe close(pipefd[1]); close(pipefd[0]); wait(NULL); return 0; }
Linux PIPE特征
1)当要写入的数据量不大于PIPE_BUF时,Linux将保证写入的原子性。
2)当要写入的数据量大于PIPE_BUF时,Linux将不再保证写入的原子性。
//示例:测试PIPE_BUF大小 int main() { int pipefd[2]; int ret = pipe(pipefd); if (ret < 0) { err_exit("pipe error"); } int flags = fcntl(pipefd[1],F_GETFL); flags |= O_NONBLOCK; ret = fcntl(pipefd[1],F_SETFL,flags); if (ret < 0) { err_exit("fcntl error"); } //Write test unsigned int countForTestPipe = 0; while (true) { ret = write(pipefd[1],"a",1); if (ret < 0) { break; } ++ countForTestPipe; } cout << "size = " << countForTestPipe << endl; } /**测试结果:Ubuntu 14.04 X64 xiaofang@xiaofang-Lenovo-G470:~/apue/it$ ./main size = 65536 */
附-管道容量查询
man 7 pipe
附-深入理解文件描述符
int main() { close(STDIN_FILENO); if (open("readfile.txt",O_RDONLY) == -1) { err_exit("open read error"); } close(STDOUT_FILENO); if (open("writefile.txt",O_WRONLY|O_TRUNC|O_CREAT,0644) == -1) { err_exit("open write error"); } if (execlp("/bin/cat","/bin/cat",NULL) == -1) { err_exit("execlp error"); } return 0; }
Linux管道(匿名PIPE)