進程間的通信 -- 共享內存

一 共享內存的概念

1. 1 共享內存的原理

? ???之前我們學過管道通信,分為匿名管道和命名管道,匿名管道通過父子進程的屬性繼承原理來完成父子進程看到同一份資源的目的,而命名管道則是通過路徑與文件名來唯一標識管道文件,來讓不同的進程之間進行通信。

? ?而共享內存也是同理, 就是允許兩個或多個不相關的進程訪問同一段物理內存空間。

1.2 基本概念?

? ?不同進程之間共享的內存通常為同一段物理內存。進程可以 將同一段物理內存連接到他們自己的地址空間中 ,所有的進程都可以訪問共享內存中的地址。如果某個進程向共享內存寫入數據,所做的改動將立即影響到可以訪問同一段共享內存的任何其他進程。

?概念圖如下:

?

?二 共享內存的使用

2.1 使用流程

一般而言,共享內存的使用可以分為以下幾步:

  1. 創建共享內存段
  2. 關聯共享內存(建立通信)
  3. 傳輸數據
  4. 關閉共享內存區域

2.1.1 創建共享內存段 shmget

創建共享內存接口:shmget

創建一個新共享內存段或者取得一個既有共享內存段的標識符(即由其他進程創建的共享內存段)。這個調用將返回后續調用中需要用到的??共享內存標識符。

接口如下:
?

int shmget(key_t key, size_t size, int shmflg);- 功能: 創建一個新的共享內存段,或者獲取一個既有的共享內存段的標識新創建的內存段中的數據都會被初始化為0- 參數:- key: key_t 類型是一個整型,通過這個找到或者創建一個共享內存一般使用16進制標識,非0值- size: 共享內存的大小,以幾頁的大小創建(大于1的數值,一般為4的整數)- shmflg: 屬性- 訪問權限- 附加屬性: 創建/判斷共享內存是不是存在- 創建: IPC_CREAT- 判斷共享內存是否存在: IPC_EXCL,需要和 IPC_CREAT一起使用IPC_CREAT | IPC_EXCL | 0664(權限)- IPC_CREAT	如果內核中對應key值的共享內存不存在,則新建一個共享內存并返回該共享內存的句柄;如果已存在,則直接返回該共享內存的句柄– IPC_CREAT IPC_EXCL	如果不存在對應key值的共享內存則新建一個共享內存并返回該共享內存的句柄;如果已存在,則出錯返回- 返回值:失敗: -1 并設置錯誤號成功: >0 返回共享內存的引用的ID (int類型),后面操作共享內存都是通過這個值

2.1.2 key值的獲取 ftok?

?key值的作用:

問:當我們調用系統接口申請了一塊共享內存,我們要保證對應的進程能夠訪問到同一塊共享內存,那么如何做到這一點呢?

答案:每一個共享內存被申請的時候都會有一個key值,這個key值用于標識系統中共享內存的唯一性。

ftok函數的作用:通過數學運算將一個已存在的路徑名pathname和一個整數標識符proj_id轉換成一個key值,稱為IPC鍵值,在使用shmget函數獲取共享內存時,這個key值會被填充進維護共享內存的數據結構當中。需要注意的是,pathname所指定的文件必須存在且可存取。

ftok運算出來的key值可能會產生沖突,不過概率很小。如果產生沖突,就對ftok的參數進行修改即可。

當我們想讓兩個進程使用共享內存的時候,我們就利用key值尋找到這一塊共享內存

key_t ftok(const char* pathname, int proj_id);- 功能: 根據指定的路徑名, 和int值, 生成一個共享內存的key- 參數: - pathname: 指定一個存在的路徑/home/ubuntu/Linux/a.txt/root- proj_id: int類型的值,但是這個系統調用只會使用其中的1個字節(8bit)范圍: 0-255   一般指定一個字符 'a'

2.1.3??共享內存的關聯 shmat

我們不是單單把共享內存創建出來就行了,我們還要將需要獲取這塊共享內存的進程與對應的共享內存關聯起來,通信結束后,解除關聯,最后進行共享內存的釋放。

void* shmat(int shmid, const void* shmaddr, int shmflg);- 功能: 和當前的進程進行關聯- 參數: - shmid: 共享內存的標識(ID),由shmget返回值獲取- shmaddr: 申請的共享內存的起始地址(虛擬內存的地址,一般不會手動指定,由內核指定,設置為NULL)- shmflg: 對共享內存的操作- 讀權限: SHM_RDONLY,必須要有讀權限- 讀寫: 0- 返回值:成功: 返回共享內存的首(起始)地址失敗: (void*) -1 設置錯誤號

?2.1.4 解除共享內存的關聯 shmdt

? ?接觸進程和共享內存的關聯。

int shmdt(const void* shmaddr);- 功能: 解除當前進程和共享內存的關聯- 參數:- shmaddr: 共享內存的首地址- 返回值:成功:  0失敗: -1

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

?2.1.5 刪除共享內存函數? shmctl

int shmctl(int shmid, int cmd, struct shmid_ds* buf);- 功能: 對共享內存進行操作。 刪除共享內存,共享內存要刪除才會消失, 創建共享內存的進程被銷毀了對共享內存是沒有任何影響的。- 參數:- shmid: 共享內存的ID- cmd: 要做的操作- IPC_STAT: 獲取共享內存的當前狀態- IPC_SET: 設置共享內存的狀態- IPC_RMID: 標記共享內存被銷毀- buf: 需要設置或者獲取的共享內存的屬性信息- IPC_STAT: buf存儲數據- IPC_SET: buf中需要初始化數據,設置到內核中- IPC_RMID: 沒有用,NULL

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

注意:一般有幾個進程關聯?這一塊共享內存,就需要解除關聯多少次,但是,刪除共享內存只需要刪除一次就好了(其實可以刪除多次,看補充)。

2.1.6 示例?

?接下來我們進行一段示例,操作內容如下:

要求:使用代碼創建一個共享內存, 支持 A.B 兩個進程進行通信。

?進程A 向共享內存當中寫 “i am process A”。

?進程B 從共享內存當中讀出內容,并且打印到標準輸出。

? 因為兩個進程中有大量重復代碼,因此,我們封裝一個頭文件,以此來復用。

shared.h內容如下:
?

#include<iostream>
#include<cassert>
#include<cstring>
#include<cerrno>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<cstdlib>
#include<unistd.h>
using namespace std;#define PATHNAME "./shared2"//ftok的第一個參數,是一個合法路徑
#define PROJ_IO 'a'//ftok的第二個參數
#define MAXSIZE 4096//創建的共享內存的大小
//1.獲取key值代碼
key_t GetKey()
{key_t k = ftok(PATHNAME, PROJ_IO);//如果返回值小于0 則創建失敗if(k < 0){std::cerr << errno << ":" << strerror(errno) << std::endl;exit(1);}return k;
}//2.創建共享內存段代碼
int GetShmget(key_t k,int flags){int shmid = shmget(k, MAXSIZE, flags);if(shmid < 0){std::cerr << errno << ":" << strerror(errno) << std::endl;exit(2);}return shmid;
}//獲取共享內存
int Getshm(key_t k){return GetShmget(k, IPC_CREAT);//沒有就創建,有就獲取
}//創建共享內存
int Createshm(key_t k){return GetShmget(k, IPC_CREAT | IPC_EXCL | 0600);//沒有創建,有就報錯,這里創建內存需要給對應的權限
}//3. 關聯共享內存,返回共享內存的空間起始位置
void* attachshm(int shmid){void* p = shmat(shmid, nullptr, 0);//因為linux系統是64位,一個地址是8個字節,所以要變成8個字節大小的數據類型做對比if((long long)p == -1L){std::cerr << errno << ":" << strerror(errno) << std::endl;exit(4);}return p;
}//4.解除共享內存的關聯
void detachshm(void* p){//如果失敗報錯if(shmdt(p) == -1){std::cerr << errno << ":" << strerror(errno) << std::endl;}
}//5 shmctl刪除共享內存
void delshm(int shmid){if(shmctl(shmid, IPC_RMID, nullptr) == -1){std::cerr << errno << ":" << strerror(shmid) << std::endl;exit(3);}
}

A.cpp:

#include"shared.hpp"int main(){//先生成一個key值key_t k=GetKey();printf("A 獲取key值成功: %d\n",k);//創建共享內存,有就獲取標識碼shmidint shmid=Getshm(k);printf("A 獲取共享內存塊成功\n");//關聯共享內存塊char* p = (char*)attachshm(shmid);if (p == (void*)-1) {perror("shmat failed");delshm(shmid);printf("A 刪除共享內存成功\n");exit(EXIT_FAILURE);}printf("A 關聯共享內存塊成功\n");//開始寫入:const char* str = "i am process A";snprintf(p, MAXSIZE, "%s", str);//去關聯detachshm(p);printf("A 去關聯成功\n");return 0;
}

B.cpp

 #include"shared.hpp"int main(){//先生成一個key值key_t k=GetKey();printf("B 獲取key值成功: %d\n",k);//創建共享內存,有就獲取標識碼shmidint shmid=Getshm(k);printf("B 獲取共享內存塊成功\n");//關聯共享內存塊char* p = (char*)attachshm(shmid);if (p == (void*)-1) {perror("shmat failed");delshm(shmid);printf("B 刪除共享內存成功\n");exit(EXIT_FAILURE);}printf("B 關聯共享內存塊成功\n");//開始讀出:sleep(5);printf("attach sucess, address p: %s\n",p);//去關聯detachshm(p);printf("B 去關聯成功\n");//刪除共享內存delshm(shmid);printf("B 刪除共享內存成功\n");return 0;
}

?代碼結果:

2.2 共享內存的命令行操作

2.2.1?共享內存的查看 –?ipcs指令

????報告進程間通信設施的狀態,包括共享內存、消息隊列以及信號量等等

ipcs用法:

ipcs -a //打印當前系統中所有進程間通信方式的信息

ipcs -m //打印出使用共享內存進行進程間通信的信息(**常用**)

ipcs -q //打印出使用消息隊列進行進程間通信的信息

ipcd -s //打印出使用信號進行進程間通信的信息

2.2.2? 共享內存的刪除-?ipcrm 指令

ipcrm用法(rm:remove)

ipcrm -M shmkey //移除用shmkey創建的共享內存段

ipcrm -m shmid //移除用shmid標識的共享內存段

ipcrm -Q msgkey //移除用msqkey創建的消息隊列

ipcrm -q msqid //移除用msqid標識的消息隊列

ipcrm -S semkey //移除用semkey創建的信號

ipcrm -s semid //移除用semid標識的信號

三 補充說明

 問題1:操作系統如何知道共享內存被讀誦進程關聯- 共享內存委會一個結構體  struct shmid_ds 這個結構體中有一個成員 shm_nattach- shm_nattach 記錄了關聯的進程個數
 問題2:可不可以對共享內存進行多次刪除 shmctl- 可以的- 因為 shmctl 僅是標記刪除共享內存,不是直接刪除- 什么時候真正刪除呢?當和共享內存關聯的進程數為0的時候,就真正被刪除- 當共享內存的key為0的時候, 共享內存被標記刪除了- 因此,需要合理衡量共享內存的刪除,不要在其它進程還在使用時刪除

?

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

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

相關文章

學習Android的第二十一天

目錄 Android ProgressDialog (進度條對話框) 例子 Android DatePickerDialog 日期選擇對話框 例子 Android TimePickerDialog 時間選擇對話框 Android PopupWindow 懸浮框 構造函數 方法 例子 官方文檔 Android OptionMenu 選項菜單 例子 官方文檔 Android Progr…

Java實戰:Spring Boot中各類參數校驗機制

引言 在開發Web應用程序時&#xff0c;對客戶端傳入的參數進行有效校驗是保證系統安全性和穩定性的重要環節。Spring Boot作為一個現代化的Java開發框架&#xff0c;提供了多種參數校驗的方法和工具&#xff0c;以滿足不同場景下的需求。本文將深入探討Spring Boot中實現各種參…

typescript 的常用方式

文章目錄 前言一、綁定props 默認值的方式&#xff1a;withDefaults1.vue2 的props設置默認值2.vue3 的props設置默認值(1) 不設置默認值的寫法(2) 設置默認值的寫法&#xff08;分離模式&#xff09;(3) 設置默認值的寫法&#xff08;組合模式&#xff09; 二、定義一個二維數…

Matlab在同一張圖中如何加入多個圖例

根據代碼最終畫出的圖片如下&#xff1a; 其實原理很簡單&#xff0c;就是在一張figure中畫多個坐標軸&#xff0c;每個坐標軸都有對應的圖例&#xff0c;之后再將多余坐標軸隱藏&#xff0c;只保留一個即可。 代碼如下&#xff1a; clear all; close all;dd_linewidth 1;a …

maven archetype 項目原型

拓展閱讀 maven 包管理平臺-01-maven 入門介紹 Maven、Gradle、Ant、Ivy、Bazel 和 SBT 的詳細對比表格 maven 包管理平臺-02-windows 安裝配置 mac 安裝配置 maven 包管理平臺-03-maven project maven 項目的創建入門 maven 包管理平臺-04-maven archetype 項目原型 ma…

Spring學習筆記(六)利用Spring的jdbc實現學生管理系統的用戶登錄功能

一、案例分析 本案例要求學生在控制臺輸入用戶名密碼&#xff0c;如果用戶賬號密碼正確則顯示用戶所屬班級&#xff0c;如果登錄失敗則顯示登錄失敗。 &#xff08;1&#xff09;為了存儲學生信息&#xff0c;需要創建一個數據庫。 &#xff08;2&#xff09;為了程序連接數…

洛谷P1927防護傘

題目描述 據說 20122012 的災難和太陽黑子的爆發有關。于是地球防衛小隊決定制造一個特殊防護傘&#xff0c;擋住太陽黑子爆發的區域&#xff0c;減少其對地球的影響。由于太陽相對于地球來說實在是太大了&#xff0c;我們可以把太陽表面看作一個平面&#xff0c;中心定為(0,0…

C 基本語法

我們已經看過 C 程序的基本結構&#xff0c;這將有助于我們理解 C 語言的其他基本的構建塊。 C 的令牌&#xff08;Token&#xff09; C 程序由各種令牌組成&#xff0c;令牌可以是關鍵字、標識符、常量、字符串值&#xff0c;或者是一個符號。例如&#xff0c;下面的 C 語句…

30天自制操作系統(第23天)

23.1 編寫malloc 參考第22天的內容&#xff0c;在繪制窗口前先分配了150*50個字節大小的內存&#xff0c;所以導致該文件經編譯后有7.6k左右&#xff0c;能否在其中使用指針呢&#xff1f;當需要開辟空間時&#xff0c;移動指針即可。在之前的章節中也有函數memman_alloc函數可…

php源碼 單色bmp圖片取模工具 按任意方式取模 生成字節數組 自由編輯點陣

http://2.wjsou.com/BMP/index.html 想試試chatGPT4生成&#xff0c;還是要手工改 php 寫一個網頁界面上可以選擇一張bmp圖片&#xff0c;界面上就顯示這張bmp圖片&#xff0c; 點生成取模按鈕&#xff0c;在圖片下方會顯示這張bmp圖片的取模數據。 取模規則是按界面設置的&a…

Linux 的交換空間(swap)是什么?有什么用?

目錄 swap是什么&#xff1f;swap有什么用&#xff1f;swap使用典型場景如何查看你的系統是否用到交換空間呢&#xff1f;查看系統中swap in/out的情況 swap是什么&#xff1f; swap就是磁盤上的一塊區域。它和Windows系統中的交換文件作用類似&#xff0c;但是它是一段連續的…

03、MongoDB -- MongoDB 權限的設計

目錄 MongoDB 權限的設計演示前準備&#xff1a;啟動 mongodb 服務器 和 客戶端 &#xff1a;1、啟動單機模式的 mongodb 服務器2、啟動 mongodb 的客戶端 MongoDB 權限的設計1、MongoDB 的每個數據庫都可以保存用戶&#xff0c;不止admin數據庫可以保存用戶。2、保存用戶的數據…

Linux 學習筆記(8)

八、 啟動引導 1 、 Linux 的啟動流程 1) BIOS 自檢 2) 啟動 GRUB/LILO 3) 運行 Linux kernel 并檢測硬件 4) 掛載根文件系統 5) 運行 Linux 系統的第一個進程 init( 其 PID 永遠為 1 &#xff0c;是所有其它進程的父進程 ) 6) init 讀取系統引導配置文件…

GD25Q32驅動

GD25Q32是一款基于SPI的Flash芯片&#xff0c;容量為32/84M bytes。它的引腳如下&#xff1a; 該芯片支持多種SPI操作方式&#xff0c;包括&#xff1a;Standard SPI(標準SPI)、Dual SPI(雙線 SPI)和Quad SPI(四線 SPI) 。有關SPI的介紹可以參考&#xff1a; SPI通信原理-CSDN…

flutter 文字一行顯示,超出換行

因為app有多語言&#xff0c;中文和其他語言長度不一致&#xff0c;可能導致英文會很長。 中文樣式 英文樣式 代碼 Row(mainAxisAlignment: MainAxisAlignment.end,crossAxisAlignment: CrossAxisAlignment.end,children: [Visibility(visible: controller.info.fee ! null,ch…

探尋2024年國內熱門低代碼平臺排行!| 功能特點一覽

低代碼開發是一項革命性的技術&#xff0c;主要目的是盡量避免程序研發的復雜性&#xff0c;讓外行開發者也能加入到應用程序的搭建中。低代碼平臺的核心概念和構成部分通常包括用戶界面和拖拽設計、預構件和模塊、自動化工作內容與數據庫集成和擴展應用&#xff0c;應用低代碼…

web前端css基本內容

web前端css 當我們用html的語法給內容規劃布局樣式時&#xff0c;可能會出現許多個相似的內容需要運用同一種樣式&#xff0c;復制粘貼太麻煩而且如果后期要改動的話比如把許多個地方從紅色改成藍色&#xff0c;就需要一個一個改了&#xff0c;這時候就需要引入css來操作了 把…

java-使用jacob刪除指定文件夾的郵件

總結見文章最后,具體代碼如下: ActiveXComponent outlook = new ActiveXComponent("Outlook.Application");Dispatch myNamespace = Dispatch.call(outlook, "GetNamespace", "MAPI").toDispatch();//指定搜索特定的文件Dispatch allFolders =…

我耀學IT—day05-Bootstrap下拉菜單與導航

一、Bootstrap5 下拉菜單 下拉菜單是可切換的&#xff0c;是以列表格式顯示鏈接的上下文菜單。 例&#xff1a; <div class"dropdown"><button type"button" class"btn btn-primary dropdown-toggle" data-bs-toggle"dropdown&…

U盤彈出提示“該設備正在使用中”:原因與解決方案

在日常使用U盤時&#xff0c;我們可能會遇到一個問題&#xff1a;當嘗試安全彈出U盤時&#xff0c;系統提示“該設備正在使用中”。這種情況可能會讓用戶感到困惑&#xff0c;擔心數據是否安全或是否會導致U盤損壞。本文旨在探討這一現象背后的原因&#xff0c;并提供相應的解決…