欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Linux通過匿名管道進行進程間通信

 更新時間:2022年02月17日 16:20:01   作者:ljianhui  
這篇文章主要介紹了Linux通過匿名管道進行進程間通信,介紹了什么是管道,popen函數(shù),pipe調(diào)用等相關(guān)內(nèi)容,小編覺得還是挺不錯的,具有一定借鑒價值,需要的朋友可以參考下

本文研究的主要是Linux通過匿名管道進行進程間通信的相關(guān)內(nèi)容,具體介紹如下。

在前面,介紹了一種進程間的通信方式:使用信號,我們創(chuàng)建通知事件,并通過它引起響應(yīng),但傳遞的信息只是一個信號值。這里將介紹另一種進程間通信的方式——匿名管道,通過它進程間可以交換更多有用的數(shù)據(jù)。

一、什么是管道

如果你使用過Linux的命令,那么對于管道這個名詞你一定不會感覺到陌生,因為我們通常通過符號“|"來使用管道,但是管理的真正定義是什么呢?管道是一個進程連接數(shù)據(jù)流到另一個進程的通道,它通常是用作把一個進程的輸出通過管道連接到另一個進程的輸入。

舉個例子,在shell中輸入命令:ls -l | grep string,我們知道ls命令(其實也是一個進程)會把當(dāng)前目錄中的文件都列出來,但是它不會直接輸出,而是把本來要輸出到屏幕上的數(shù)據(jù)通過管道輸出到grep這個進程中,作為grep這個進程的輸入,然后這個進程對輸入的信息進行篩選,把存在string的信息的字符串(以行為單位)打印在屏幕上。

二、使用popen函數(shù)

1、popen函數(shù)和pclose函數(shù)介紹

有靜就有動,有開就有關(guān),與此相同,與popen函數(shù)相對應(yīng)的函數(shù)是pclose函數(shù),它們的原型如下:

#include <stdio.h> 
FILE* popen (const char *command, const char *open_mode); 
int pclose(FILE *stream_to_close); 

poen函數(shù)允許一個程序?qū)⒘硪粋€程序作為新進程來啟動,并可以傳遞數(shù)據(jù)給它或者通過它接收數(shù)據(jù)。command是要運行的程序名和相應(yīng)的參數(shù)。open_mode只能是"r(只讀)"和"w(只寫)"的其中之一。注意,popen函數(shù)的返回值是一個FILE類型的指針,而Linux把一切都視為文件,也就是說我們可以使用stdio I/O庫中的文件處理函數(shù)來對其進行操作。

如果open_mode是"r",主調(diào)用程序就可以使用被調(diào)用程序的輸出,通過函數(shù)返回的FILE指針,就可以能過stdio函數(shù)(如fread)來讀取程序的輸出;如果open_mode是"w",主調(diào)用程序就可以向被調(diào)用程序發(fā)送數(shù)據(jù),即通過stdio函數(shù)(如fwrite)向被調(diào)用程序?qū)憯?shù)據(jù),而被調(diào)用程序就可以在自己的標(biāo)準(zhǔn)輸入中讀取這些數(shù)據(jù)。

pclose函數(shù)用于關(guān)閉由popen創(chuàng)建出的關(guān)聯(lián)文件流。pclose只在popen啟動的進程結(jié)束后才返回,如果調(diào)用pclose時被調(diào)用進程仍在運行,pclose調(diào)用將等待該進程結(jié)束。它返回關(guān)閉的文件流所在進程的退出碼。

2、例子

很多時候,我們根本就不知道輸出數(shù)據(jù)的長度,為了避免定義一個非常大的數(shù)組作為緩沖區(qū),我們可以以塊的方式來發(fā)送數(shù)據(jù),一次讀取一個塊的數(shù)據(jù)并發(fā)送一個塊的數(shù)據(jù),直到把所有的數(shù)據(jù)都發(fā)送完。下面的例子就是采用這種方式的數(shù)據(jù)讀取和發(fā)送方式。源文件名為popen.c,代碼如下:

#include <unistd.h> 
#include <stdlib.h> 
#include <stdio.h> 
#include <string.h> 
 
int main() 
{ 
  FILE *read_fp = NULL; 
  FILE *write_fp = NULL; 
  char buffer[BUFSIZ + 1]; 
  int chars_read = 0; 
   
  //初始化緩沖區(qū) 
  memset(buffer, '\0', sizeof(buffer)); 
  //打開ls和grep進程 
  read_fp = popen("ls -l", "r"); 
  write_fp = popen("grep rwxrwxr-x", "w"); 
  //兩個進程都打開成功 
  if(read_fp && write_fp) 
  { 
    //讀取一個數(shù)據(jù)塊 
    chars_read = fread(buffer, sizeof(char), BUFSIZ, read_fp); 
    while(chars_read > 0) 
    { 
      buffer[chars_read] = '\0'; 
      //把數(shù)據(jù)寫入grep進程 
      fwrite(buffer, sizeof(char), chars_read, write_fp); 
      //還有數(shù)據(jù)可讀,循環(huán)讀取數(shù)據(jù),直到讀完所有數(shù)據(jù) 
      chars_read = fread(buffer, sizeof(char), BUFSIZ, read_fp); 
    } 
    //關(guān)閉文件流 
    pclose(read_fp); 
    pclose(write_fp); 
    exit(EXIT_SUCCESS); 
  } 
  exit(EXIT_FAILURE); 
} 

運行結(jié)果如下:

從運行結(jié)果來看,達(dá)到了信息篩選的目的。程序在進程ls中讀取數(shù)據(jù),再把數(shù)據(jù)發(fā)送到進程grep中進行篩選處理,相當(dāng)于在shell中直接輸入命令:ls -l | grep rwxrwxr-x。

3、popen的實現(xiàn)方式及優(yōu)缺點

當(dāng)請求popen調(diào)用運行一個程序時,它首先啟動shell,即系統(tǒng)中的sh命令,然后將command字符串作為一個參數(shù)傳遞給它。

這樣就帶來了一個優(yōu)點和一個缺點。優(yōu)點是:在Linux中所有的參數(shù)擴展都是由shell來完成的。所以在啟動程序(command中的命令程序)之前先啟動shell來分析命令字符串,也就可以使各種shell擴展(如通配符)在程序啟動之前就全部完成,這樣我們就可以通過popen啟動非常復(fù)雜的shell命令。

而它的缺點就是:對于每個popen調(diào)用,不僅要啟動一個被請求的程序,還要啟動一個shell,即每一個popen調(diào)用將啟動兩個進程,從效率和資源的角度看,popen函數(shù)的調(diào)用比正常方式要慢一些。

三、pipe調(diào)用

如果說popen是一個高級的函數(shù),pipe則是一個底層的調(diào)用。與popen函數(shù)不同的是,它在兩個進程之間傳遞數(shù)據(jù)不需要啟動一個shell來解釋請求命令,同時它還提供對讀寫數(shù)據(jù)的更多的控制。

pipe函數(shù)的原型如下:

#include <unistd.h> 
int pipe(int file_descriptor[2]); 

我們可以看到pipe函數(shù)的定義非常特別,該函數(shù)在數(shù)組中墻上兩個新的文件描述符后返回0,如果返回返回-1,并設(shè)置errno來說明失敗原因。

數(shù)組中的兩個文件描述符以一種特殊的方式連接起來,數(shù)據(jù)基于先進先出的原則,寫到file_descriptor[1]的所有數(shù)據(jù)都可以從file_descriptor[0]讀回來。由于數(shù)據(jù)基于先進先出的原則,所以讀取的數(shù)據(jù)和寫入的數(shù)據(jù)是一致的。

特別提醒:

1、從函數(shù)的原型我們可以看到,它跟popen函數(shù)的一個重大區(qū)別是,popen函數(shù)是基于文件流(FILE)工作的,而pipe是基于文件描述符工作的,所以在使用pipe后,數(shù)據(jù)必須要用底層的read和write調(diào)用來讀取和發(fā)送。

2、不要用file_descriptor[0]寫數(shù)據(jù),也不要用file_descriptor[1]讀數(shù)據(jù),其行為未定義的,但在有些系統(tǒng)上可能會返回-1表示調(diào)用失敗。數(shù)據(jù)只能從file_descriptor[0]中讀取,數(shù)據(jù)也只能寫入到file_descriptor[1],不能倒過來。

例子:

首先,我們在原先的進程中創(chuàng)建一個管道,然后再調(diào)用fork創(chuàng)建一個新的進程,最后通過管道在兩個進程之間傳遞數(shù)據(jù)。源文件名為pipe.c,代碼如下:

#include <unistd.h> 
#include <stdlib.h> 
#include <stdio.h> 
#include <string.h> 
 
int main() 
{ 
  int data_processed = 0; 
  int filedes[2]; 
  const char data[] = "Hello pipe!"; 
  char buffer[BUFSIZ + 1]; 
  pid_t pid; 
  //清空緩沖區(qū) 
  memset(buffer, '\0', sizeof(buffer)); 
 
  if(pipe(filedes) == 0) 
  { 
    //創(chuàng)建管道成功 
    //通過調(diào)用fork創(chuàng)建子進程 
    pid = fork(); 
    if(pid == -1) 
    { 
      fprintf(stderr, "Fork failure"); 
      exit(EXIT_FAILURE); 
    } 
    if(pid == 0) 
    { 
      //子進程中 
      //讀取數(shù)據(jù) 
      data_processed = read(filedes[0], buffer, BUFSIZ); 
      printf("Read %d bytes: %s\n", data_processed, buffer); 
      exit(EXIT_SUCCESS); 
    } 
    else 
    { 
      //父進程中 
      //寫數(shù)據(jù) 
      data_processed = write(filedes[1], data, strlen(data)); 
      printf("Wrote %d bytes: %s\n", data_processed, data); 
      //休眠2秒,主要是為了等子進程先結(jié)束,這樣做也只是純粹為了輸出好看而已 
      //父進程其實沒有必要等等子進程結(jié)束 
      sleep(2); 
      exit(EXIT_SUCCESS); 
    } 
  } 
  exit(EXIT_FAILURE); 
} 

運行結(jié)果為:

可見,子進程讀取了父進程寫到filedes[1]中的數(shù)據(jù),如果在父進程中沒有sleep語句,父進程可能在子進程結(jié)束前結(jié)束,這樣你可能將看到兩個輸入之間有一個命令提示符分隔。

四、把管道用作標(biāo)準(zhǔn)輸入和標(biāo)準(zhǔn)輸出

下面來介紹一種用管道來連接兩個進程的更簡潔方法,我們可以把文件描述符設(shè)置為一個已知值,一般是標(biāo)準(zhǔn)輸入0或標(biāo)準(zhǔn)輸出1。這樣做最大的好處是可以調(diào)用標(biāo)準(zhǔn)程序,即那些不需要以文件描述符為參數(shù)的程序。

為了完成這個工作,我們還需要兩個函數(shù)的輔助,它們分別是dup函數(shù)或dup2函數(shù),它們的原型如下

#include <unistd.h> 
int dup(int file_descriptor); 
int dup2(int file_descriptor_one, int file_descriptor_two); 

dup調(diào)用創(chuàng)建一個新的文件描述符與作為它的參數(shù)的那個已有文件描述符指向同一個文件或管道。對于dup函數(shù)而言,新的文件描述總是取最小的可用值。而dup2所創(chuàng)建的新文件描述符或者與int file_descriptor_two相同,或者是第一個大于該參數(shù)的可用值。所以當(dāng)我們首先關(guān)閉文件描述符0后調(diào)用dup,那么新的文件描述符將是數(shù)字0.

例子

在下面的例子中,首先打開管道,然后fork一個子進程,然后在子進程中,使標(biāo)準(zhǔn)輸入指向讀管道,然后關(guān)閉子進程中的讀管道和寫管道,只留下標(biāo)準(zhǔn)輸入,最后調(diào)用execlp函數(shù)來啟動一個新的進程od,但是od并不知道它的數(shù)據(jù)來源是管道還是終端。父進程則相對簡單,它首先關(guān)閉讀管道,然后在寫管道中寫入數(shù)據(jù),再關(guān)閉寫管道就完成了它的任務(wù)。源文件為pipe2.c,代碼如下:

#include <unistd.h> 
#include <stdlib.h> 
#include <stdio.h> 
#include <string.h> 
 
int main() 
{ 
  int data_processed = 0; 
  int pipes[2]; 
  const char data[] = "123"; 
  pid_t pid; 
 
  if(pipe(pipes) == 0) 
  { 
    pid = fork(); 
    if(pid == -1) 
    { 
      fprintf(stderr, "Fork failure!\n"); 
      exit(EXIT_FAILURE); 
    } 
    if(pid == 0) 
    { 
      //子進程中 
      //使標(biāo)準(zhǔn)輸入指向fildes[0] 
      close(0); 
      dup(pipes[0]); 
      //關(guān)閉pipes[0]和pipes[1],只剩下標(biāo)準(zhǔn)輸入 
      close(pipes[0]); 
      close(pipes[1]); 
      //啟動新進程od 
      execlp("od", "od", "-c", 0); 
      exit(EXIT_FAILURE); 
    } 
    else 
    { 
      //關(guān)閉pipes[0],因為父進程不用讀取數(shù)據(jù) 
      close(pipes[0]); 
      data_processed = write(pipes[1], data, strlen(data)); 
      //寫完數(shù)據(jù)后,關(guān)閉pipes[1] 
      close(pipes[1]); 
      printf("%d - Wrote %d bytes\n", getpid(), data_processed); 
    } 
  } 
  exit(EXIT_SUCCESS); 
} 

運行結(jié)果為:

從運行結(jié)果中可以看出od進程正確地完成了它的任務(wù),與在shell中直接輸入od -c和123的效果一樣。

五、關(guān)于管道關(guān)閉后的讀操作的討論

現(xiàn)在有這樣一個問題,假如父進程向管道file_pipe[1]寫數(shù)據(jù),而子進程在管道file_pipe[0]中讀取數(shù)據(jù),當(dāng)父進程沒有向file_pipe[1]寫數(shù)據(jù)時,子進程則沒有數(shù)據(jù)可讀,則子進程會發(fā)生什么呢?再者父進程把file_pipe[1]關(guān)閉了,子進程又會有什么反應(yīng)呢?

當(dāng)寫數(shù)據(jù)的管道沒有關(guān)閉,而又沒有數(shù)據(jù)可讀時,read調(diào)用通常會阻塞,但是當(dāng)寫數(shù)據(jù)的管道關(guān)閉時,read調(diào)用將會返回0而不是阻塞。注意,這與讀取一個無效的文件描述符不同,read一個無效的文件描述符返回-1。

六、匿名管道的缺陷

看了這么多相信大家也知道它的一個缺點,就是通信的進程,它們的關(guān)系一定是父子進程的關(guān)系,這就使得它的使用受到了一點的限制,但是我們可以使用命名管道來解決這個問題。命名管道將在下一篇文章:Linux進程間通信——使用命名管道中介紹。

總結(jié)

以上就是本文關(guān)于Linux通過匿名管道進行進程間通信的全部內(nèi)容,希望對大家有所幫助。感興趣的朋友可以繼續(xù)參閱本站其他相關(guān)專題,如有不足之處,歡迎留言指出。感謝朋友們對本站的支持!

相關(guān)文章

  • Linux上下行網(wǎng)速測試工具_(dá)speedtest-cli安裝使用方法

    Linux上下行網(wǎng)速測試工具_(dá)speedtest-cli安裝使用方法

    speedtest-cli是Linux下的一個上下行網(wǎng)速測試工具,是一個用Python寫的命令行腳本,需要的朋友可以參考下
    2017-03-03
  • centos 7中設(shè)置tomcat 7為系統(tǒng)服務(wù)的方法詳解

    centos 7中設(shè)置tomcat 7為系統(tǒng)服務(wù)的方法詳解

    這篇文章主要給大家介紹了關(guān)于在centos 7中設(shè)置tomcat 7為系統(tǒng)服務(wù)的相關(guān)資料,文中介紹的非常詳細(xì),對大家具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來跟著小編一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-06-06
  • centos6利用yum安裝php mysql gd的步驟

    centos6利用yum安裝php mysql gd的步驟

    我在84vps中利用yum順利安裝mysql php 及gd庫,因為vps本身自帶了apahce2.2所以沒有apache安裝過程
    2012-09-09
  • apache的AllowOverride以及Options使用詳解

    apache的AllowOverride以及Options使用詳解

    通常利用Apache的rewrite模塊對 URL 進行重寫的時候, rewrite規(guī)則會寫在 .htaccess 文件里。但要使 apache 能夠正常的讀取.htaccess 文件的內(nèi)容,就必須對.htaccess 所在目錄進行配置
    2012-11-11
  • Linux基礎(chǔ)學(xué)習(xí)之文件查找find的常見用法

    Linux基礎(chǔ)學(xué)習(xí)之文件查找find的常見用法

    這篇文章主要給大家介紹了關(guān)于Linux基礎(chǔ)學(xué)習(xí)之文件查找find的常見用法的相關(guān)資料,例如:根據(jù)文件名查找、根據(jù)正則表達(dá)式查找、根據(jù)路徑查找、根據(jù)文件類型查找以及根據(jù)文件大小等等,文中給出了詳細(xì)的示例代碼,需要的朋友可以參考借鑒。
    2017-12-12
  • Linux系統(tǒng)查看當(dāng)前網(wǎng)絡(luò)連接數(shù)的方法小結(jié)

    Linux系統(tǒng)查看當(dāng)前網(wǎng)絡(luò)連接數(shù)的方法小結(jié)

    在日常的運維和開發(fā)中,網(wǎng)絡(luò)連接數(shù)的監(jiān)控是一個非常重要的指標(biāo),當(dāng)你需要排查網(wǎng)絡(luò)問題或優(yōu)化系統(tǒng)性能時,查看連接數(shù)是關(guān)鍵的一步,不同的 Linux 環(huán)境和發(fā)行版可能需要使用不同的方法來獲取連接信息,本文給大家介紹了Linux系統(tǒng)查看當(dāng)前網(wǎng)絡(luò)連接數(shù)的方法
    2024-12-12
  • linux查找日志cat和grep方式

    linux查找日志cat和grep方式

    在Linux系統(tǒng)中,我們常常需要查詢?nèi)罩疚募械奶囟ㄐ畔?這時候掌握一些關(guān)鍵字查詢技巧就顯得非常重要,例如,要查詢關(guān)鍵字前后30行,可以使用grep命令的'-C'選項,若要查詢兩個關(guān)鍵字同時出現(xiàn)的情況,可以將grep命令連續(xù)使用,對于壓縮后的文件
    2024-10-10
  • Linux配置防火墻,開啟80、3306端口的實例方法

    Linux配置防火墻,開啟80、3306端口的實例方法

    在本篇文章里小編給大家整理的是關(guān)于Linux配置防火墻,開啟80端口、3306端口的相關(guān)內(nèi)容,需要的朋友們參考下。
    2020-02-02
  • 基于centos7 安裝python3.6.4出錯的解決方法

    基于centos7 安裝python3.6.4出錯的解決方法

    下面小編就為大家分享一篇基于centos7 安裝python3.6.4出錯的解決方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-01-01
  • Linux進程基礎(chǔ)教程詳解

    Linux進程基礎(chǔ)教程詳解

    這篇文章主要為大家詳細(xì)介紹了Linux進程基礎(chǔ)教程,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-04-04

最新評論