Linux進程通信之共享內存

文章目錄

  • 共享內存原理
  • 申請共享內存函數(shmget)
    • 參數key
    • 生成key值示例
    • 申請共享內存
  • 掛接到進程地址空間函數(shmat)
  • 去關聯函數(shmdt)
  • 控制共享內存(shmctl)
    • IPC_STAT
    • IPC_RMID
  • ipcs
  • 其余進程獲取該共享內存
  • 進程間通信

進程間通信:IPC,Inter Process Communication

共享內存原理

進程之間通信的本質是:讓不同的進程,看到同一份資源。
無論是匿名管道還是命名管道的通信,我們都是在內核空間里的緩沖區進行實現的進程通信,對于這種方式在用戶輸入時我們在內核空間緩沖區中進行通信,然后再寫入物理內存,那么我們是否可以做到直接讓進程在物理內存中進行進程間的通信呢?
在這里插入圖片描述
每一個進程都有一個虛擬內存(地址),他們通過頁表將虛擬地址映射到物理地址上,因此我們可以讓操作系統幫我們在物理內存申請一段空間,然后通過頁表把這份空間映射到虛擬內存的共享區,這樣我們就做到了讓不同的進程看到了同一份資源。

申請共享內存函數(shmget)

在這里插入圖片描述

shmget:shared memory get,就是獲取共享內存
參數:
key:這個共享內存段名字
size:需要創建的共享內存的大小
shmflg:通過類似位圖實現的多操作整數
IPC_CREAT(單獨):如果你申請的共享內存不存在,存在,就獲取返回
IPC_CREAT | IPC_EXCL:如果你申請的共享內存不存在,就創建,存在,就出錯返回。這是為了確保我們申請的共享內存,一定是一個新的,沒有被別人使用
IPC_EXCL:不單獨使用
這段共享內存的權限,千萬不要忘記或上 | 權限,不然后面就會導致進程掛接不上

//申請共享內存
int shmid = shmget(key, NUM, IPC_CREAT | 0666);//返回值為共享內存標識符

返回值:
共享內存標識符

參數key

key是一個數字,這個值是隨機的,關鍵是這個隨機數在內核中具有唯一性,可以讓他對不同的進程擁有唯一的標識,第一個進程創建出key之后,后面的進程只要有了這個key,就可以拿著這個key去和其它進程進行共享內存了。舉個例子:這個key就相當于是一把鑰匙,而這個共享內存就是一把鎖,只要不同的進程拿著這個鑰匙,就可以打開這個內存了。那么現在的問題是我們怎么去生成這個key值呢,又這么保證它在內核中具有唯一性呢?
系統中什么具有唯一性呢?對,就是路徑,路徑就有唯一性,因此我們可以通過一個函數把路徑作為參數傳給這個函數,函數根據一定的算法幫助我們生成這個key。
庫里面給我們提供了生成key的函數:ftok()
在這里插入圖片描述
參數:
pathname:文件路徑
proj_id:項目id,我們給定的一個int值,目的就是為了和文件路徑一起通過一系列的算法生成一個唯一key值
作用:ftok函數通過給的路徑和int值就可以幫我們生成一個在內核中具有唯一性的值。
返回值:
在這里插入圖片描述
成功返回key,失敗返回-1并設置錯誤碼
key存在哪里呢?系統中一定有很多的共享內存,操作系統要想把他們管理起來,就要先描述再組織,因此key一定被存在共享內存的描述對象中。

生成key值示例

#include <sys/types.h>
#include <sys/ipc.h>
#include <iostream>using namespace std;#define PATHNAME "./common.cc" 
#define PROJ_ID 0x8888int main()
{key_t key = ftok(PATHNAME, PROJ_ID);if(key == -1){perror("ftok error");exit(0);}cout << key << endl;return 0;
}

在這里插入圖片描述
所給的路徑,一般是當前文件的路徑,當然也可以給其他文件的,但為了保證唯一性我們一般在哪個路徑下生成key就使用哪個路徑,但前提是所給的文件路徑一定是存在的,如果不存在就會生成失敗。

申請共享內存

在這里插入圖片描述

//申請共享內存,方式1
int shmid = shmget(key, 1024, IPC_CREAT 0666);
//申請共享內存,方式2
int shmid = shmget(key, 1024, IPC_CREAT|IPC_EXCL 0666);

問題:我們繞了一大圈生成的這個shmid和key都是具有唯一性的,那么我們為什么不直接用key呢?非要再去通過給shmget函數傳key生成呢?
key是用于在操作系統內標定唯一性,而shmid是共享內存標識符,只存在于我們的進程內,用來表示資源的唯一性
在這里插入圖片描述
共享內存的大小一般是4096(4KB)的整數倍,雖然我們這里申請的是1024,其實系統給我們的就是4096,因為操作系統是按4KB為單位對我們進行分配的,需要注意的是我們只能用1024,多給的我們不可以使用,用了就是越界可能會出現錯誤

掛接到進程地址空間函數(shmat)

shmat:shared memory attach
返回值為掛接到虛擬地址中的哪個位置
上面我們只是在物理內存中申請了一份共享內存,但是我們還要去把該共享內存掛接到進程地址空間的共享區中,如何做到呢?通過shmat函數來實現
在這里插入圖片描述
參數:
shmid:就是申請共享內存函數shmget的返回值,共享內存標識符
shmaddr:讓申請的共享內存掛到共享區的哪一個位置(地址),設置成nullptr就是讓系統進行決定
shmflg:進程以什么方式去掛接這個共享內存

shmaddr為NULL,核心自動選擇一個地址
shmaddr不為NULL且shmflg無SHM_RND標記,則以shmaddr為連接地址。
shmaddr不為NULL且shmflg設置了SHM_RND標記,則連接的地址會自動向下調整為SHMLBA的整數倍。公式:shmaddr - (shmaddr % SHMLBA)
shmflg=SHM_RDONLY,表示連接操作用來只讀共享內存
返回值:返回一個void類型的指針,指向在物理內存中申請的共享內存在虛擬地址中共享內存的起始地址
共享內存:指在物理內存中申請的共享內存
共享區:指在虛擬內存中的共享區域

在這里插入圖片描述

去關聯函數(shmdt)

dt:delete touch
給虛擬內存共享區掛接的物理內存,如果進程結束,系統會自動取消關聯,但是我們現在不想等到進程結束,想要在進程還在運行時就去關聯,怎么做呢?
通過函數shmdt()就可以做到去關聯
在這里插入圖片描述
參數:
const void* shmaddr:就是在物理內存申請的共享內存掛接到共享區的起始地址,也就是函數shmat的返回值。
返回值:
成功返回0,失敗返回-1,并設置錯誤碼
注意:將共享內存段與當前進程脫離不等于刪除共享內存段。

//去關聯
shmdt(shmaddr);

控制共享內存(shmctl)

在這里插入圖片描述
參數:
shmid:共享內存標識符,就是我們通過shmget函數得到的返回值。
cmd:控制這個函數去做什么
buf:指向一個保存著共享內存的模式狀態和訪問權限的數據結構
返回值:
成功返回0,失敗返回-1
其中參數cmd有三個取值,如下:
在這里插入圖片描述

IPC_STAT

用于獲取管理該共享內存結構中的數據,這個共享內存的key值,關聯個數等數據

struct shmid_ds shmds;
shmctl(shmid, IPC_STAT, &shmds);
cout << "shm size: " << shmds.shm_segsz << endl;
cout << "shm nattch: " << shmds.shm_nattch << endl;
printf("shm key: 0x%x\n",  shmds.shm_perm.__key);
cout << "shm mode: " << shmds.shm_perm.mode << endl;

IPC_RMID

刪除共享內存選項
shmdt函數只是把進程和該共享內存的關系從也表中刪除從而取消關聯,但是我們在物理內存中申請的共享內存并沒有被釋放,因此我們可以通過shmctl來釋放對應的共享內存

ipcs

如果不通過shmctl來釋放對應的共享內存,那么即使進程結束,該共享內存仍然是被占用的,因此我們可以手動的來釋放該共享內存,操作如下:
在這里插入圖片描述
在這里插入圖片描述
此時只是去關聯了,但是該共享內存仍然被占用沒有被釋放
通過ipcrm -m選項 + 對應的共享內存標識符來釋放該共享內存
在這里插入圖片描述

其余進程獲取該共享內存

只需要創建一次共享內存,其余的進程只需拿著這個共享內存的標識符就可以得到這個共享內存了

#include <sys/types.h>
#include <sys/ipc.h>
#include <iostream>
#include <sys/shm.h>
#include <unistd.h>#include "log.hpp"using namespace std;#define PATHNAME "/home/Linux3" 
#define PROJ_ID 0x6666
#define NUM 1024key_t GetKey()
{//生成參數唯一key值key_t key = ftok(PATHNAME, PROJ_ID);if(key == -1){perror("ftok error");exit(0);}return key;
}int GetShareMemHelper(int flag)
{key_t key = GetKey();int shmid = shmget(key, NUM, flag);//返回值為共享內存標識符return shmid;
}//創建第一個共享內存
int CreatShm()
{return GetShareMemHelper(IPC_CREAT |IPC_EXCL | 0666);
}//獲取已經創建好的共享內存
int GetShm()
{return GetShareMemHelper(IPC_CREAT);
}
#include "common.hpp"int main()
{int shmid = GetShm();//自定義函數,用于獲取已經創建好的共享內存char* shmaddr = (char*)shmat(shmid, nullptr, 0);sleep(5);shmdt(shmaddr);sleep(2);shmctl(shmid, IPC_RMID, nullptr);return 0;
}

進程間通信

此時我們就可以向該共享內存寫入了,而不是先寫入緩沖區,再講緩沖區數據拷貝到內存,其它進程就直接從該共享內存進行讀取就可以了。
但是此時會有一個問題,就是共享內存對于不同進程而言不是同步的,即寫進程還沒寫,讀進程就從共享內存中讀,此時讀到的為空,或者上次的,就一直在那打印,因此我們要讓進程同步,等寫進程寫入了,讀進程才開始讀,這里舉個例子,我們可以通過管道來實現進程同步,因為read函數,如果沒有讀取到就會一直等待

char c = 'c';
while(1)
{fgets(shmaddr, 1024, stdin);write(fd, &c, 1);//向命名管道寫入信息,用來通知讀進程可以從共享內存中讀取了//通過命名管道使得進程a,b同步}
Init init;
int fd = open(FIFO_FILE, O_RDONLY);
char c;
while (1)
{ssize_t n = read(fd, &c, 1);//等待通知信息,如果沒有就退出if(n <= 0){break;}cout << shmaddr;
}

創建一個管道,當向共享內存寫入時,該進程通過write函數向管道中寫入數據,對于讀進程如果一直沒有read到,就會一直等待,當讀到了才會向下執行代碼,這里管道的作用就是為了實現進程同步。

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

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

相關文章

在openSUSE-Leap-15.5-DVD-x86_64中使用deepin-wine-6.0.0.19再使用金山打字通2016

在openSUSE-Leap-15.5-DVD-x86_64中使用deepin-wine-6.0.0.19再使用金山打字通2016 在openSUSE Software官網輸入關鍵字deepin-wine搜索得到fedora-deepin-wine6的作者是xuthus5 https://software.opensuse.org/package/fedora-deepin-wine6 在百度貼吧fedora吧的《fedora下的…

C++11『lambda表達式 ‖ 線程庫 ‖ 包裝器』

?個人主頁&#xff1a; 北 海 &#x1f389;所屬專欄&#xff1a; C修行之路 &#x1f383;操作環境&#xff1a; Visual Studio 2022 版本 17.6.5 文章目錄 &#x1f307;前言&#x1f3d9;?正文1.lambda表達式1.1.仿函數的使用1.2.lambda表達式的語法1.3.lambda表達式的使用…

數據結構-深度優先搜索Java實現

目錄 一、引言二、算法步驟三、原理演示遞歸實現非遞歸實現&#xff08;使用堆棧&#xff09; 四、代碼實戰五、結論 一、引言 深度優先搜索&#xff08;DFS&#xff09;是一種在圖或樹中進行搜索的算法&#xff0c;它沿著樹的深度遍歷樹的節點&#xff0c;盡可能深的搜索樹的分…

使用C++從0到1實現人工智能神經網絡及實戰案例

引言 既然是要用C++來實現,那么我們自然而然的想到設計一個神經網絡類來表示神經網絡,這里我稱之為Net類。由于這個類名太過普遍,很有可能跟其他人寫的程序沖突,所以我的所有程序都包含在namespace liu中,由此不難想到我姓劉。在之前的博客反向傳播算法資源整理中,我列舉…

CTF-PWN-QEMU-前置知識

文章目錄 QEMU 內存管理(QEMU 如何管理某個特定 VM 的內存)MemoryRegion gpa->hpaFlatView&#xff1a;表示MR 樹對應的地址空間FlatRange&#xff1a;存儲不同MR對應的地址信息AddressSpace&#xff1a;不同類型的 MemoryRegion樹RAMBlock總體簡化圖 QEMU 設備模擬 &#x…

【Java進階開發實戰】用Java中的Base64數據加密與解密處理

簡介 ? Base64編碼,是我們程序開發中經常使用到的編碼方法。它是一種基于用64個可打印字符來表示二進制數據的表示方法。它通常用作存儲、傳輸一些二進制數據編碼方法, 也是MIME(多用途互聯網郵件擴展,主要用作電子郵件標準)中一種可打印字符表示二進制數據的常見編碼方法…

Proteus下仿真AT89C51報“串行口通信失敗,請檢查電平適配是否正確。”解決辦法

在Proteus下進行AT89C51串行口仿真時&#xff0c;如果遇到“串行口通信失敗&#xff0c;請檢查電平適配是否正確”的錯誤提示&#xff0c;以下是一些解決辦法&#xff1a; 1. 了解AT89C51和外部設備的電平要求&#xff1a; 首先&#xff0c;了解AT89C51和外部設備之間的電平…

【華為OD機試python】分班【2023 B卷|100分】

【華為OD機試】-真題 !!點這里!! 【華為OD機試】真題考點分類 !!點這里 !! 題目描述 幼兒園兩個班的小朋友在排隊時混在了一起,每位小朋友都知道自己是否與前面一位小朋友是否同班,請你幫忙把同班的小朋友找出來。 小朋友的編號為整數,與前一位小朋友同班用Y表示,不同班…

C語言——文件操作

歸納編程學習的感悟&#xff0c; 記錄奮斗路上的點滴&#xff0c; 希望能幫到一樣刻苦的你&#xff01; 如有不足歡迎指正&#xff01; 共同學習交流&#xff01; &#x1f30e;歡迎各位→點贊 &#x1f44d; 收藏? 留言?&#x1f4dd; 我輩皆凡人&#xff0c;用一生鋪就的…

C++的new / delete 與 C語言的malloc/realloc/calloc / free 的講解

在C語言中我們通常會使用malloc/realloc/calloc來動態開辟的空間&#xff0c;malloc是只會開辟你提供的空間大小&#xff0c;并不會初始化內容&#xff1b;calloc不但會開辟空間&#xff0c;還會初始化&#xff1b;realloc是專門來擴容的&#xff0c;當你第一次開辟的空間不夠用…

目標檢測YOLO實戰應用案例100講-基于YOLO的小目標檢測改進算法(續)

目錄 3.3基于混合注意力的多尺度特征融合改進方法 3.3.1整體網絡架構 3.3.2特征金字塔的構建

Vue 2.0源碼分析-實例掛載的實現

Vue 中我們是通過 $mount 實例方法去掛載 vm 的&#xff0c;$mount 方法在多個文件中都有定義&#xff0c;如 src/platform/web/entry-runtime-with-compiler.js、src/platform/web/runtime/index.js、src/platform/weex/runtime/index.js。因為 $mount 這個方法的實現是和平臺…

Python 使用tkinter復刻Windows記事本UI和菜單功能(三)

上一篇&#xff1a;Python 使用tkinter復刻Windows記事本UI和菜單功能&#xff08;二&#xff09;-CSDN博客 下一篇&#xff1a;敬請耐心等待&#xff0c;如發現BUG以及建議&#xff0c;請在評論區發表&#xff0c;謝謝&#xff01; 本文章完成了記事本的新建、保存、另存、打…

【技巧】前端開發技巧 增加前端的請求緩存 提高開發效率

定義變量 /*** 開發緩存 開關* 說明* 方便開發使用 提升開發效率* true 打開緩存* false 關閉緩存 這里上線的時候必須改為* type {boolean}*/ const cacheFlag true/*** 排除某個url 方便開發時的數據實時生效* 這里根據開發到哪個功能 實時變更&#xff0c; 比如開…

京東數據分析(京東大數據):2023年10月京東手機行業品牌銷售排行榜

鯨參謀監測的京東平臺10月份手機市場銷售數據已出爐&#xff01; 根據鯨參謀平臺的數據顯示&#xff0c;今年10月份&#xff0c;京東平臺手機行業的銷量約340萬&#xff0c;環比增長約11%&#xff0c;同比則下滑約2%&#xff1b;銷售額為108億&#xff0c;環比增長約17%&#x…

請你說一下Vue中v-if和v-for的優先級誰更高

v-if 與 v-for簡介 v-ifv-forv-if & v-for使用 v-if 與 v-for優先級比較 vue2 中&#xff0c;v-for的優先級高于v-if 例子進行分析 vue3 v-if 具有比 v-for 更高的優先級 例子進行分析 總結 在vue2中&#xff0c;v-for的優先級高于v-if在vue3中&#xff0c;v-if的優先級高…

RubyMine 2023:提升Rails/Ruby開發效率的強大利器

在Rails/Ruby開發領域&#xff0c;JetBrains RubyMine一直以其強大的功能和優秀的性能而備受開發者的青睞。現如今&#xff0c;我們迎來了全新的RubyMine 2023版本&#xff0c;它將為開發者們帶來更高效的開發體驗和無可比擬的工具支持。 首先&#xff0c;RubyMine 2023提供了…

Java-使用poi-tl根據word模板動態生成word

作者wangsz&#xff0c;想寫一些關于word的工具&#xff0c;所以就寫了這篇文章 1.首先&#xff0c;先導入所需要的依賴&#xff08;poi相關依賴即可&#xff09; <!-- POI --><dependency><groupId>org.apache.poi</groupId><artifactId>poi&l…

【libGDX】使用Mesh繪制立方體

1 前言 本文主要介紹使用 Mesh 繪制立方體&#xff0c;讀者如果對 Mesh 不太熟悉&#xff0c;請回顧以下內容&#xff1a; 使用Mesh繪制三角形使用Mesh繪制矩形使用Mesh繪制圓形 在繪制立方體的過程中&#xff0c;主要用到了 MVP &#xff08;Model View Projection&#xff0…

目標檢測YOLO系列從入門到精通技術詳解100篇-【目標檢測】計算機視覺(最終篇)

目錄 知識儲備 KITTI數據集 1.KITTI數據集概述 2.數據采集平臺 3.Dataset詳述 算法原理