IPC 進程間通信(一):管道(匿名管道進程池)

1. 初識進程間通信

1.1進程間通信的目的:

1、數據傳輸:一個進程需要將它的數據發送給另一個進程
2、資源共享:多個進程之間共享同樣的資源
3、通知事件:一個進程需要向另一個或一組進程發送消息,通知它(它們)發生了某種事件(如進程終止 時要通知父進程)

4、進程控制:有些進程希望完全控制另一個進程的執行(如Debug進程),此時控制進程希望能夠攔截另 一個進程的所有陷入和異常,并能夠及時知道它的狀態改變

1.2 為什么要有進程間通信

為了實現兩個或者多個進程實現數據層面的交互,因為進程獨立性的存在,導致進程通信的成本比較高? ?很多場景下需要多個進程協同工作來完成要求。如下:

  • 這條命令首先使用?cat??讀取?log.txt?的內容,然后通過管道?(|) 將輸出傳遞給?grep?命令。grep?用于搜索指定的字符串。
  • grep Hello:??這個命令搜索包含 "Hello" 的行。

1.3進程間通信的方式

管道通過文件系統通信)

  • 匿名管道pipe
  • 命名管道?

System V IPC 聚焦在本地通信

  • System V 消息隊列
  • System V 共享內存
  • System V 信號量

POSIX IPC 讓通信可以跨主機)

  • 消息隊列
  • 共享內存
  • 信號量
  • 互斥量
  • 條件變量
  • 讀寫鎖

注意:

  • ?System V?標準需要重新構建操作系統代碼來實現進程通信,比較繁瑣。
  • 在?System V?標準出現之前,而「管道通信」是直接復用現有操作系統的代碼
  • 現在本地通信已經被網絡通信取代,所以進程間通信方式只重點介紹管道通信和共享內存通信

知識補充:

(1)進程間通信的本質:必須讓不同的進程看到同一份“資源”(資源:特定形式的內存空間)

(2)這個資源誰提供?一般是操作系統

  • 為什么不是我們兩個進程中的一個呢?假設一個進程提供,這個資源屬于誰?
  • 這個進程獨有,破壞進程獨立性,所以要借用第三方空間

(3)我們進程訪問這個空間,進行通信,本質就是訪問操作系統!

  • 進程代表的就是用戶,資源從創建,使用(一般),釋放--系統調用接口!

2.匿名管道

2.1.什么是管道

進程可以通過 讀/寫 的方式打開同一個文件,操作系統會創建兩個不同的文件對象 file,但是文件對象 file 中的內核級緩沖區、操作方法集合等并不會額外創建,而是一個文件的文件對象的內核級緩沖區、操作方法集合等通過指針直接指向另一個文件的內核級緩沖區、操作方法集合等。

  • 這樣以讀方式打開的文件和以寫方式打開的文件共用一個?內核級緩沖區

  • 進程通信的前提是不同進程看到同一份共享資源

所以根據上述原理,父子進程可以看到同一份共享資源:被打開文件的內核級緩沖區。父進程向被打開文件的內核級緩沖區寫入,子進程從被打開文件的內核級緩沖區讀取,這樣就實現了進程通信!

  • 這里也將被打開文件的內核級緩沖區稱為?「?管道文件」,而這種由文件系統提供公共資源的進程間通信,就叫做「?管道?

注意:

此外,管道通信只支持單向通信,即只允許父進程傳輸數據給子進程,或者子進程傳輸數據給父進程。

  • 當父進程要傳輸數據給子進程時,就可以只使用以寫方式打開的文件的管道文件,關閉以讀方式打開的文件,

  • 同樣的,子進程只是用以讀方式打開的文件的管道文件,關閉掉以寫方式打開的文件。

  • 父進程向以寫方式打開的文件的管道文件寫入,子進程再從以讀方式打開的文件的管道文件讀取,從而實現管道通信。如果是要子進程向父進程傳輸數據,同理即可。

管道特點總結:

  • 一個進程將同一個文件打開兩次,一次以寫方式打開,另一次以讀方式打開。此時會創建兩個struct file,而文件的屬性會共用,不會額外創建
  • 如果此時又創建了子進程,子進程會繼承父進程的文件描述符表,指向同一個文件,把父子進程都看到的文件,叫管道文件
  • 管道只允許單向通信

  • 管道里的內容不需要刷新到磁盤

2.2 創建匿名管道

匿名管道:沒有名字的文件(struct file)

匿名管道用于父子間通信,或者由一個父創建的兄弟進程(必須有“血緣“)之間進行通信

#include <unistd.h>
原型:int pipe(int fd[2]);功能:創建匿名管道
參數 fd:文件描述符數組,其中fd[0]表示讀端, fd[1]表示寫端
返回值:成功返回0,失敗返回錯誤代碼

使用如下:

int main()
{// 1. 創建管道int fds[2] = {0};int n = pipe(fds); // fds: 輸出型參數if(n != 0){std::cerr << "pipe error" << std::endl;return 1;}std::cout << "fds[0]: " << fds[0] << std::endl;std::cout << "fds[1]: " << fds[1] << std::endl;return 0;
}// 運行如下:
fds[0]: 3
fds[1]: 4
  • 輸出型參數:文件的描述符數字帶出來,讓用戶使用-->3,4,因為0,1,2分別被stdin,stdout,stderr占用。

2.3 匿名管道通信案例(父子通信)

注意:匿名管道需要在創建子進程之前創建,因為只有這樣才能復制到管道的操作句柄,與具有親緣關系的進程實現訪問同一個管道通信

情況一:管道為空 && 管道正常(read 會阻塞【read 是一個系統調用】)

具體代碼演示如下:(子進程寫入,父進程讀取)

#include <iostream>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <cstdlib>// 父進程 -- 讀取
// 子進程 -- 寫入
void write(std::string &info, int cnt)
{info += std::to_string(getpid());info += ", cnt: ";info += std::to_string(cnt);info += ')';
}int main()
{// 1. 創建管道int fds[2] = {0};int n = pipe(fds); // fds: 輸出型參數if (n != 0){std::cerr << "pipe error" << std::endl;return 1;}// 2. 創建子進程pid_t id = fork();if (id < 0){std::cerr << "fork error" << std::endl;return 2;}else if (id == 0){// 子進程// 3. 關閉不需要的 fd, 關閉 readint cnt = 0;while (true){close(fds[0]);std::string message = "(hello linux, pid: ";write(message, cnt);::write(fds[1], message.c_str(), message.size());cnt++;sleep(2);}exit(0);}else{// 父進程// 3. 關閉不需要的 fd, 關閉 writeclose(fds[1]);char buffer[1024];while(true){ssize_t n = ::read(fds[0], buffer, 1024);if(n > 0){buffer[n] = 0;std::cout << "child->father, message: " << buffer << std::endl;}}// 記錄退出信息pid_t rid = waitpid(id, nullptr, 0);std::cout << "father wait chile success" << rid << std::endl;}return 0;
}

從上面可以知道:

  1. 子進程寫入的信息是變化的信息
  2. 父進程打印信息的時間間隔和子進程一樣,那么子進程沒傳入信息的時候,父進程處于阻塞?--> (IPC 本質:先讓不同的進程,看到同一份資源,可以保護共享資源)

情況二:管道為滿 && 管道正常(write 會阻塞【write 是一個系統調用】)

如下對代碼做點修改(紅框內的代碼)

管道有上限,Ubuntu?-> 64 KB

如果我們讓父進程正常讀取,那么結果又是怎樣的呢?

當我們到 65536 個字節時,管道已滿,父進程讀取了管道數據,子進程會繼續進行寫入,然后進行繼續讀取,就有點數據溢出的感覺

情況三:管道寫端關閉?&& 讀端繼續(讀端讀到0,表示讀到文件結尾)

代碼修改如下:

else if (id == 0)
{int cnt = 0, total = 0;while (true){close(fds[0]);std::string message = "h";// fds[1]total += ::write(fds[1], message.c_str(), message.size());cnt++;std::cout << "total: " << total << std::endl; // 最后寫到 65536 個字節sleep(2);break; // 寫端關閉}exit(0);
}
else
{// 父進程// 3. 關閉不需要的 fd, 關閉 writeclose(fds[1]);char buffer[1024];while (true) {sleep(1);ssize_t n = ::read(fds[0], buffer, 1024);if (n > 0) {buffer[n] = 0;std::cout << "child->father, message: " << buffer << std::endl;}else if (n == 0) {std::cout << "n: " << n << std::endl;std::cout << "child quit??? me too " << std::endl;break;}std::cout << std::endl;}pid_t rid = waitpid(id, nullptr, 0);std::cout << "father wait chile success" << rid << std::endl;
}

結論:如果寫端關閉,讀端讀完管道內部數據,再讀取就會讀取到返回值 0,表示對端關閉,也表示讀到文件結尾

情況四:管道寫端正常?&& 讀端關閉(OS 會直接殺掉寫入進程)

情況二:

如何殺死呢?

a. OS 會給 目標進程發送信號:13) SIGPIPE

b. 證明如下;

else if (id == 0)
{int cnt = 0, total = 0;while (true){close(fds[0]);std::string message = "h";// fds[1]total += ::write(fds[1], message.c_str(), message.size());cnt++;std::cout << "total: " << total << std::endl; // 最后寫到 65536 個字節sleep(2);}exit(0);
}
else
{close(fds[1]);char buffer[1024];while (true){sleep(1);ssize_t n = ::read(fds[0], buffer, 1024);if (n > 0){buffer[n] = 0;std::cout << "child->father, message: " << buffer << std::endl;}else if (n == 0){std::cout << "n: " << n << std::endl;std::cout << "child quit??? me too " << std::endl;break;}close(fds[0]); // 讀端關閉break;std::cout << std::endl;}// 記錄退出信息int status = 0;pid_t rid = waitpid(id, &status, 0);std::cout << "father wait chile success: " << rid << " exit code: " <<((status << 8) & 0xFF) << ", exit sig: " << (status & 0x7F) << std::endl;
}
小結

🦋 管道讀寫規則

  • 當沒有數據可讀時
    • read 調用阻塞,即進程暫停執行,一直阻塞等待
    • read 調用返回-1,errno值為EAGAIN。
  • 當管道滿的時候
    • write 調用阻塞,直到有進程讀走數據
    • 調用返回-1,errno值為 EAGAIN
  • 如果所有管道寫端對應的文件描述符被關閉,則read返回0
  • 如果所有管道讀端對應的文件描述符被關閉,則write操作會產生信號

2.4 匿名管道特性

  1. 匿名管道:只用來進行具有血緣關系的進程之間,進行通信,常用于父子進程之間通信

  2. 管道文件的生命周期是隨進程的

  3. 管道內部,自帶進程之間同步的機制(多執行流執行代碼的時候,具有明顯的順序性)

?????4.管道文件在通信的時候,是面向字節流的。(寫的次數和讀取的次數不是一一匹配的)

  1. 管道的通信模式,是一種特殊的半雙工模式,數據只能向一個方向流動;需要雙方通信時,需要建立起兩個管道

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/73725.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/73725.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/73725.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Linux-數據結構-單鏈表練習-雙鏈表

一.單鏈表練習和一些功能實現 【1】單鏈表實現字典查詢 &#xff08;1&#xff09;定義存放數據的結構體&#xff0c;在每次向里面存放數據時候需要清空 &#xff08;2&#xff09;對字典進行切割 空格切割字母&#xff0c;再從剩余里切割到解釋&#xff0c;這里windows的txt文…

網絡爬蟲相關

一、爬蟲的基礎內容 1、基本概念和用途 1.1、概念&#xff1a; 模擬瀏覽器、發送請求&#xff0c;獲取響應。&#xff08;只能獲取客戶端所展示出來的數據&#xff09; 1.2、特點&#xff1a;知識碎片化&#xff0c;針對不同的網站對應的方法也不同。 爬蟲&#xff1a;模擬…

Lora 中 怎么 實現 矩陣壓縮

Lora 中 怎么 實現 矩陣壓縮 1. 導入必要的庫 import torch import re from datasets import Dataset from transformers import AutoTokenizer, AutoModelForCausalLM, TrainingArguments, Trainer, \get_cosine_schedule_with_warmup, EarlyStoppingCallback from peft

golang-嵌套結構體

結構體嵌套 golang中沒有類&#xff0c;他通過結構體來實現其他編程語言中類的相關功能。 具名結構體 基本語法 基本語法 golang的結構體嵌套特別簡單。 type 結構體類型1 struct{字段 類型1字段 類型2 }//這樣就實現了結構體的嵌套 type 結構體類型2 struct{字段 類型1字…

基于Spring Boot的大學校園生活信息平臺的設計與實現(LW+源碼+講解)

專注于大學生項目實戰開發,講解,畢業答疑輔導&#xff0c;歡迎高校老師/同行前輩交流合作?。 技術范圍&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬蟲、數據可視化、安卓app、大數據、物聯網、機器學習等設計與開發。 主要內容&#xff1a;…

【 利用socket來實現簡單遠控】

利用socket來實現簡單遠控 &#x1f539; 免責聲明?? 重要提示一、什么是socket&#xff1f;二、如何使用socket來實現兩臺計算機之間的通信&#xff1f;服務端1、首先需要創建一個socket&#xff1b;2、綁定IP以及端口3、開啟監聽4、接受客戶端連接5、客戶端連接上之后就是命…

數據可視化在特征分布對比中的應用

數據可視化在特征分布對比中的應用 1. 引言 在機器學習系統開發和維護過程中,特征分布對比是評估數據質量和模型魯棒性的關鍵環節。當訓練數據與測試數據分布存在偏差,或生產環境中的數據分布隨時間發生變化時,模型性能通常會顯著下降。有效的數據可視化不僅能幫助檢測這些…

依賴倒置 DIP、依賴注入 DI、控制反轉 IoC 和工廠模式

1. 依賴倒置 依賴倒置原則&#xff08;Dependency Inversion Principle, DIP&#xff09;是 SOLID 原則中的一項&#xff0c;其核心思想是通過抽象解耦高層模塊和低層模塊&#xff0c;使二者都依賴于抽象而非具體實現。 依賴反轉/倒置的體現&#xff1a;傳統依賴方向是高層模塊…

UnitTest框架管理測試用例——python自動化測試

UnitTest框架 UnitTest是Python自帶一個單元測試框架&#xff0c;常用它來做單元測試。 注意:對于測試來說&#xff0c;UnitTest框架的作用是 自動化腳本(用例代碼)執行框架————(使用UnitTest框架來管理 運行多個測試用例的) 為什么使用UnitTest框架 能夠組織多個用例去執…

Vue 過濾器深度解析與應用實踐

文章目錄 1. 過濾器概述1.1 核心概念1.2 過濾器生命周期 2. 過濾器基礎2.1 過濾器定義2.2 過濾器使用 3. 過濾器高級用法3.1 鏈式調用3.2 參數傳遞3.3 動態過濾器 4. 過濾器應用場景4.1 文本格式化4.2 數字處理4.3 數據過濾 5. 性能優化與調試5.1 性能優化策略5.2 調試技巧 6. …

ngx_http_module_t

定義在 src\http\ngx_http_config.h typedef struct {ngx_int_t (*preconfiguration)(ngx_conf_t *cf);ngx_int_t (*postconfiguration)(ngx_conf_t *cf);void *(*create_main_conf)(ngx_conf_t *cf);char *(*init_main_conf)(ngx_conf_t *cf, void *conf);…

每日定投40刀BTC(9)20250312 - 20250315

定投截圖 區塊鏈相關新聞 BTC價格一度跌破8萬美元 3月14日&#xff0c;BTC價格盤中跌破8萬美元&#xff0c;最低報79,954.60美元&#xff0c;日內下跌1.34%&#xff0c;市場情緒一度轉為謹慎 BTC價格波動背后的原因 經濟環境變化、市場情緒波動以及政策監管動態是導致BTC價…

Matlab 汽車二自由度轉彎模型

1、內容簡介 Matlab 187-汽車二自由度轉彎模型 可以交流、咨詢、答疑 2、內容說明 略 摘 要 本文前一部分提出了側偏角和橫擺角速度作為參數。描述了車輛運動的運動狀態&#xff0c;其中文中使用的參考模型是二自由度汽車模型。汽車速度被認為是建立基于H.B.Pacejka的輪胎模…

CentOS 6 YUM源切換成國內yum源

由于 CentOS 6 已于 2020 年 11 月進入 EOL&#xff08;End of Life&#xff09;&#xff0c;官方軟件源已不再提供更新&#xff0c;因此你可能會遇到 yum makecache 命令失敗的問題。以下是解決該問題的詳細步驟&#xff1a; ### 解決方案 1. **備份原有 yum 源文件** bash …

Leetcode 3483. Unique 3-Digit Even Numbers

Leetcode 3483. Unique 3-Digit Even Numbers 1. 解題思路2. 代碼實現 題目鏈接&#xff1a;3483. Unique 3-Digit Even Numbers 1. 解題思路 這一題其實是一個easy的題目&#xff0c;因為限制條件有限&#xff0c;最暴力的方法就是直接遍歷一下100到999的全部數字&#xff…

《基于深度學習的高分衛星圖像配準模型研發與應用》開題報告

目錄 1. 選題的背景和意義 1.1 選題的背景 1.2 國內外研究現狀 1.3 發展趨勢 2&#xff0e;研究的基本內容 2.1 主要研究內容 &#xff08;1&#xff09;訓練與測試數據集構建 &#xff08;2&#xff09;基于深度學習的高精度衛星影像配準模型 &#xff08;3&#xff0…

【Python 算法零基礎 1.線性枚舉】

我裝作漠視一切&#xff0c;以為這樣就可以不在乎 —— 25.3.17 一、線性枚舉的基本概念 1.時間復雜度 線性枚舉的時間復雜度為 O(nm)&#xff0c;其中 n是線性表的長度。m 是每次操作的量級&#xff0c;對于求最大值和求和來說&#xff0c;因為操作比較簡單&#xff0c;所以 …

前端性能優化回答思路

前端性能優化是面試中經常涉及的一個話題&#xff0c;面試官通常希望了解你在實際項目中如何處理性能瓶頸&#xff0c;如何識別和優化性能問題。以下是一些前端性能優化的常見問題以及你可以用來回答的思路&#xff1a; 如何提升頁面加載速度&#xff1f; 回答思路&#xff1…

02-Canvas-fabric.ActiveSelection

fabric.ActiveSelection fabric.ActiveSelection 用于表示當前選中的多個對象&#xff08;即多選狀態&#xff09;。 當用戶在畫布上選擇多個對象時&#xff0c;Fabric.js 會自動將這些對象包裝在fabric.ActiveSelection 實例中&#xff0c;以便統一操作&#xff08;如移動、縮…

Leetcode——151.反轉字符串中的單詞

題解一 思路 最開始的想法是把一個字符串分為字符串數組&#xff0c;但是不知道一共有幾個單詞&#xff08;當時沒想起來split()&#xff09;&#xff0c;所以選擇了用ArrayList儲存字符串&#xff0c;在輸出時沒有考慮ArrayList可以存儲空字符串&#xff0c;所以最開始的輸出…