linux 匿名管道缓冲大小 sigpipe,Linux管道(匿名PIPE)

管道基本概念

管道是Unix中最古老的进程间通信的形式。

我们把从一个进程连接到另一个进程的一个数据流称为一个“管道”

如:ps aux | grep httpd | awk '{print $2}'

管道示意图

管道的本质

固定大小的内核缓冲区

管道限制

1)管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道;

2)匿名管道只能用于具有共同祖先的进程(如父进程与fork出的子进程)之间进行通信;[通常,一个管道由一个进程创建,然后该进程调用fork,此后父子进程共享该管道]

匿名管道pipe

SYNOPSIS

#include

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_NONBLOCKdisable: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返回0int 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操作会产生信号SIGPIPEvoid 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,匿名管道缓冲大小,sigpipe)