1. 程式人生 > >7.程序間通訊:匿名管道pipe

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;                                                                                                                      
}                
[
[email protected]
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