通信方式:命名管道

一、命名管道

1. 命名管道的原理

有了匿名管道,理解命名管道就非常簡單了。

對于普通文件而言兩個進程打開同一個文件,OS是不會將文件加載兩次的,這兩個進程都會指向同一個文件,那么,也就享有同一份 inode 和 文件緩沖區。這在一定層面上也是實現了進程之間共享數據。但是,普通文件是需要向磁盤上進行刷新的

所以,才有了一種特殊的文件管道,管道(包含匿名管道和命名管道)是不需要向磁盤上進行刷新的,命名管道是文件系統中的一種特殊類型文件。管道就是解決進程之間共享數據的問題的OS會為每個進程分配一個 struct file 結構體的,但是它們指向同一個 struct pipe_inode_info ,這個結構體里包含環形緩沖區,這就保證了不同的進程之間可以訪問同一份數據

2. 命名管道的操作

//命令
mkfifo //創建命名管道

在這里插入圖片描述

可以看到,fifo 文件的大小是 0,這也說明了數據是不會刷新到磁盤上的

//系統調用
//mode代表權限
//成功返回0,失敗返回-1
int mkfifo(const char* pathname, mode_t mode);//創建命名管道
//成功返回0,失敗返回-1
int unlink(const char* pathname); //刪除命名管道

3. 命名管道的實現

在這里插入圖片描述

. client.cpp

#include "NamePipe.hpp"int main()
{NamePipe name_pipe(fifoname);name_pipe.OpenForWrite();std::string line;while (true){std::cout << "Please Enter# ";std::getline(std::cin, line);name_pipe.Write(line);}name_pipe.Close();return 0;
}

. common.hpp

#ifndef __COMMON_HPP__
#define __COMMON_HPP__#include<iostream>
#include<string>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>std::string fifoname = "fifo";
mode_t mode = 0666;#define SIZE 128#endif

. NamePipe.hpp

#include "common.hpp"const int gdefaultfd = -1;
class NamePipe
{
public:NamePipe(std::string &name, int fd = gdefaultfd) : _name(name), _fd(fd){}bool Create(){// server 創建管道int n = mkfifo(fifoname.c_str(), mode);if (n < 0){perror("mkfifo fail");return false;}return true;}bool OpenForRead(){// 命名管道的操作特點:打開一端,另一端沒打開的時候,open會阻塞_fd = open(fifoname.c_str(), O_RDONLY);if (_fd < 0){perror("server open fail");return false;}return true;}bool OpenForWrite(){_fd = open(fifoname.c_str(), O_WRONLY);if (_fd < 0){perror("client open fail");return false;}return true;}bool Read(std::string *out){char buffer[SIZE] = {0};ssize_t num = read(_fd, buffer, sizeof(buffer) - 1);if (num > 0){buffer[num] = 0;// std::cout 也有緩沖區,需要刷新*out = buffer;}else if (num == 0){// client quit,read讀到文件末尾,返回0return false;}else{return false;}return true;}void Write(const std::string &in){write(_fd, in.c_str(), in.size());}void Close(){close(_fd);}void Remove(){unlink(fifoname.c_str()); // 刪除管道文件}~NamePipe(){}private:std::string _name;int _fd;
};

. server.hpp

#include "NamePipe.hpp"int main()
{NamePipe name_pipe(fifoname);name_pipe.Create();name_pipe.OpenForRead();std::cout << "open file success" << std::endl;std::string message;while (true){bool res = name_pipe.Read(&message);if(!res)break;std::cout << "Client Say@ " << message << std::endl;}name_pipe.Close();name_pipe.Remove();return 0;
}

. Makefile

.PHONY:all
all:client serverclient:client.cppg++ -o $@ $^ -g -std=c++11
server:server.cppg++ -o $@ $^ -g -std=c++11.PHONY:clean
clean:rm -f clientrm -f server

命名管道主要解決毫無關系的進程之間,進行文件級進程通信

匿名管道和命名管道剩下的特點是一樣的。

server 和 client 之所以能夠看到同一份資源,是因為它們打開的是同一份文件,該文件路徑相同,inode相同,所以能夠共享數據

看到這里,大家應該明白了,匿名管道和命名管道是非常相似的匿名管道是內存級文件,是內存模擬出來的,而命名管道是磁盤上的一種特殊文件類型,是有名字的。正是因為有名字,所以才叫做命名管道

二、System V共享內存

1. System V的原理

還記得動態庫的加載嗎進程會把自己依賴的動態庫加載到物理內存里,通過頁表建立虛擬地址和物理地址的映射關系,而動態庫就被映射到了虛擬地址空間中的共享區中

那么,一個進程需要的動態庫會被映射到自己的共享區中,另一個進程也需要這個動態庫呢?那么,這個進程也會把動態庫映射到自己的共享區中,OS不會將這個動態庫加載兩次,兩個進程用的是同一個動態庫。這從某種意義上來說,不就是OS給動態庫開辟了一塊物理內存,這塊物理內存被映射到了進程虛擬地址空間中的共享區上嗎。這不就是兩個進程之間共享內存了嘛

所以,我們想實現共享內存,就必須

1.創建共享內存

2.建立虛擬地址和物理地址的映射關系

3.刪除共享內存

動態庫的詳細加載過程請看這篇文章:從ELF到進程間通信:剖析Linux程序的加載與交互機制。

在這里插入圖片描述

假設進程A,進程B之間通過共享內存通信,如果進程C,D也需要通信呢,許多進程之間都需要共享內存通信呢?那么,是不是會有許多共享內存,OS要不要進行管理呢?肯定是要的。先描述再組織

2. System V的操作

//key表示共享內存段的唯一鍵值(類似文件名)
//size是共享內存段的大小,一般建議是4KB的整數倍
//shmflg 創建方式和訪問權限
//成功返回共享內存標識符,失敗返回-1(類似于文件描述符fd)
//shmflg的選項,介紹兩個
//IPC_CREAT,單獨使用,共享內存不存在,則創建,已存在,直接獲取
//IPC_EXCL,不能單獨使用
//一起使用,共享內存不存在,則創建,已存在,返回出錯
int shmget(key_t key, size_t size, int shmflg)//創建或獲取一個共享內存
//這兩個參數是可以隨便寫的
//成功返回 key,失敗返回-1
key_t ftok(const char* pathname, int proj_id)//形成唯一的鍵值,傳入shmget函數中

共享內存的生命周期不隨進程,隨內核

所以,共享內存需要我們自己手動釋放資源。可以使用以下命令查看共享內存的信息以及刪除共享內存

ipcs -m //查看所有共享內存段的信息
ipcrm -m shmid //刪除指定的共享內存段

在這里插入圖片描述

可以看到,第一次共享內存是創建出來的,第二次就創建失敗,這也證明了共享內存的生命周期不隨進程。當刪除了指定的共享內存,就可以再次創建共享內存了

但這種刪除共享內存的方式還是太麻煩了,我們希望進程結束時就刪除掉共享內存。

文件 = 文件屬性 + 文件內容

共享內存 = 內存塊 + 共享內存的 struct(shm的屬性)

//shmid就是共享內存標識符
//cmd可以使用IPC_RMID,表示立即刪除
//buf設置為NULL
//0表示成功, -1表示失敗
int shmctl(int shmid, int cmd, struct shmid_ds* buf); //對共享內存的屬性做操作
//shmaddr 設置為 nullptr,讓內核自動選擇映射的虛擬地址
//shmflg 設置為 0,缺省,繼承共享內存的權限
//成功了返回共享內存映射到當前進程虛擬地址空間的具體地址,失敗返回-1
void* shmat(int shmid, const void* shmaddr, int shmflg); //將共享內存與虛擬地址進行映射

在這里插入圖片描述
在這里插入圖片描述

這是為什么呢?要想掛接成功,就必須對共享內存設置權限才可以

在這里插入圖片描述
在這里插入圖片描述

可以看到,映射成功了,并且共享內存有了權限,nattch 掛接的進程數量也變為了 1

在刪除共享內存之前,虛擬地址空間依然和共享內存有著映射關系,但這并不是我們想要的,所以,在刪除共享內存之前,需要先將虛擬地址空間和共享內存去關聯,然后再刪除共享內存

//成功返回0,失敗返回-1
int shmdt(const void* shmaddr); //將共享內存與虛擬地址空間進行去關聯

我們說過,OS內會有許多共享內存,所以對于共享內存是需要進行管理的,在用戶層面上提供了一種結構體來描述共享內存。我們來看一下。

在這里插入圖片描述
在這里插入圖片描述

我們可以獲取里面的屬性看一看。不過在此之前,需要了解 shmctl 函數的一個選項。

在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述

共享內存的特征總結

1.生命周期隨內核

2.共享內存是 IPC 中速度最快的(因為共享內存寫入數據和讀取數據不需要使用系統調用,只需要使用指針就可以完成,系統調用也是有成本的)

3.共享內存沒有同步互斥機制,來對多個進程的訪問進行協同

3. 共享內存的實現

. client.cpp

#include "Shm.hpp"int main()
{SharedMemory shm;shm.Get();// sleep(5);shm.Attach();// sleep(5);// 通信shm.SetZero();char ch = 'A';int cnt = 10;while(cnt--){std::cout << "client 開始寫入" << std::endl;shm.AddChar(ch);ch++;sleep(1);}shm.Detach();// sleep(5);return 0;
}

. Shm.hpp

#ifndef __SHM_HPP__
#define __SHM_HPP__#include <iostream>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>#define gdefaultsize 4096std::string gpathname = ".";
int gproj_id = 0x66;class SharedMemory
{
private:bool CreateHelper(int flags){// 形成唯一的鍵值_key = ftok(gpathname.c_str(), gproj_id);if (_key < 0){perror("ftok fail");return false;}printf("形成鍵值成功:0x%x\n", _key);// 獲取共享內存_shmid = shmget(_key, _size, flags); // 創建全新的共享內存if (_shmid < 0){perror("shmget fail");return false;}printf("shmid:%d\n", _shmid);return true;}public:SharedMemory(size_t size = gdefaultsize) : _key(0), _size(size), _shmid(-1),_start_addr(nullptr), _windex(0), _rindex(0),_num(nullptr), _data_start(nullptr){}bool Create(){return CreateHelper(IPC_CREAT | IPC_EXCL | 0666);}bool Get(){return CreateHelper(IPC_CREAT);}bool RemoveShm(){// 刪除共享內存int n = shmctl(_shmid, IPC_RMID, NULL);if (n < 0){perror("shmctl fail");return false;}std::cout << "刪除shm成功" << std::endl;return true;}bool Attach(){// 將共享內存映射到虛擬地址空間中//共享內存需要權限才能掛接在指定的進程_start_addr = shmat(_shmid, nullptr, 0);if ((long long)_start_addr == -1){perror("shmat fail");return false;}_num = (int *)_start_addr;_data_start = (char *)_start_addr + sizeof(int);std::cout << "共享內存映射到進程的虛擬地址空間中" << std::endl;return true;}bool Detach(){// 共享內存與虛擬地址空間進行去關聯int n = shmdt(_start_addr);if (n < 0){perror("Detach fail");return false;}std::cout << "虛擬地址空間與共享內存進行去關聯" << std::endl;return true;}void SetZero(){*_num = 0;}void AddChar(char ch){if (*_num == _size - sizeof(int))return;((char *)_data_start)[_windex++] = ch;_data_start[_windex] = 0;_windex %= (_size - sizeof(int));(*_num)++;// std::cout << "Debug: " << _data_start[_windex - 1] << " _num = " << *_num << std::endl;}void PopChar(char *out){// std::cout << " _num = " << *_num;if (*_num == 0)return;*out = _data_start[_rindex++];_rindex %= (_size - sizeof(int));(*_num)--;// std::cout << "client read: " << _data_start[_rindex - 1] << std::endl;}void PrintAttr(){struct shmid_ds ds;int n = shmctl(_shmid, IPC_STAT, &ds);if(n < 0){perror("PrintAttr shmctl fail");return;}printf("key:0x%x\n", ds.shm_perm.__key);printf("shm_atime:%ld\n",ds.shm_atime);printf("shm_nattch:%ld\n",ds.shm_nattch);}int GetCount(){return *_num;}~SharedMemory(){}private:key_t _key;size_t _size;int _shmid;void *_start_addr;int *_num;char *_data_start;int _windex;int _rindex;
};#endif

. server.cpp

#include "Shm.hpp"int main()
{SharedMemory shm;shm.Create();// sleep(5);shm.Attach();shm.PrintAttr();sleep(2);// 通信char ch;while (true){if(!shm.GetCount()) break;shm.PopChar(&ch);printf("server getchar:%c\n", ch);sleep(1);}shm.Detach();// sleep(5);shm.RemoveShm();// sleep(5);return 0;
}

. Makefile

.PHONY:all
all:client serverclient:client.cppg++ -o $@ $^ -g -std=c++11
server:server.cppg++ -o $@ $^ -g -std=c++11.PHONY:clean
clean:rm -f clientrm -f server

今天的文章分享到此結束,覺得不錯的給個一鍵三連吧。

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

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

相關文章

如何將數據庫快速接入大模型實現智能問數,實現chatbi、dataagent,只需短短幾步,不需要配置工作流!

智能問數系統初始化操作流程 一、系統初始化與管理員賬號創建登錄與初始化提示&#xff1a;首次訪問系統登錄頁&#xff0c;若系統未初始化&#xff0c;會彈出 “系統未完成初始化&#xff0c;請初始化管理員賬號” 提示&#xff0c;點擊【去創建】。填寫管理員信息&#xff1a…

告別手寫文檔!Spring Boot API 文檔終極解決方案:SpringDoc OpenAPI

在前后端分離和微服務盛行的今天&#xff0c;API 文檔是團隊協作的“通用語言”。一份清晰、準確、實時同步的文檔&#xff0c;能極大提升開發和聯調效率。然而&#xff0c;手動編寫和維護 API 文檔&#xff08;如 Word、Markdown 或 Postman&#xff09;是一場永無止境的噩夢—…

N4200EX是一款全智能超聲波檢測儀產品簡析

N4200EX是一款全智能超聲波檢測儀&#xff0c;適用于石油、石化、天然氣、氣體生產等行業的壓力管路、閥門、設備的各種防爆場合氣體泄漏、真空泄漏、閥門內漏檢測。●本安防爆設計&#xff0c;防爆、防塵、防水、抗摔。●適應惡劣環境&#xff0c;可在-25℃超低溫環境檢測&…

NestJS @Inject 裝飾器入門教程

一、核心概念解析 1.1 依賴注入&#xff08;DI&#xff09;的本質 依賴注入是一種設計模式&#xff0c;通過 IoC&#xff08;控制反轉&#xff09;容器管理對象生命周期。在 NestJS 中&#xff0c;Injectable() 標記的類會被容器管理&#xff0c;而 Inject() 用于顯式指定依賴項…

網絡地址詳解

子網劃分詳解&#xff1a;從 IP 地址結構到實際應用 在計算機網絡中&#xff0c;子網劃分是一項關鍵的技術&#xff0c;它能幫助我們更高效地管理 IP 地址資源&#xff0c;優化網絡性能。要深入理解子網劃分&#xff0c;首先需要從 IP 地址的基本結構說起。 一、IPv4 地址的基…

吾日三省吾身 | 周反思 8.19

上周一覽總體來說&#xff0c;上個周是一個被項目驅使而險些喪失自主思考能力的危險階段。相比任何有機械化工作經驗的讀者都有類似的體驗&#xff0c;在手上打螺絲的無盡循環中&#xff0c;自己的腦子就會逐漸喪失對自身的感知以及自主思考的能力。而這個負循環一旦開始&#…

08.19總結

連通性 在無向圖中&#xff0c;若任意兩點間均存在路徑相連&#xff0c;則該圖稱為連通圖。 若刪除圖中任意一個頂點后&#xff0c;剩余圖仍保持連通性&#xff0c;則該圖為點雙連通圖。 若刪除圖中任意一條邊后&#xff0c;圖仍保持連通性&#xff0c;則該圖為邊雙連通圖。 在…

車e估牽頭正式啟動乘用車金融價值評估師編制

8月13日&#xff0c;汽車金融行業職業能力評價規范編制啟動工作會議在廣州圓滿落幕。本次會議由中國機械工業聯合會機械工業人才評價中心主辦&#xff0c;廣州穗圣信息科技有限公司&#xff08;車e估&#xff09;承辦。會議匯聚了眾多行業精英&#xff0c;包括中國機械工業聯合…

清空 github 倉庫的歷史提交記錄(創建新分支)

想在 現有倉庫中創建一個新分支 master&#xff0c;刪除原來的 main&#xff0c;然后把 master 重命名為 main&#xff0c;并且清空歷史。可以用下面一條完整的命令序列操作&#xff1a; # 1. 創建一個沒有歷史的新分支 master git checkout --orphan master# 2. 添加當前所有文…

使用B210在Linux下實時處理ETC專用短程通信數據(2)-CPU單核高速數據處理

在上一篇文章中&#xff0c;使用Octave初步驗證了ETC車聯數據的格式。然而&#xff0c;Octave無法實時處理20M的采樣帶寬。我們本節通過C語言&#xff0c;重寫 Octave程序&#xff0c;實現實時處理&#xff0c;涉及下面三個關鍵特點。 文章目錄1. 全靜態內存2. 使用環狀緩存3 無…

Spark 運行流程核心組件(二)任務調度

1、調度策略參數默認值說明spark.scheduler.modeFIFO調度策略&#xff08;FIFO/FAIR&#xff09;spark.locality.wait3s本地性降級等待時間spark.locality.wait.processspark.locality.waitPROCESS_LOCAL 等待時間spark.locality.wait.nodespark.locality.waitNODE_LOCAL 等待時…

Orbbec---setBoolProperty 快捷配置設備行為

在奧比中光&#xff08;Orbbec&#xff09;SDK&#xff08;通常稱為ob庫&#xff09;中&#xff0c;setBoolProperty函數是用于設置設備或傳感器的布爾類型屬性的核心接口。它主要用于開啟/關閉設備的某些功能或模式&#xff0c;是配置設備行為的重要方法。 函數原型與參數解析…

[OWASP]智能體應用安全保障指南

1.關鍵組件定義 KC1 生成式語言模型&#xff08;Generative Language Models&#xff09; KC1.1 大語言模型&#xff08;LLMs&#xff09;&#xff1a;作為代理的“大腦”&#xff0c;基于預訓練基礎模型&#xff08;如 GPT 系列、Claude、Llama、Gemini&#xff09;&#xff…

【Vivado TCL 教程】從零開始掌握 Xilinx Vivado TCL 腳本編程(三)

【Vivado TCL 教程】從零開始掌握 Xilinx Vivado TCL 腳本編程&#xff08;三&#xff09; 系列文章目錄 1、VMware Workstation Pro安裝指南&#xff1a;詳細步驟與配置選項說明 2、VMware 下 Ubuntu 操作系統下載與安裝指南 3、基于 Ubuntu 的 Linux 系統中 Vivado 2020.1 下…

AI與大數據驅動下的食堂采購系統源碼:供應鏈管理平臺的未來發展

在數字化浪潮不斷加速的今天&#xff0c;很多企業和機構都在追求一個目標&#xff1a;如何把“效率”與“成本”做到最佳平衡。對于學校、企事業單位的食堂來說&#xff0c;采購環節就是重中之重。往小了說&#xff0c;它關系到食堂員工的工作體驗&#xff1b;往大了說&#xf…

HarmonyOS 實戰:學會在鴻蒙中使用第三方 JavaScript 庫(附完整 Demo)

摘要 在鴻蒙&#xff08;HarmonyOS NEXT / ArkTS&#xff09;開發中&#xff0c;我們大部分業務邏輯和 UI 都是用 ArkTS 寫的。不過在做一些數據處理、網絡請求、工具函數或者復雜算法時&#xff0c;完全沒必要“重復造輪子”。這時候就可以直接引入 JavaScript 的第三方庫。鴻…

C++實現教務管理系統,文件操作賬戶密碼登錄(附源碼)

教務管理系統項目介紹 項目概述 這是一個基于C開發的教務管理系統&#xff0c;提供了學生、教師和系統管理員三種角色的功能模塊&#xff0c;實現了教務信息的錄入、查詢、修改和刪除等基本操作。系統采用文件存儲方式保存數據&#xff0c;具有簡單易用、功能完備的特點。 項…

《C++進階之STL》【二叉搜索樹】

【二叉搜索樹】目錄前言&#xff1a;------------概念介紹------------1. 什么是二叉搜索樹?2. 二叉搜索樹的性能怎么樣&#xff1f;------------基本操作------------一、查找操作思想步驟簡述二、插入操作目標步驟簡述三、刪除操作目標步驟簡述------------代碼實現--------…

Orange的運維學習日記--47.Ansible進階之異步處理

Orange的運維學習日記–47.Ansible進階之異步處理 文章目錄Orange的運維學習日記--47.Ansible進階之異步處理Playbook 執行順序原理可選執行策略調整并發連接數&#xff1a;forks 參數查看與修改 forks性能調優建議分批執行全局任務&#xff1a;serial 關鍵字serial 用法示例應…

從一個ctf題中學到的多種php disable_functions bypass 姿勢

題目介紹 題目是Lilctf2025 的php-jail-is-my-cry 比賽鏈接&#xff1a;https://lilctf.xinshi.fun/ 題目環境前半部分是 php最近的phar 新 trick 大佬的原理分析 https://fushuling.com/index.php/2025/07/30/%e5%bd%93include%e9%82%82%e9%80%85phar-deadsecctf2025-baby-we…