7.进程间通信:匿名管道pipe

匿名管道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   

猜你喜欢

转载自blog.csdn.net/weixin_36750623/article/details/83054146