Linux : System V 共享內存

目錄

一 前言

二?共享內存概念

?三 共享內存創建?

四 查看共享內存?

五 共享內存的刪除

六 共享內存的關聯?

七 共享內存去關聯?

八 共享內存的使用(通信)

?九 共享內存的特點


一 前言

共享內存區是最快的IPC形式(進程間通信:IPC,InterProcess Communication)?一旦這樣的內存映射到共享它的進程的地址空間,這些進程間數據傳遞不再涉及到內核,換句話說是進程不再通過執行進入內核的系統調用write()和read()來傳遞彼此的數據。


二?共享內存概念

?在上一篇進程間的管道通信中我們提到過,在進程間進行通信的時候,由于程序地址空間的存在,進程間的獨立性使得他們之間的通信很麻煩,如果想要通信則需要兩個進程看到同一份資源,上篇通過系統調用創建管道文件,使得進程之間看到共享資源(內存級文件),而本節進程間通信時進程之間看到的同一份資源是??共享內存。

🚀什么是共享內存呢??

實際上,我們在學習程序地址空間的時候,如上圖所示,我們已經看到了有一個區域的名字是共享區,在之前我們學習動靜態庫的時候,就說過動態庫是在進程運行的時候加載到程序地址空間中的共享區的。當程序需要的時候,就會來到這部分讀取數據。這一塊內存就可以看作是一塊只讀共享區,共享內存進程通信實際上就是這個原理。

? ? ? ? ? ? ?共享內存進程通信就是在物理內存中開辟一塊可以讓所有進程都看到的內存空間,然后多個進程只需要向這塊空間中讀取或者寫入數據,這樣就達到了多個進程間一起通信的目的

也就是說,共享內存進程間的通信就是在物理內存中開辟一塊空間當作共享內存,然后通信的進程們通過各自的頁表將這塊物理內存(共享內存)映射到各自的程序地址空間中?


?三 共享內存創建?

shmget()? ? (share?memory? get)

這個接口的參數一共有三個?

  1. key_t key :??這是一個鍵值,key_t是一個整型,此參數其實是傳入的是一個整數。通常這個鍵是通過??ftok()?函數從一個文件路徑和一個項目ID生成的.?這個key值其實就是共享內存段在操作系統層面的唯一標識符。共享內存是Linux系統的一種進程通信的手段, 而操作系統中共享內存段一定是有許多的, 為了管理這些共享內存段, 操作系統一定會描述共享內存段的各種屬性。類似其他管理方式,操作系統也會為共享內存維護一個結構體,在這個結構體內會維護一個key值,表示此共享內存在系統層面的唯一標識符,其一般由用戶傳入,為了區別每一塊的共享內存,key的獲取也是有一定的方法。

    ftok()函數的作用是將 一個文件 和 項目id 轉換為一個System V IPC key值。用戶就是使用這個函數來生成key值。

    他有兩個參數,第一個參數顯而易見是文件的路徑,第二個參數則是隨意的8bite位的數值。ftok()函數執行成功則會返回一個key值,這個key值是該函數通過傳入文件的inode值和傳入的proi_id值通過一定的算法計算出來的。由于每一個文件的inode值是唯一的,所以我們不用擔心key值得重復。

  2. size_t size:??該參數傳入的是想要開辟的共享內存的大小,單位是 byte字節。值得注意的是系統是按照4KB大小為單位開辟空間的,因為我們在磁盤一篇中學到系統I/O的單位大小就是4KB,也就是說無論這個參數傳的是1、1024還是4096時,系統都會開辟4KB,但是雖然系統是按照4KB為單位開辟的空間,但是實際上用戶能使用的空間的大小還是傳入的size字節大小。

  3. int shmflg: 這里傳入的是一組標識位,可以控制shemget的行為,它包括權限標志(類似0666)和命令標志,就像我們使用文件接口open時的O_WRONLY、O_RDONLY一樣。共享內存接口標識符的兩個最重要的宏是:IPC_CREAT、IPC_EXCL

    IPC_CREAT:傳入該宏,表示創建一個新的共享內存段,若共享內存段已經存在,則獲取此內存段;若不存在就創建一個新的內存段。

    IPC_EXCL:該宏需要和IPC_CREAT一起使用。表示如果創建的內存段不存在,則正常創建,若存在則返回錯誤。使用該宏保證的是此次使用shmget()接口創建成功時,創建出來的共享內存是全新的。

  4. shmget()函數的返回值,如果創建共享內存成功或者找到共享內存則返回共享內存的id,該id的作用是可以讓通信的進程找到同一份塊的資源。此id 是給上層用戶使用,是為了標識共享內存。而key也是為了標識共享內存,但是是從系統層面來說。

🚍:接下來我們來學習和認識共享內存的創建

///comm.hpp/
#ifndef _COMM_HPP_
#define COMM_HPP_#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <unistd.h>#define PATHNAME "."
#define PROJ_ID 0x66           //目標標識符 int proj_id 就是一個整型
#define MAX_SIZE 4096
key_t getKey()
{key_t k = ftok(PATHNAME,PROJ_ID);//可以獲取同一個keyif(k < 0){//cin ,cout,cerr -------->stdin stdout stderr--->0 1 2std::cerr<<errno<<":"<<strerror(errno)<<std::endl;//strerror(errno) 打印錯誤碼對應的錯誤信息exit(1);}return k;
}
/shm_client.cpp///
#include "comm.hpp"
int main()
{key_t k=getKey();printf("key: 0x%x\n",k); return 0;
}
///shm_server.cpp
#include "comm.hpp"int main()
{key_t k=getKey();printf("key: 0x%x\n",k);return 0;
}

?運行結果:

?🚋:key的值是什么并不重要,重要的是能進行唯一性標識。

有了key之后,我們就可以用唯一的key進行共享內存的創建

//comm.hpp
//將一些函數進行封裝到comm.hpp,然后client和server進行調用
int getShmHelper(key_t k,int flags)
{int shmid=shmget(k,MAX_SIZE,flags);//創建共享內存函數shmgetif(shmid < 0){std::cerr<<errno<<":"<<strerror(errno)<<std::endl;exit(2);}return shmid;
}//獲取共享內存
int getShm(key_t k)
{return getShmHelper(k,IPC_CREAT); //如果存在就獲取,所以在客戶端client,我們可以通過這個函數,來獲取共享內存
}
////創建共享內存
int createShm(key_t k)//創建共享內存的工作,有服務端server來做
{return getShmHelper(k,IPC_CREAT | IPC_EXCL | 0600);//如果存在就創建失敗,保證了我們創建的一定是新的共享內存,0600 代表創建的共享內存可讀可寫
}
//server.cpp
#include "comm.hpp"int main()
{key_t k=getKey();printf("key: 0x%x\n",k);int shmid=createShm(k);//服務端進行創建共享內存printf("shmid: %d\n",shmid);return 0;
}
///client.cpp
#include "comm.hpp"
int main()
{key_t k=getKey();printf("key: 0x%x\n",k);int shmid=getShm(k);//客戶端進行獲取共享內存printf("shmid: %d\n",shmid);return 0;
}

?運行結果:

?🍉這里的 key shmid 有什么區別呢?

key:是系統層面的,系統通過key來創建共享內存。

shmid:是上層用戶層面,用戶通過shmid來找到共享內存。


四 查看共享內存?

可是當我們再次運行服務端的時候,會發現出現如下問題:文件已存在

?這是什么原因呢?事實上,共享內存并不會隨著進程的退出而退出,在創建共享內存的進程退出之后,共享內存是依舊存在于操作系統中的。而我們的服務端用key創建共享內存的時候,必須要求創建一個新的,如果當前的key對應的共享內存已經存在,則報錯。

我們可以通過命令查看共享內存資源: ipcs -m??

這表明共享內存的生命周期是隨著OS的,并不會因為進程的退出,而把共享內存刪除。


五 共享內存的刪除

我們可以使用 ipcrm -m (InterProcess Communication Remove Memory) 命令來刪除。

那我們是通過 key 刪除共享內存還是 shmid呢? 前面我們也說了key是內核層面,操作系統使用key,而刪除共享內存,是指令操作,屬于用戶層面,所以我們通過shmid刪除共享內存。

?我們還可以通過調用系統函數? shmctl 來對共享內存進行刪除

我們在comm.hpp中對 shmctl進行封裝成刪除共享內存的函數。

/comm.hpp
void delShm(int shmid)
{if(shmctl(shmid,IPC_RMID,nullptr)==-1)//返回-1代表調用失敗{std::cerr<<"shmctl"<<errno<<":"<<strerror(errno)<<std::endl;}
}
/sever.cpp
#include "comm.hpp"int main()
{key_t k=getKey();printf("key: 0x%x\n",k);int shmid=createShm(k);//服務端進行創建共享內存printf("shmid: %d\n",shmid);sleep(5);delShm(shmid);//調用刪除共享內存函數}

?再次測試:


六 共享內存的關聯?

🌏:前面我們講述了服務端對共享內存的創建以及刪除,但是要想使得兩個進程進行通信,我們還需要進行共享內存對兩個進程關聯起來。

?系統調用函數 shmat (attach)

?我們在comm.hpp中對 shmat進行封裝成關聯共享內存的函數。

/comm.hpp//
//關聯共享內存
void* attachShm(int shmid)
{void* mem =shmat(shmid,nullptr,0);if((long long)mem==-1){std::cerr<<errno<<":"<<strerror(errno)<<std::endl;exit(3);}return mem;
}
///server.cpp
#include "comm.hpp"int main()
{key_t k=getKey();printf("key: 0x%x\n",k);int shmid=createShm(k);//服務端進行創建共享內存printf("shmid: %d\n",shmid);sleep(5);//關聯共享內存char* start=(char*)attachShm(shmid);//返回值是共享內存地址printf("attach success,address start:%p\n",start);//刪除sleep(5);delShm(shmid);return 0;
}

測試結果


七 共享內存去關聯?

既然共享內存可以關聯,自然也可以去關聯,去關聯并不是刪除共享內存,而是去除進程與共享內存的聯系。

系統調用函數 shmdt()? ? (detach)

我們在comm.hpp中對 shmdt進行封裝成去關聯共享內存的函數。

//comm.cpp////去關聯共享內存
void detachShm(void* start)
{if(shmdt(start)==-1){std::cerr<<errno<<":"<<strerror(errno)<<std::endl;}
}

八 共享內存的使用(通信)

🌿,在前面我們做了以下工作

共享內存的創建------------->關聯共享內存---------->(這里我們將進行共享內存的通信)---------------->關聯共享內存------------------------------------>刪除共享內存?

//client.cpp
// 4.使用即通信const char* message="hello server, 我是另外一個進程正在和你通信";pid_t id=getpid();int count =0;while(true){sleep(1);//向共享內存輸入消息snprintf(start,MAX_SIZE,"%s[pid:%d][消息編號:%d]",message,id,count++);//pid count message}
/server.cpp///
//4.使用while(true){printf("client say: %s\n",start);//打印由客戶端發來的消息,直接打印共享內存地址即可sleep(1);}

測試結果:

?

?完整測試如下

/comm.hpp
#ifndef _COMM_HPP_
#define COMM_HPP_#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <unistd.h>#define PATHNAME "."
#define PROJ_ID 0x66           //目標標識符 int proj_id 就是一個整型
#define MAX_SIZE 4096
key_t getKey()
{key_t k = ftok(PATHNAME,PROJ_ID);//可以獲取同一個keyif(k < 0){//cin ,cout,cerr -------->stdin stdout stderr--->0 1 2std::cerr<<errno<<":"<<strerror(errno)<<std::endl;//strerror(errno) 打印錯誤碼對應的錯誤信息exit(1);}return k;
}int getShmHelper(key_t k,int flags)
{int shmid=shmget(k,MAX_SIZE,flags);//創建共享內存函數shmgetif(shmid < 0){std::cerr<<errno<<":"<<strerror(errno)<<std::endl;exit(2);}return shmid;
}//獲取共享內存
int getShm(key_t k)
{return getShmHelper(k,IPC_CREAT); //如果存在就獲取,所以在客戶端client,我們可以通過這個函數,來獲取共享內存
}
////創建共享內存
int createShm(key_t k)//創建共享內存的工作,有服務端server來做
{return getShmHelper(k,IPC_CREAT | IPC_EXCL | 0666);//如果存在就創建失敗,保證了我們創建的一定是新的共享內存
}//關聯共享內存
void* attachShm(int shmid)
{void* mem =shmat(shmid,nullptr,0);if((long long)mem==-1){std::cerr<<errno<<":"<<strerror(errno)<<std::endl;exit(3);}return mem;
}//去關聯共享內存
void detachShm(void* start)
{if(shmdt(start)==-1){std::cerr<<errno<<":"<<strerror(errno)<<std::endl;}
}
//刪除共享內存
void delShm(int shmid)
{if(shmctl(shmid,IPC_RMID,nullptr)==-1)//返回-1代表調用失敗{std::cerr<<"shmctl"<<errno<<":"<<strerror(errno)<<std::endl;}
}#endif
client.cpp///
#include "comm.hpp"
int main()
{//1.獲取keykey_t k=getKey();printf("key: 0x%x\n",k);//2.獲取共享內存int shmid=getShm(k);//客戶端進行獲取共享內存printf("shmid: %d\n",shmid);sleep(5);//3.進行關聯char* start=(char*)attachShm(shmid);printf("attach success,address start:%p\n",start);sleep(5);// 4.使用即通信const char* message="hello server, 我是另外一個進程正在和你通信";pid_t id=getpid();int count =0;while(true){sleep(1);//向共享內存輸入消息snprintf(start,MAX_SIZE,"%s[pid:%d][消息編號:%d]",message,id,count++);//pid count message}// sleep(5);//5.去關聯detachShm(start);return 0;
}
/server.cpp
#include "comm.hpp"int main()
{//1.創建keykey_t k=getKey();printf("key: 0x%x\n",k);//2.創建共享內存int shmid=createShm(k);//服務端進行創建共享內存printf("shmid: %d\n",shmid);sleep(5);//3. 關聯共享內存char* start=(char*)attachShm(shmid);//返回值是共享內存地址printf("attach success,address start:%p\n",start);//4.使用while(true){printf("client say: %s\n",start);//打印由客戶端發來的消息,直接打印共享內存地址即可sleep(1);}// // 5.去關聯detachShm(start);sleep(5);//刪除sleep(10);delShm(shmid);return 0;
}

?九 共享內存的特點

共享內存的優點:所以進程間通信,速度是最快的,能大大減少拷貝次數。

同樣的代碼,考慮到鍵盤輸入和顯示器輸出 ,如果用管道來實現,會對數據進行幾次拷貝?

共享內存的缺點:不給我們進行同步和互斥的操作,沒有對數據做任何保護。

即客戶端不進行寫,服務端也一直進行讀取,服務端不進行讀取,客戶端依然進行寫入。?

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

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

相關文章

Spring Cloud 2023.x安全升級:OAuth2.1與JWT動態輪換實戰

引言&#xff1a;當安全遇上云原生&#xff0c;零停機密鑰輪換成為剛需 在微服務架構中&#xff0c;OAuth2.1與JWT已成為身份驗證的黃金標準&#xff0c;但傳統方案存在兩大痛點&#xff1a; 密鑰輪換風險&#xff1a;手動替換JWT密鑰需重啟服務&#xff0c;導致短暫鑒權中斷&…

創建私人阿里云docker鏡像倉庫

一.登錄阿里云 https://cr.console.aliyun.com/cn-hangzhou/instances 二.創建個人實例 【實例列表】 》【創建個人實例】 》【設置Registry登錄密碼】 三.創建命名空間 步驟&#xff1a;【個人實例】》【命名空間】》【創建命名空間】 注:一個賬號最多可以創建3個命名空…

oracle基礎知識視圖的定義和應用

1.1 視圖的定義 視圖(View)是數據庫中非常重要的內容&#xff0c;在實際開發中必須學會視圖的編寫。 用于產生視圖的表叫做該視圖的基表。一個視圖也可以從另一個視圖中產生。視圖是可以嵌套的。 視圖的定義存在數據庫中&#xff0c;與此定義相關的數據并沒有再存一份于數據庫中…

邊緣計算:工業自動化的智能新引擎

在工業4.0的浪潮中&#xff0c;工業自動化正經歷著前所未有的變革。隨著物聯網&#xff08;IoT&#xff09;技術的普及&#xff0c;越來越多的工業設備被連接到網絡中&#xff0c;產生了海量的數據。然而&#xff0c;傳統的云計算架構在處理這些實時性要求極高的工業數據時&…

12-SpringBoot3入門-項目打包和運行

1、打包 1&#xff09;打包插件 pom.xml <!--SpringBoot應用打包插件--> <build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plu…

【PCIE711-214】基于PCIe總線架構的4路HD-SDI/3G-SDI視頻圖像模擬源

產品概述 PCIE711-214是一款基于PCIE總線架構的4路SDI視頻模擬源。該板卡為標準的PCIE插卡&#xff0c;全高尺寸&#xff0c;適合與PCIE總線的工控機或者服務器&#xff0c;板載協議處理器&#xff0c;可以通過PCIE總線將上位機的YUV 422格式視頻數據下發通過SDI接口播放出去&…

PipeWire 音頻設計與實現分析一——介紹

PipeWire 是一個基于圖的媒體處理引擎&#xff0c;一個可以運行多媒體節點圖的媒體服務器&#xff0c;是 Linux 的音頻/視頻總線&#xff0c;它管理 Linux 系統中&#xff0c;不同應用程序對音頻和視頻設備的共享訪問。它提供了一個本地客戶端音頻 API&#xff0c;但也提供兼容…

使用卷積神經網絡識別MNIST數據集

卷積神經網絡 卷積神經網絡本質是共享權重稀疏鏈接的全連接網絡 編寫步驟 構建一個神經網絡&#xff0c;步驟是幾乎不變的&#xff0c;大概有以下幾步 準備數據集 #更高級的CNN網絡 import torch import torch.nn as nn import torch.nn.functional as F import torchvisi…

力扣125.驗證回文串

如果在將所有大寫字符轉換為小寫字符、并移除所有非字母數字字符之后&#xff0c;短語正著讀和反著讀都一樣。則可以認為該短語是一個 回文串 。 字母和數字都屬于字母數字字符。 給你一個字符串 s&#xff0c;如果它是 回文串 &#xff0c;返回 true &#xff1b;否則&#…

UR機械臂sim2real推薦包

推薦一個和ur機械臂配套的interface&#xff1a; ur_rtde Universal Robots RTDE C Interface — ur_rtde 1.6.0 documentation 也歡迎大家提供新想法和bug

CST學習筆記(三)MATLAB與CST聯合仿真-遠場數據批量導出

CST學習筆記&#xff08;三&#xff09;MATLAB與CST聯合仿真-遠場數據批量導出 一、直接數據導出 &#xff08;1&#xff09;打開遠場&#xff0c;調至笛卡爾坐標系 &#xff08;2&#xff09;然后點擊post processing → Import/Export → Plot Data (ASCII) &#xff0c;即…

藍橋杯---BFS解決FloofFill算法1---圖像渲染

文章目錄 1.算法簡介2.題目概述3.算法原理4.代碼分析 1.算法簡介 這個算法是關于我們的floodfill的相關的問題&#xff0c;這個算法其實從名字就可以看出來&#xff1a;洪水灌溉&#xff0c;其實這個算法的過程就和他的名字非常相似&#xff0c;下面的這個圖就生動的展示了這個…

我與數學建模之啟程

下面的時間線就是從我的大二上開始 9月開學就迎來了本科階段最重要的數學建模競賽——國賽&#xff0c;這個比賽一般是在9月的第二周開始。 2021年國賽是我第一次參加國賽&#xff0c;在報名前我還在糾結隊友&#xff0c;后來經學長推薦找了另外兩個學長。其實第一次國賽沒啥…

利用 SSRF 和 Redis 未授權訪問進行內網滲透

目錄 環境搭建 ?編輯 發現內網存活主機 ?編輯 掃描內網端口 ?編輯 利用 Redis 未授權訪問進行 Webshell 寫入 步驟1&#xff1a;生成 payload 方式1&#xff1a;使用python生成 payload 方式二&#xff1a;使用 Gopher 工具 步驟 2&#xff1a;寫入 Webshell&#xf…

【Vue2插槽】

Vue2插槽 Vue2插槽默認插槽子組件代碼&#xff08;Child.vue&#xff09;父組件代碼&#xff08;Parent.vue&#xff09; 命名插槽子組件代碼&#xff08;ChildNamed.vue&#xff09;父組件代碼&#xff08;ParentNamed.vue&#xff09; 代碼解釋 Vue2插槽 Vue2插槽 下面為你詳…

ORB-SLAM學習感悟記錄

orb特征點的旋轉不變性 利用灰度質心法求出的質心后&#xff0c;與形心連線所形成的角度如下圖所示&#xff1a; 這里容易對上圖進行誤解&#xff1a; 為了保證旋轉不變性&#xff0c;這里注意ORB-slam是利用這個角度旋轉坐標系&#xff0c;以新坐標系為標準從圖像中采點進行…

搜索算法------深度優先搜索

1. 介紹 深度優先搜索&#xff08;Depth-First Search&#xff0c;DFS&#xff09;是一種用于遍歷或搜索樹或圖的算法。這種算法通過盡可能深地搜索圖的分支來探索解決方案空間&#xff0c;直到達到一個沒有分支的點&#xff0c;然后回溯 1.1 原理 選擇起始點&#xff1a;從…

4.2 單相機引導機器人放料-僅考慮角度變化

【案例說明】 本案例產品在托盤中,角度變化不大(<15度);抓取沒有問題,只是放的穴位只能容許3度的角度偏差,因此需要測量產品的角度。 思路是:機器人抓料后、去固定拍照位拍照(找到與標準照片的角度偏差),機器人在放料的位置上多旋轉這個角度偏差,把產品放進去。 …

六級詞匯量積累day13

commend 表揚 exhaust 耗盡&#xff0c;用盡 weary 疲憊的&#xff0c;勞累的 fatigue 疲憊&#xff0c;勞累 obese 臃腫的&#xff0c;肥胖的 adopt 采納&#xff0c;收養 adapt 適應 accomplish 完成&#xff0c;實現 accomplishment 成就 achieve 實現&#xff0c;完成 achi…

醫院信息系統與AI賦能的介紹

隨著醫療行業的不斷發展&#xff0c;醫院信息系統&#xff08;HIS&#xff0c;Hospital Information System&#xff09;已經成為現代醫療服務不可或缺的一部分。醫院信息系統通過數字化、信息化手段&#xff0c;有效地整合了醫院內部的醫療、財務、后勤等各個業務環節&#xf…