Linux -- 進程間通信【命名管道】

目錄

一、命名管道定義

二、命名管道創建

1、指令

2、系統調用

3、刪除

三、匿名管道和命名管道的區別

四、命名管道的打開規則

五、代碼示例

1、comm.hpp

2、server.cc

3、client.cc


一、命名管道定義

#?匿名管道存在以下核心限制:

  • 僅限親緣關系進程:只能用于父子進程等有血緣關系的進程間通信(如通過?fork()?創建的子進程)。
  • 單向通信:數據只能單向流動(一端寫,另一端讀),雙向通信需創建兩個管道。
  • 臨時性:存在于內存中,進程結束后自動銷毀。
  • 緩沖區有限:大小固定(通常為一個內存頁,如4KB),易寫滿阻塞。

# 引入命名管道的原因

為解決匿名管道的局限性,命名管道允許任意進程(無論是否有親緣關系)通過文件系統路徑訪問,實現跨進程通信。

# 由于匿名管道的局限性,如果我們想讓兩個毫不相關的進程間進行通信,就需要使用我們的命名管道。

# 命名管道與匿名管道都是只存在于內存中的文件,并不會向磁盤刷新,唯一不同的是匿名管道是通過父子進程看到同一份資源,而命名管道是通過路徑與文件名的方式找到同一份文件資源,因為我們知道路徑具有唯一性。

#?我們可以使用FIFO文件來做這項工作,它經常被稱為命名管道。命名管道是一種特殊類型的文件。


二、命名管道創建

1、指令

mkfifo <路徑名> ? # 例如:mkfifo /tmp/my_pipe

#?生成一個具名管道文件,權限默認受?umask?影響。

#?并且我們可以直接通過其進行echo和cat兩個進程間的通信:

2、系統調用

#?使用?mkfifo()?函數:

#include <sys/types.h>
#include <sys/stat.h>


int mkfifo(const char *pathname, mode_t mode); ?// 成功返回0,失敗返回-1

  • 參數
    • pathname:管道路徑/文件名(如?/tmp/my_pipe)。若以路徑的方式給出,則將命名管道文件創建在pathname路徑下,若以文件名的方式給出,則將命名管道文件默認創建在當前路徑下
    • mode:權限標志(如?0666?表示所有用戶可讀寫),它受默認掩碼umask的約束。
  • 后續操作
    • 需用?open()?打開管道(讀模式?O_RDONLY?或寫模式?O_WRONLY)。
    • 默認阻塞行為:讀端打開時寫端阻塞,反之亦然;可通過?O_NONBLOCK?設為非阻塞。

#?比如說我們可以通過該接口實現客戶端client與服務端server間的通信。

3、刪除

  • 命令行:rm <路徑名>?或?unlink <路徑名>
  • 程序內:unlink(pathname)


三、匿名管道和命名管道的區別

關鍵補充

  • 語義一致性:打開后兩者操作方式相同(如?read()/write())。
  • 網絡支持:命名管道可跨機器通信,匿名管道僅限本地。
  • 阻塞行為:兩者均受緩沖區影響,但命名管道可通過?O_NONBLOCK?靈活控制阻塞。


四、命名管道的打開規則


五、代碼示例

#?下面為了更好理解命名管道,我們直接來一段代碼,使用命名管道讓兩個無血緣關系的進程進行通信——一個進程寫一個進程讀。

#?這里client.cc和server.cc代表兩個沒有血緣關系的進程,在前面學習進程時我們知道,.cc文件跑起來就是一個進程,所以這里不多贅述。而我們命名管道的創建,以及打開管道文件進行操作的代碼則封裝在comm.hpp中。Makefile則是我們配置的自動化工具。

1、comm.hpp

#?下面我們就來在comm.hpp中將代碼封裝起來,首先需要將命名管道創建,最后結束通信后還需要將管道回收,因為命名管道不會隨進程的生命周期,所以需要我們手動回收。代碼如下:

class NamedFifo
{
public:NamedFifo(const std::string &path, const std::string &name): _path(path), _name(name){_filename = _path + "/" + _name;// 創建命名管道int n = mkfifo(_filename.c_str(), 0666);if(n < 0){std::cerr << "mkfifo failed" << std::endl;}else{std::cout << "mkfifo success" << std::endl;}}~NamedFifo(){// 回收命名管道int n = unlink(_filename.c_str());if(n < 0){std::cerr << "remove fifo failed" << std::endl;}else{std::cout << "remove fifo success" << std::endl;}}private:std::string _path;std::string _name;std::string _filename;
};

# 由于我們要實現一個進程寫,一個進程讀的單向通信,所以我們先規定,讓客戶端client.cc進程來寫,服務端server.cc進程來讀,那么讀寫操作我們還需要再封裝一個類,因為我們只要創建一個管道就行了。

# 如果都封裝在一個類中,那么客戶端和服務端都需要實例化出一個對象,才能對管道讀寫通信,但這樣就會創建兩個命名管道了,因為只要構造函數就會創建命名管道,而我們不需要兩個命名管道,我們只需要創建一個命名管道,然后服務端和客戶端分別以讀寫的方式打開這個管道文件就可以進行通信了,所以我們可以再封裝一個類來實現對打開的命名管道進行操作。代碼如下:

class Fileoper
{
public:Fileoper(const std::string &path, const std::string &name): _path(path), _name(name), _fd(-1){_filename = _path + "/" + _name;}void OpenForRead(){_fd = open(_filename.c_str(), O_RDONLY);if(_fd < 0){std::cerr << "open fifo failed" << std::endl;}else{std::cout << "open fifo success" << std::endl;}}void OpenForWrite(){_fd = open(_filename.c_str(), O_WRONLY);if(_fd < 0){std::cerr << "open fifo failed" << std::endl;}else{std::cout << "open fifo success" << std::endl;}}~Fileoper() {}private:std::string _path;std::string _name;std::string _filename;int _fd;
};

# 由于我們需要打開指定路徑的管道文件,所以成員變量仍然需要和NamedFifo類一樣,但是我們打開管道文件后,需要通過返回的文件描述符后續管理規管道文件,所以我們還需要一個成員變量_fd,來接收open返回的文件描述符。客戶端需要從管道寫入,服務端需要從管道讀取,所以客戶端以只寫的方式打開管道文件,而服務端以只讀的方式打開管道文件。但是打開之后我們客戶端和服務端還需要對管道進行讀寫操作,所以我們還需要分別實現一個寫函數和一個讀函數。代碼如下:

    void Write(){std::string message;while(true){std::cout << "Please Enter#";std::getline(std::cin, message);write(_fd, message.c_str(), message.size());}}void Read(){while(true){char buffer[1024];ssize_t n = read(_fd, buffer, sizeof(buffer)-1);if(n > 0){buffer[n] = 0;std::cout << "Client say#" << buffer << std::endl;}else if(n == 0){std::cout << "Client quit! me too!" << std::endl;break;}else{std::cerr << "read error" << std::endl;break;}}}

#?當然,通信結束之后我們需要關閉文件描述符。

    void Close(){close(_fd);}

#?我們定義兩個宏,想要在當前路徑下創建一個fifo的管道文件:

#define PATH "."
#define FILENAME "fifo"

# 再定義一個錯誤退出的宏:

// 在 C 語言中,\(反斜杠)在這里的作用是續行符,用于將一行代碼延續到下一行。
#define ERR_EXIT(m) \
do \
{ \perror(m); \exit(EXIT_FAILURE); \
} while(0)

# 源碼:

#pragma once#include <iostream>
#include <string>
#include <cstdio>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>#define PATH "."
#define FILENAME "fifo"// 在 C 語言中,\(反斜杠)在這里的作用是續行符,用于將一行代碼延續到下一行。
#define ERR_EXIT(m) \
do \
{ \perror(m); \exit(EXIT_FAILURE); \
} while(0)class NamedFifo
{
public:NamedFifo(const std::string &path, const std::string &name):_path(path),_name(name){_fifoname = _path + "/" + _name;// 將文件默認掩碼設置為0umask(0);// 新建管道int n = mkfifo(_fifoname.c_str(), 0666);if (n < 0){// std::cerr << "mkfifo error" << std::endl;// perror("mkfifo");// exit(1);ERR_EXIT("mkfifo");}else{std::cout << "mkfifo success" << std::endl;}}~NamedFifo(){// 刪除管道文件int n = unlink(_fifoname.c_str());if (n == 0){std::cout << "remove fifo success" << std::endl;}else{// std::cout << "remove fifo failed" << std::endl;ERR_EXIT("unlink");}}private:std::string _path;std::string _name;std::string _fifoname;
};class FileOper
{
public:FileOper(const std::string &path, const std::string &name): _path(path), _name(name),_fd(-1){_fifoname = _path + "/" + _name;}void OpenForRead(){// 打開// write方沒有執行open的時候,read方就要在open內部進行阻塞,直到有人把管道文件打開了,open才會返回_fd = open(_fifoname.c_str(), O_RDONLY); // 以讀方式打開命名管道文件if (_fd < 0){// std::cerr << "open fifo error" << std::endl;// return;ERR_EXIT("open");}std::cout << "open fifo success" << std::endl;}void OpenForWrite(){// write_fd = open(_fifoname.c_str(), O_WRONLY); // 以寫方式打開命名管道文件if (_fd < 0){// std::cerr << "Open fifo error" << std::endl;// return;ERR_EXIT("open");}std::cerr << "Open fifo success" << std::endl;}void Write(){// 寫入操作std::string message;int cnt = 1;pid_t id = getpid();while (true){std::cout << "Please Enter# ";std::getline(std::cin, message);message += ", message number: " + std::to_string(cnt++) + "[" + std::to_string(id) + "]";write(_fd, message.c_str(), message.size());}}void Read(){// 正常的readwhile (true){char buffer[1024];int number = read(_fd, buffer, sizeof(buffer) - 1);if (number > 0){// 讀取成功buffer[number] = 0; // 字符串末尾置\0std::cout << "Client say# " << buffer << std::endl;}else if (number == 0){std::cout << "client quit! me too!" << std::endl;break;}else{std::cerr << "read error" << std::endl;break;}}}void Close(){close(_fd);}~FileOper(){}private:std::string _path;std::string _name;std::string _fifoname;int _fd;
};

2、server.cc

#include "comm.hpp"int main()
{// 創建管道NamedFifo f(PATH, FILENAME);// 文件操作Fileoper reader(PATH, FILENAME);reader.OpenForRead();reader.Read();reader.Close();return 0;
}

3、client.cc

#include "comm.hpp"int main()
{Fileoper Writer(PATH, FILENAME);Writer.OpenForWrite();Writer.Write();Writer.Close();   return 0;
}

運行測試:

#?可以看到成功實現了兩個沒有血緣關系的進程的單向通信。

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

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

相關文章

LinuxC系統多線程程序設計

一.多線程程序設計1. 線程概述&#xff1a;1.1 什么是線程?線程是進程中的一個實體(組成單元),是系統進程調度的最小單元。一個進程至少具有一個線程&#xff0c;如果進程僅有一個線程&#xff0c;該線程就代表進程本身。把代表進程本身的線程稱為主線程&#xff0c;一個進程…

Vue3 + TS + MapboxGL.js 三維地圖開發項目

文章目錄 1. 安裝依賴 2. 新建 Map 組件(components/MapView.vue) 3. 在頁面中使用(views/Home.vue) 4. 效果說明 1. 安裝依賴 npm install mapbox-gl @types/mapbox-gl --save?? 注意:需要去 Mapbox 官網,申請一個 access token。 package.json {"name":…

【編程語言】Rust 入門

目錄 一、Rust 是什么&#xff1f;為什么選擇它&#xff1f; 二、環境搭建&#xff0c;邁出第一步 2.1 Windows 系統安裝步驟 2.2 macOS 系統安裝步驟 2.3 Linux 系統安裝步驟 2.4 安裝過程中的常見問題及解決方案 三、基礎語法&#xff0c;構建知識大廈的基石 3.1 變量…

Python 編碼與加密全解析:從字符編碼到 RSA 簽名驗證

在 Python 開發中&#xff0c;字符編碼&#xff08;如 UTF-8、GBK&#xff09;和 數據加密&#xff08;如 Base64、MD5、RSA&#xff09;是處理數據傳輸、存儲安全的核心技術。本文結合實戰代碼&#xff0c;從基礎的字符編解碼入手&#xff0c;逐步深入到加密算法的應用&#x…

關于shell命令的擴展

目錄 一、邏輯運算符 1. &&&#xff08;AND&#xff09; 2. ||&#xff08;OR&#xff09; 3. 組合使用&#xff1a;A && B || C 二、輸出與重定向 1. echo 輸出 2. 標準文件描述符&#xff08;FD&#xff09; 3. 重定向操作符 4. 同時重定向 stdout 和…

MySQL EXPLAIN 查看執行計劃詳解

MySQL 的 EXPLAIN 命令。這是一個分析和優化 SQL 查詢性能不可或缺的強大工具。它展示了 MySQL 如何執行一條 SQL 語句&#xff0c;包括如何使用索引、表連接順序、估計的行數等關鍵信息。1. 如何使用 EXPLAIN在你要分析的 SELECT 語句前加上 EXPLAIN 或 EXPLAIN FORMATJSON&am…

TensorFlow 面試題及詳細答案 120道(51-60)-- 模型保存、加載與部署

《前后端面試題》專欄集合了前后端各個知識模塊的面試題,包括html,javascript,css,vue,react,java,Openlayers,leaflet,cesium,mapboxGL,threejs,nodejs,mangoDB,SQL,Linux… 。 前后端面試題-專欄總目錄 文章目錄 一、本文面試題目錄 51. TensorFlow中保存和加…

從零開始學Shell編程:從基礎到實戰案例

從零開始學Shell編程&#xff1a;從基礎到實戰案例 文章目錄從零開始學Shell編程&#xff1a;從基礎到實戰案例一、認識Shell&#xff1a;是什么與為什么學1.1 Shell的定義1.2 常用Shell解釋器二、Shell編程快速入門&#xff1a;編寫第一個腳本2.1 步驟1&#xff1a;創建腳本文…

機器學習算法全景解析:從理論到實踐

機器學習算法全景解析&#xff1a;從理論到實踐引言機器學習作為人工智能的核心組成部分&#xff0c;正在深刻地改變我們的世界。從推薦系統到自動駕駛&#xff0c;從醫療診斷到金融風控&#xff0c;機器學習算法無處不在。本文將全面系統地介紹機器學習的主要算法類別及其核心…

week5-[二維數組]對角線

week5-[二維數組]對角線 題目描述 給定一個 nnn\times nnn 的正方形二維數組&#xff0c;輸出它兩條對角線上元素的和。 輸入格式 輸入共 n1n 1n1 行。 第 111 行 111 個正整數 nnn。 接下來 nnn 行&#xff0c;每行 nnn 個正整數 aija_{ij}aij? 表示這個二維數組。 輸出格式…

GoogLeNet:深度學習中的“卷積網絡變形金剛“

大家好&#xff01;今天我們要聊一個在深度學習領域掀起革命的經典網絡——GoogLeNet&#xff08;又稱Inception v1&#xff09;。這個由Google團隊在2014年提出的模型&#xff0c;不僅拿下了ImageNet競賽冠軍&#xff0c;更用"網絡中的網絡"設計理念徹底改變了卷積神…

筆記本電腦藍牙搜索不到設備-已解決

方法1打開疑難解答&#xff0c;選擇其他疑難解答&#xff0c;下劃選擇藍牙&#xff0c;點擊運行&#xff0c;電腦自行檢測并修復藍牙方法2右鍵此電腦&#xff0c;選擇管理&#xff0c;找到自己的藍牙設備。然后對箭頭指向的這個點擊右鍵&#xff0c;選擇《更新驅動程序》&#…

WPF 程序用戶權限模塊利用MarkupExtension實現控制控件顯示

工作記錄 ------------------------------------------------------------------------------------------------------- MarkupExtension:XAML標記擴展 實現了什么作用&#xff1a;通過擴展標記將一種輸入轉化為另一種類型的輸出 思路&#xff1a; 不直接設置控件的Visib…

SpringMVC相關梳理

SpringMVC 返回值類型&#xff08;一&#xff09;核心返回值類型分類視圖渲染類&#xff1a;用于跳轉并渲染頁面&#xff0c;如String&#xff08;指定視圖名&#xff09;、ModelAndView&#xff08;視圖 數據&#xff09;。數據返回類&#xff1a;用于返回數據&#xff08;而…

Docker化性能監控平臺搭建:JMeter+InfluxDB+Grafana全攻略

你作為一名DevOps工程師或測試專家&#xff0c;正在監控一個高并發微服務系統&#xff1a;突發流量峰值導致響應延遲&#xff0c;服務器CPU飆升&#xff0c;但你只能手動查看日志&#xff0c;優化起來像大海撈針。這時&#xff0c;DockerJMeterInfluxDBGrafana的“夢幻四重奏”…

Adobe Acrobat 中通過 JavaScript 調用 Web 服務

強大的JavaScript支持&#xff0c;允許用戶通過腳本自動化處理PDF文檔。本文將詳細介紹如何在Adobe Acrobat環境中使用JavaScript調用Web服務&#xff0c;包括基礎概念、實現方法、代碼示例以及常見問題解決方案。 第一部分&#xff1a;基礎概念與技術背景 1.1 Acrobat JavaScr…

SpringCloud OpenFeign 遠程調用(RPC)(三)

目錄 1 概念導入 2 添加依賴 3 在啟動類上添加注解 4 編寫對應的接口 5 注入并調用 6 日志 7 超時控制 8 超時重試 9 攔截器 10 Fallback兜底 1 概念導入 Spring Cloud OpenFeign Features :: Spring Cloud Openfeign 2 添加依賴 <!-- 遠程調用 --><depen…

【Flask】測試平臺開發,登陸重構

概述我們在開篇的時候實現了簡單的登陸功能&#xff0c;也實現了一個前后端聯調的登陸功能&#xff0c;但是你有沒有發現&#xff0c;那個登陸只是一個簡單的登陸&#xff0c;且密碼在接口返回的過程中是銘文密碼&#xff0c;在生產環境中使用肯定是不行的&#xff0c;一般密碼…

【Bluedroid】A2DP Source設備音頻數據讀取機制分析(btif_a2dp_source_read_callback)

本文聚焦Android 藍牙 A2DP Source設備的音頻數據讀取核心邏輯,深入解析關鍵回調函數btif_a2dp_source_read_callback的功能實現,包括從 HAL(硬件抽象層,支持 HIDL/AIDL 兩種傳輸方式)或 UIPC(用戶空間進程間通信)獲取音頻數據的路徑選擇機制,以及數據下溢(Underflow)…

多方調研賦能AI+智慧消防 豪越科技人工智能創新獲認可

8月26日&#xff0c;中國職業安全健康協會城市及社區安全發展專業委員會秘書長汪衛國以及常務副秘書長黃強亮等諸位領導到訪委員單位豪越科技&#xff0c;展開了實地的調研活動并給予相關指導。此次調研著重于了解豪越科技自主研發的“AI消防救援一體化安全管控平臺”&#xff…