1.管道的本质:固定大小的内核缓冲区。
2.管道是半双工的,数据只能一个方向流动。
3.只有调用fork 父子进程才能管道通信。
4.管道的读写规则
一.如果所有管道读端对应的文件描述符被关闭,则write操作会产生信号SIGPIPE.
#include
#include
#include
#include
#include
void handler(int num)
{
int mypid;
if(num == SIGCHLD)
{
while((mypid =waitpid(-1,NULL,WNOHANG)) > 0)
{
printf("exit pid id %d \n",mypid);
}
}
if(num == SIGPIPE)
{
printf("sigpipe is happen \n");
}
}
int main()
{
int pipefd[2]={0};
pipe(pipefd);
signal(SIGCHLD,handler);
signal(SIGPIPE,handler);
pid_t pid = fork();
if(pid <0)
{
perror("fork error \n");
}
if(pid == 0)
{
int i = 0;
int ret = 0;
int count = 0;
int flags = 0;
close(pipefd[0]);
flags = fcntl(pipefd[1],F_GETFD);
flags |= O_NONBLOCK;
ret = fcntl(pipefd[1],F_SETFD,flags);
if(ret == -1)
{
printf("fcntl error \n");
}
while(1)
{
ret =write(pipefd[1],"l",1);
if(ret == -1)
{
printf("count is %d \n",count);
break;
}
count++;
}
printf("cout is %d \n",count);
close(pipefd[1]);
exit(0);
}else{
close(pipefd[0]);
sleep(1);
}
printf("father is break \n");
return 0;
}
二.如果所有管道写端对应的文件描述符被关闭,则read返回0.
三.当管道不停的被写,写满的时候
O_NONBLOCK disable: write调用阻塞
O_NONBLOCK enable:调用返回-1,errno值为EAGAIN
以测试管道容量为例:
(F_GETFL 开始写成 F_GETFLD 结果是write一直阻塞状态 无法退出哎)
void handler(int num)
{
int mypid;
if(num == SIGCHLD)
{
while((mypid =waitpid(-1,NULL,WNOHANG)) > 0)
{
printf("exit pid id %d \n",mypid);
}
}
if(num == SIGPIPE)
{
printf("sigpipe is happen \n");
}
}
int main()
{
int pipefd[2]={0};
int rett;
rett = pipe(pipefd);
if(rett == -1)
{
perror("error......\n");
}
signal(SIGCHLD,handler);
signal(SIGPIPE,handler);
pid_t pid = fork();
if(pid <0)
{
perror("fork error \n");
}
if(pid == 0)
{
int i = 0;
int ret = 0;
int count = 0;
close(pipefd[0]);
int flags = fcntl(pipefd[1], F_GETFL);
flags = flags | O_NONBLOCK;
ret = fcntl(pipefd[1], F_SETFL, flags);
if (ret == -1)
{
printf("fcntl err.\n");
exit(0);
}
while(1)
{
ret =write(pipefd[1],"l",1);
if(ret == -1)
{
perror("begin break \n");
break;
}
count++;
}
printf("cout is %d \n",count);
close(pipefd[1]);
exit(0);
}else if(pid >0){
sleep(3);
close(pipefd[0]);
close(pipefd[1]);
}
printf("father is break \n");
return 0;
}
5. 当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。
当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。
#include
#include
#include
#include
#include
#include
#define num_k 68*1024
int main()
{
char a[num_k];
char b[num_k];
int fd[2] = {0};
memset(a,'A',sizeof(a));
memset(b,'B',sizeof(b));
int ret = pipe(fd);
pid_t pid = fork();
if(pid < 0)
{
perror("fork error.....\n");
return -1;
}
if(pid == 0)
{
close(fd[0]);
ret = write(fd[1],a,sizeof(a));
printf("son1 pid is %d,ret:%d \n",getpid(),ret);
exit(0);
}
pid = fork();
if(pid == 0)
{
close(fd[0]);
ret = write(fd[1],b,sizeof(b));
printf("son2 pid is %d,ret:%d \n",getpid(),ret);
exit(0);
}
close(fd[1]);
sleep(2);
char buf_read[1024*4] = {0};
while(1)
{
int read_ret = 0;
ret = read(fd[0],buf_read,sizeof(buf_read));
if(ret == 0)
return -1;
printf("pid id %d,read buf[4095]%c \n",getpid(),buf_read[4095]);
}
执行结果如下:
pid id 50648,read buf[4095]A
pid id 50648,read buf[4095]A
pid id 50648,read buf[4095]A
pid id 50648,read buf[4095]A
pid id 50648,read buf[4095]A
pid id 50648,read buf[4095]A
pid id 50648,read buf[4095]A
son1 pid is 50649,ret:69632
pid id 50648,read buf[4095]A
pid id 50648,read buf[4095]A
pid id 50648,read buf[4095]A
pid id 50648,read buf[4095]A
pid id 50648,read buf[4095]A
pid id 50648,read buf[4095]A
pid id 50648,read buf[4095]A
pid id 50648,read buf[4095]A
pid id 50648,read buf[4095]A
pid id 50648,read buf[4095]B
pid id 50648,read buf[4095]A
pid id 50648,read buf[4095]B
pid id 50648,read buf[4095]B
pid id 50648,read buf[4095]B
pid id 50648,read buf[4095]B
pid id 50648,read buf[4095]B
pid id 50648,read buf[4095]B
pid id 50648,read buf[4095]B
pid id 50648,read buf[4095]B
pid id 50648,read buf[4095]B
pid id 50648,read buf[4095]B
pid id 50648,read buf[4095]B
pid id 50648,read buf[4095]B
pid id 50648,read buf[4095]B
pid id 50648,read buf[4095]B
son2 pid is 50650,ret:69632
pid id 50648,read buf[4095]B
pid id 50648,read buf[4095]B
AB交叉了。write不再是一次性全部写入。管道容量可以测试:65536(我的)
6.最后 深入理解下管道
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main(void )
{
int pipefd[2];
pid_t pid;
if (pipe(pipefd) == -1 )
{
printf("pipe() err..\n");
return -1;
}
pid = fork();
if (pid == -1)
{
printf("fork err..\n");
return -1;
}
if (pid == 0)
{
close(pipefd[0]);
//复制文件描述符pipefd[1],给标准输出,言外之意:execlp的ls命令输出到管道中
dup2(pipefd[1], STDOUT_FILENO);
close(pipefd[1]);
execlp("ls", "ls", NULL);
//如果替换新的进程印象失败,则会执行下面一句话
sprintf(stderr, "execute the cmd ls err..\n");
exit(0);
}
else if (pid > 0 )
{
int len = 0;
char buf[100] = {0};
close(pipefd[1]);
//复制文件描述符pipefd[0],给标准输入,言外之意:execlp的wc命令从管道中读
dup2(pipefd[0], STDIN_FILENO);
close(pipefd[0]);
//len = read(pipefd[0], buf, 100);
execlp("wc", "wc", "-w", NULL);
printf("len:%d, buf:%s \n", len , buf);
//close(pipefd[0]);
}
wait(NULL);
printf("parent ..quit\n");
return 0;
}
用管道实现了 shell中管道ls | wc -l