Linux系統程式設計之程式間通訊方式:管道(二)

千鋒教育官方發表於2019-09-16

  管道的特點

  每個管道只有一個頁面作為緩衝區,該頁面是按照環形緩衝區的方式來使用的。這種訪問方式是典型的生產者 —— 消費者 模型。當 生產者 程式有大量的資料需要寫時,而且每當寫滿一個頁面就需要進行睡眠等待,等待 消費者 從管道中讀走一些資料,為其騰出一些空間。相應的,如果管道中沒有可讀資料, 消費者 程式就要睡眠等待,具體過程如下圖所示:

 

  預設的情況下,從管道中讀寫資料,最主要的特點就是阻塞問題(這一特點應該記住),當管道里沒有資料,另一個程式預設用read() 函式從管道中讀資料是阻塞的。

 

  測試程式碼如下:

 

  #include<stdio.h>

 

  #include<string.h>

 

  #include<unistd.h>

 

  #include<stdlib.h>

 

  #include<sys/types.h>

 

  #include<sys/wait.h>

 

  int main(int argc,char*argv[])

 

  {

 

  int fd_pipe[2]={0};

 

  pid_t pid;

 

  if(pipe(fd_pipe)<0){// 建立無名管道

 

  perror("pipe");

 

  }

 

  pid=fork();// 建立程式

 

  if(pid<0){// 出錯

 

  perror("fork");

 

  exit(-1);

 

  }

 

  if(pid==0){// 子程式

 

  _exit(0);

 

  }else if(pid>0){// 父程式

 

  wait(NULL);// 等待子程式結束,回收其資源

 

  char str[50]={0};

 

  printf("before read\n");

 

  // 從管道里讀資料,如果管道沒有資料, read() 會阻塞

 

  read(fd_pipe[0],str,sizeof(str));

 

  printf("after read\n");

 

  printf("str=[%s]\n",str);// 列印資料

 

  }

 

  return 0;

 

  }

 

  執行結果:

 

  當然,我們程式設計時可透過fcntl() 函式設定檔案的阻塞特性。

 

  設定為阻塞:fcntl(fd,F_SETFL,0);

 

  設定為非阻塞:fcntl(fd,F_SETFL,O_NONBLOCK);

 

  測試程式碼如下:

 

  #include<stdio.h>

 

  #include<string.h>

 

  #include<unistd.h>

 

  #include<stdlib.h>

 

  #include<sys/types.h>

 

  #include<sys/wait.h>

 

  #include<fcntl.h>

 

  int main(int argc,char*argv[])

 

  {

 

  int fd_pipe[2]={0};

 

  pid_t pid;

 

  if(pipe(fd_pipe)<0){// 建立無名管道

 

  perror("pipe");

 

  }

 

  pid=fork();// 建立程式

 

  if(pid<0){// 出錯

 

  perror("fork");

 

  exit(-1);

 

  }

 

  if(pid==0){// 子程式

 

  sleep(3);

 

  char buf[]="hello,edu";

 

  write(fd_pipe[1],buf,strlen(buf));// 寫資料

 

  _exit(0);

 

  }else if(pid>0){// 父程式

 

  fcntl(fd_pipe[0],F_SETFL,O_NONBLOCK);// 非阻塞

 

  //fcntl(fd_pipe[0],F_SETFL,0);// 阻塞

 

  while(1){

 

  char str[50]={0};

 

  read(fd_pipe[0],str,sizeof(str));// 讀資料

 

  printf("str=[%s]\n",str);

 

  sleep(1);

 

  }

 

  }

 

  return 0;

 

  }

 

  執行結果:

 

  預設的情況下,從管道中讀寫資料,還有如下特點(知道有這麼回事就夠了,不用刻意去記這些特點):

 

  1 )呼叫 write() 函式向管道里寫資料,當緩衝區已滿時 write() 也會阻塞。

 

  測試程式碼如下:

 

  #include<stdio.h>

 

  #include<string.h>

 

  #include<unistd.h>

 

  #include<stdlib.h>

 

  #include<sys/types.h>

 

  #include<sys/wait.h>

 

  int main(int argc,char*argv[])

 

  {

 

  int fd_pipe[2]={0};

 

  pid_t pid;

 

  char buf[1024]={0};

 

  memset(buf,'a',sizeof(buf));// 往管道寫的內容

 

  int i=0;

 

  if(pipe(fd_pipe)<0){// 建立無名管道

 

  perror("pipe");

 

  }

 

  pid=fork();// 建立程式

 

  if(pid<0){// 出錯

 

  perror("fork");

 

  exit(-1);

 

  }

 

  if(pid==0){// 子程式

 

  while(1){

 

  write(fd_pipe[1],buf,sizeof(buf));

 

  i++;

 

  printf("i========%d\n",i);

 

  }

 

  _exit(0);

 

  }else if(pid>0){// 父程式

 

  wait(NULL);// 等待子程式結束,回收其資源

 

  }

 

  return 0;

 

  }

 

  執行結果:

 

  2 )通訊過程中,別的程式先結束後,當前程式讀埠關閉後,向管道內寫資料時, write() 所在程式會(收到 SIGPIPE 訊號)退出,收到 SIGPIPE 預設動作為中斷當前程式。

 

  測試程式碼如下:

 

  #include<stdio.h>

 

  #include<string.h>

 

  #include<unistd.h>

 

  #include<stdlib.h>

 

  #include<sys/types.h>

 

  #include<sys/wait.h>

 

  int main(int argc,char*argv[])

 

  {

 

  int fd_pipe[2]={0};

 

  pid_t pid;

 

  if(pipe(fd_pipe)<0){// 建立無名管道

 

  perror("pipe");

 

  }

 

  pid=fork();// 建立程式

 

  if(pid<0){// 出錯

 

  perror("fork");

 

  exit(-1);

 

  }

 

  if(pid==0){// 子程式

 

  //close(fd_pipe[0]);

 

  _exit(0);

 

  }else if(pid>0){// 父程式

 

  wait(NULL);// 等待子程式結束,回收其資源

 

  close(fd_pipe[0]);// 當前程式讀埠關閉

 

  char buf[50]="12345";

 

  // 當前程式讀埠關閉

 

  //write() 會收到 SIGPIPE 訊號,預設動作為中斷當前程式

 

  write(fd_pipe[1],buf,strlen(buf));

 

  while(1);// 阻塞

 

  }

 

  return 0;

 

  }

 

  執行結果:

 

 


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69914734/viewspace-2657157/,如需轉載,請註明出處,否則將追究法律責任。

相關文章