您的位置:首页 > 运维架构 > Linux

【study】linux 进程通信之管道

2017-08-30 09:55 134 查看
1.管道的本质:固定大小的内核缓冲区。

2.管道是半双工的,数据只能一个方向流动。

3.只有调用fork 父子进程才能管道通信。

4.管道的读写规则

一.如果所有管道读端对应的文件描述符被关闭,则write操作会产生信号SIGPIPE.

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>
#include<fcntl.h>
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<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>
#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 <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h>

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  linux