1. 程式人生 > >Linux 下父程序與子程序的通訊(pipe管道)

Linux 下父程序與子程序的通訊(pipe管道)

每個程序各自有不同的使用者地址空間,任 何一個程序的全域性變數在另一個程序中都看不到,所以程序之間要交換資料必須通過核心,在核心中開闢一塊緩衝 區,程序1把資料從使用者空間拷到核心緩衝區,程序2再從核心緩衝區把資料讀走,核心提供的這種機制稱為程序間通訊(IPC,InterProcess Communication)。如下圖所示。

 程序間通訊

fork產生子程序利用pipe管道通訊 - 司群 -       司群léo


管道是一種最基本的IPC機制,由pipe函式建立:

fork產生子程序利用pipe管道通訊 - 司群 -       司群léo

調 用pipe函式時在核心中開闢一塊緩衝區(稱為管道)用於通訊,它有一個讀端一個寫端,然後通過filedes引數傳出給使用者程式兩個檔案描述 符,filedes[0]指向管道的讀端,filedes[1]指向管道的寫端(很好記,就像0是標準輸入1是標準輸出一樣)。所以管道在使用者程式看起來 就像一個開啟的檔案,通過read(filedes[0]);或者write(filedes[1]);向這個檔案讀寫資料其實是在讀寫核心緩衝區。 pipe函式呼叫成功返回0,呼叫失敗返回-1。 

int pipe(int filedes[2]) 中的兩個檔案描述符被強制規定filedes[0]只能指向管道的讀端,如果進行寫操作就會出現錯誤;同理filedes[1]只能指向管道的寫端,如果進行讀操作就會出現錯誤
開闢了管道之後如何實現兩個程序間的通訊呢?比如可以按下面的步驟通訊。

fork產生子程序利用pipe管道通訊 - 司群 -       司群léo 

1. 父程序呼叫pipe開闢管道,得到兩個檔案描述符指向管道的兩端。 
2. 父程序呼叫fork建立子程序,那麼子程序也有兩個檔案描述符指向同一管道。 
3. 父程序關閉管道讀端,子程序關閉管道寫端。父程序可以往管道里寫,子程序可以從管道里讀,管道是用環形佇列實現的,資料從寫端流入從讀端流出,這樣就實現了程序間通訊。

注:之前一直沒明白為什麼在這裡父程序要關閉管道讀端,並且子程序要關閉管道寫端,想了很久終於想通了...,原因如下:因為上面的這個程式是要模擬父程序和子程序的管道讀寫操作,其中父程序用於向管道中寫入資料,子程序用於向管道中讀取資料,因此開始要關閉父程序的讀檔案描述符filedes[0], 以及關閉子程序的寫檔案描述符filedes[1],這是為了模擬這個過程。

        然後至於為什麼父程序關閉管道的讀檔案描述符filedes[0]後子程序還能讀取管道的資料,是因為系統維護的是一個檔案的檔案描述符表的計數,父子程序都各自有指向相同檔案的檔案描述符,當關閉一個檔案描述符時,相應計數減一,當這個計數減到0時,檔案就被關閉,因此雖然父程序關閉了其檔案描述符filedes[0],但是這個檔案的檔案描述符計數還沒等於0,所以子程序還可以讀取。也可以這麼理解,父程序和子程序都有各自的檔案描述符,因此雖然父程序中關閉了filedes[0],但是對子程序中的filedes[0]沒有影響

檔案表中的每一項都會維護一個引用計數,標識該表項被多少個檔案描述符(fd)引用,在引用計數為0的時候,表項才會被刪除。所以呼叫close(fd)關閉子程序的檔案描述符,只會減少引用計數,但是不會使檔案表項被清除,所以父程序依舊可以訪問

        最後需要注意,在linux的pipe管道下,在寫端進行寫資料時,不需要關閉讀端的緩衝檔案(即不需要讀端的檔案描述符計數為0),但是在讀端進行讀資料時必須先關閉寫端的緩衝檔案(即寫端的檔案描述符計數為0)然後才能讀取資料。

 管道

fork產生子程序利用pipe管道通訊 - 司群 -       司群léo

fork產生子程序利用pipe管道通訊 - 司群 -       司群léo

使用管道有一些限制: 
? 兩個程序通過一個管道只能實現單向通訊,比如上面的例子,父程序寫子程序讀,如果有時候也需要子程序寫父程序讀,就必須另開一個管道。請讀者思考,如果只開一個管道,但是父程序不關閉讀端,子程序也不關閉寫端,雙方都有讀端和寫端,為什麼不能實現雙向通訊? 
? 管道的讀寫端通過開啟的檔案描述符來傳遞,因此要通訊的兩個程序必須從它們的公共祖先那裡繼承管道檔案描述符。上面的例子是父程序把檔案描述符傳給子程序 之後父子程序之間通訊,也可以父程序fork兩次,把檔案描述符傳給兩個子程序,然後兩個子程序之間通訊,總之需要通過fork傳遞檔案描述符使兩個程序 都能訪問同一管道,它們才能通訊。 
使用管道需要注意以下4種特殊情況(假設都是阻塞I/O操作,沒有設定O_NONBLOCK標誌): 
1. 如果所有指向管道寫端的檔案描述符都關閉了(管道寫端的引用計數等於0),而仍然有程序從管道的讀端讀資料,那麼管道中剩餘的資料都被讀取後,再次read會返回0,就像讀到檔案末尾一樣。 
2. 如果有指向管道寫端的檔案描述符沒關閉(管道寫端的引用計數大於0),而持有管道寫端的程序也沒有向管道中寫資料,這時有程序從管道讀端讀資料,那麼管道中剩餘的資料都被讀取後,再次read會阻塞,直到管道中有資料可讀了才讀取資料並返回。 
3. 如果所有指向管道讀端的檔案描述符都關閉了(管道讀端的引用計數等於0),這時有程序向管道的寫端write,那麼該程序會收到訊號SIGPIPE,通常會導致程序異常終止。在第 33 章 訊號會講到怎樣使SIGPIPE訊號不終止程序。 
4. 如果有指向管道讀端的檔案描述符沒關閉(管道讀端的引用計數大於0),而持有管道讀端的程序也沒有從管道中讀資料,這時有程序向管道寫端寫資料,那麼在管道被寫滿時再次write會阻塞,直到管道中有空位置了才寫入資料並返回。 
管道的這四種特殊情況具有普遍意義。在第 37 章 socket程式設計要講的TCP socket也具有管道的這些特性。


相關推薦

Linux 程序程序通訊pipe管道

每個程序各自有不同的使用者地址空間,任 何一個程序的全域性變數在另一個程序中都看不到,所以程序之間要交換資料必須通過核心,在核心中開闢一塊緩衝 區,程序1把資料從使用者空間拷到核心緩衝區,程序2再從核心緩衝區把資料讀走,核心提供的這種機制稱為程序間通訊(IPC,Inte

python 多程序程序

多程序: 1.os.fork() 2.from multiprocessing import Process 3.form multiprocessing import Pool 子程序: subprocess 很多時候,子程序並不是自身,而是一個外部程序。我們建立了子程序後,還需要控制

程序程序的執行順序

from multiprocessing import Process import time def task(name): print("%s start" % name) time.sleep(3) print("%s stop" % name) if __name_

微信小程式wepy框架中元件元件通訊和互動

官網上描述: $broadcast $broadcast事件是由父元件發起,所有子元件都會收到此廣播事件,除非事件被手動取消。事件廣播的順序為廣度優先搜尋順序。 $emit $emit與$broadcast正好相反,事件發起元件的所有祖先元件會依次接收到$emit事件。 $in

Python 程序程序,執行緒執行緒

一、什麼是程序    顧名思義,程序即正在執行的一個過程。程序是對正在執行程式的一個抽象。(執行程式三大執行元件:記憶體,磁碟,CPU。程式就是一堆程式碼,放在磁盤裡面,在執行程式時,程式碼載入到記憶體,由CPU到記憶體取程式碼,最終程式執行起來。這就是起了一個程序。)   

深入Node.js的程序程序:從文件到實踐

歡迎關注Github倉庫,這是一個自2018年起持續更新的前端&演算法開源部落格。目前已有node學習、js面試筆記、css3動畫設計、webpack4系列教程、設計模式、劍指offer·js版等多個系列。 倉庫地址:https://github.com/dongyuanxin/blog 程序

程序執行緒總結比較全面

1.程序和執行緒 1.1 概述: 程序是具有一定獨立功能的程式關於某個資料集合上的一次執行活動,程序是系統進行資源分配和排程的一個獨立單位. 執行緒是程序的一個實體,是CPU排程和分派的基本單位,它是比程序更小的能獨立執行的基本單位.執行緒自己基本上不擁有系統資源,只擁有一點在執行中必不

在PySide中使用多程序多執行緒multiprocess,threading

在UI介面程式中,使用到多程序與多執行緒是很常見的場景,有時候我們需要將一些耗時的操作放在其他的執行緒或者程序中,避免卡死主執行緒。而且利用多執行緒加Qt的訊號槽機制我們可以在子程序中實現事件監聽,實時監測程序間的通訊。之前一直對執行緒和程序的理解不太深刻,藉著

程序通訊PIPE呼叫

看過了高階的popen呼叫之後,我們來了解一下底層的pipe函式。popen函式用過啟動一個shell來解釋請求的命令。而pipe不需要啟動shell來解釋。 #include<unistd.h> int pipe(int file_descriptor[2]

如何解決linuxnavicat過期問題以及亂碼問題親測

一  解決navicat過期問題 1,如果之前已經安裝過navicat,那麼在home/使用者名稱 /  下將存在隱藏檔案 .navicat ,找到此檔案 2,將 .navicat檔案強制刪除 rm -rf .navicat 3, 進入到navicat解壓目錄,找到

詳解Linuxauto工具製作Makefile原始碼包製作篇

收藏於 2012-03-25 遷移自個人的百度空間 -------------------------------- 一、 概述 為了更好的製作configure與Makefile,我先把製作流程給寫在這裡,好讓大夥都有個心理準備。這裡只說流程,不做解釋。(附圖供參考) 1

C#伺服器端客戶端通訊客戶端

客戶端登陸介面 先定義三個視窗級變數(全域性變數) private TcpClient client;         private NetworkStream stream;         private

Linux檢視檔案和資料夾大小df&du

df 用法:df [選項]… [檔案]… 顯示每個檔案所在的檔案系統的資訊,預設顯示全部的檔案系統 常用選項 -h, –human-readable 大小顯示為人類易讀形式 (e.g., 1K

面向物件:執之手 偕老 第二期

嗨,大家好,我們的面向物件,執子之手  與子偕老 第二期來嘍,這一期可是大帥哥哦,單身妹子快點站起來拉 ,不然帥哥被搶跑啦,哪位程式設計師有單身的妹妹,快點快點‘下手’,這個是我同事哦今日推薦 No.2 出生年月:199703身高:184CM體重:65KG學歷:本科所在城市:

linux mysql 5.7 配置 my.cnfmysqld.cnf檔案位置 以及具體的配置方式

一、問題 mysql 5.7 版本,/etc/my.cnf  和 /etc/mysql/my.cnf  空空如也,需要自己新增需要的配置,而不能像之前一樣 只要去掉 #號註釋即可。 可以參考djCode的blogMySQL的my.cnf檔案(解決5.7.18下沒有my-d

LinuxG++編譯第一個C++程式Hello, world

安裝完各種環境工具之後(sudo apt install g++) 繼而新建一個空白文件Helloworl.cpp,貼上進最簡單的程式碼: #include<iostream>

linux生成動態連結庫並使用使用cmake

使用cmake生成庫主要要注意三個資料夾 (1)原始檔資料夾 (2)中間資料夾(編譯生成的.o等檔案的資料夾) (3)安裝資料夾(最終可用的庫所在的資料夾) 使用庫的步驟 (1)在工程檔案中包括庫函式的標頭檔案(可為絕對路徑,也可配置全域性環境變數用相對路徑)

基於Minifilter的檔案過濾驅動以及應用層通訊付程式碼

前一段時間在做一個檔案過濾系統, 為了配合公司的產品使用,希望對指定目錄禁止訪問。一開始使用的是sfilter的框架,很多事情需要自己做,建立過濾驅動的控制裝置,建立符號連結,設定IRP例程,設定FAST I/O例程,用這個框架做了一半,與應用層通訊比較麻煩,就

linux獲取磁碟使用情況的命令 df du

1、前言   在嵌入式裝置中,硬碟空間非常有限,在涉及到經常寫日誌的程序時候,需要考慮日誌的大小和刪除,不然很快就硬碟寫滿,導致日誌程式崩潰。為了捕獲硬碟寫滿的異常場景,我們需要在寫日誌過程中判斷硬碟空間的使用情況,根據硬碟的使用情況,就可以判斷是否寫滿了。如果將要寫滿了,

Linux————Linux批量建立使用者並設定密碼shell指令碼

在這裡分為兩種情況 1.有規則給定使用者名稱 2.給定無規則使用者名稱 3.刪除 一:以下是建立給定使用者名稱: 首先得給出給定使用者名稱,並逐個建立 for user in {u1,u2,u3,u