匿名管道pipe
1.管道的概念
本质:
内核缓冲区
伪文件-不占用磁盘空间
特点:两部分
读端,写端,对应两个文件描述符
数据写端流入,读端流出
操作管道的进程被销毁后,管道自动被释放
管道默认是阻塞的
2.管道的原理
内部实现方式:环形队列
特点:先进先出,不能操作中间数据
缓冲区大小:
默认4K
3.管道局限性
队列:
数据只能读取一次,不能重复读取
半双工: 数据传输的方向是单向流动的,因此必须有一对pipe
匿名管道:仅适用于有血缘关系的进程
4.创建匿名管道
int pipe(int pipefd[2]);
0-读端
1-写端
思考题:
单个进程能否使用管道完成读写操作?答:可以
父子进程间通信是否需要sleep函数?答:不需要,因为[管道]是阻塞的
5.管道的读写行为**
读操作
1.有数据:read-正常都,返回读出的字节数
2.无数据:
[1]写端全部关闭
read解除阻塞,返回0
相当于读文件读到了尾部
[2]没有全部关闭
read阻塞
写操作
1.读端全部关闭(重点)
管道破裂,进程被终止(内核给当前进程发送SIGPIPE信号)
2.读端没有全部关闭
缓冲区写满了:write阻塞
缓冲区没满:write继续写
6.查看管道缓冲区大小
命令:ulimit -a # pipe
函数:fpathconf # _PC_PIPE_BUF
long size=fpathconf(fd[0],_PC_PIPE_BUF); //测试管道缓冲区大小
7.设置管道[非阻塞]属性
获取原来的flags: int flags=fcntl(fd[0],F_GETFL);
flag|=O_NONBLOCK;
设置新的flags:fcntl(fd[0],F_SETFL,flags);
案例1:ps aux|grep bash
程序功能:父子进程间通信实现ps aux|grep bash
ps aux|grep bash实现思路:
1.父子进程:ps aux和grep bash
2.ps aux进程将输出数据写到管道中(要进行重定向)
dup2(fd[1],STDOUT_FILENO);
3.grep bash从管道中读取数据(要进行重定向)
dup2(fd[0],STDIN_FILENO);
int main(){
int fd[2];
if(pipe(fd)==-1){
perror("pipe");
exit(1);
}
pid_t pid=fork();
if(pid>0){ //父进程 ps aux
close(fd[0]); //关闭"read"
//不是写到STDOUT_FILENO,而是写入fd[1]
dup2(fd[1],STDOUT_FILENO); //STDOUT_FILENO文件描述符,重定向到"write"端(fd[1])
execlp("ps","ps","aux",NULL);
close(fd[1]);
}
if(pid==0){//子进程 grep bash
close(fd[1]); //关闭"close"
//不从STDIN_FILENO中read,而是从管道fd[0]中read
dup2(fd[0],STDIN_FILENO);//STDIN_FILENO文件描述符,重定向到"read"端(fd[0])
execlp("grep","grep","bash","--color=auto",NULL);
close(fd[0]);
}
return 0;
}
dup2(fd[1],STDOUT_FILENO); 解读:
STDOUT_FILENO文件描述符,重定向为指向fd[1](作为write端)
案例2:ps aux|grep bash
程序功能:兄弟进程间通信实现ps aux|grep bash
int main(){
int fd[2];
if(pipe(fd)==-1){
perror("pipe");
exit(1);
}
int i;
pid_t pid;
for(i=0;i<2;i++){ //父进程创建2个子进程用于通信
pid=fork();
if(pid==0)
break;
}
if(i==0){ //子进程1
close(fd[1]); //close 'write'
dup2(fd[0],STDIN_FILENO);
execl("/bin/grep","grep","bash","--color=auto",NULL);
close(fd[0]);
}
if(i==1){ //子进程2
close(fd[0]); //close 'read'
dup2(fd[1],STDOUT_FILENO);
execl("/bin/ps","ps","aux",NULL);
close(fd[1]);
}
if(i==2){ //父进程
close(fd[0]);
close(fd[1]);
pid_t w_pid;
int status;
while((w_pid=waitpid(-1,&status,WNOHANG))!=-1){
if(w_pid==0)
continue;
printf("w_pid=%d\n",w_pid);
if(WIFEXITED(status))
printf("exit value=%d\n",WEXITSTATUS(status));
if(WIFSIGNALED(status))
printf("term signal=%d\n",WTERMSIG(status));
}
}
return 0;
}
[gjw@localhost 1-pipe]$ ./test
root 840 0.0 0.0 115300 956 ? S 08:24 0:00 /bin/bash /usr/sbin/ksmtuned
gjw 2259 0.0 0.0 72384 780 ? Ss 08:25 0:00 /usr/bin/ssh-agent /bin/sh -c exec -l /bin/bash -c "env GNOME_SHELL_SESSION_MODE=classic gnome-session --session gnome-classic"
gjw 3189 0.0 0.0 116952 3616 pts/0 Ss 08:25 0:00 bash
gjw 4922 0.0 0.0 116820 3360 pts/1 Ss+ 08:52 0:00 bash
gjw 10706 0.0 0.0 116740 3252 pts/2 Ss 10:49 0:00 bash
gjw 16374 0.0 0.0 112728 988 pts/0 S+ 12:53 0:00 grep bash --color=auto
w_pid=16375
exit value=0
w_pid=16374
exit value=0