『Linux從入門到精通』第 ? 期 - System V 共享內存

在這里插入圖片描述

文章目錄

  • 💐專欄導讀
  • 💐文章導讀
  • 🐧共享內存原理
  • 🐧共享內存相關函數
    • 🐦key 與 shmid 區別
  • 🐧代碼實例

💐專欄導讀

🌸作者簡介:花想云 ,在讀本科生一枚,C/C++領域新星創作者,新星計劃導師,阿里云專家博主,CSDN內容合伙人…致力于 C/C++、Linux 學習。

🌸專欄簡介:本文收錄于 Linux從入門到精通,本專欄主要內容為本專欄主要內容為Linux的系統性學習,專為小白打造的文章專欄。

🌸相關專欄推薦:C語言初階系列C語言進階系列C++系列數據結構與算法

💐文章導讀

共享內存是一種進程間通信的機制,允許多個進程訪問同一塊物理內存,以實現數據的共享。通過共享內存,進程可以直接讀寫共享的內存區域,而無需通過中間的數據傳輸機制(例如管道或消息隊列)進行通信,因此共享內存是最快的IPC形式。

共享內存示意圖
在這里插入圖片描述

🐧共享內存原理

  1. 創建共享內存: 在一個進程中調用系統調用(例如 shmget),請求創建一塊共享內存。這個調用需要指定內存的大小以及一些標志,以控制共享內存的權限和行為。

  2. 關聯共享內存: 其他進程通過調用系統調用(例如 shmat)將共享內存附加到它們的地址空間中。這個調用返回指向共享內存區域的指針,使得進程可以直接讀寫這塊內存。

  3. 讀寫共享內存: 一旦多個進程都關聯了同一塊共享內存,它們就可以直接對這塊內存進行讀寫操作,就像操作普通的內存一樣。因為它們共享同一塊物理內存,一個進程對共享內存的修改會立即反映到其他進程的視圖中。

  4. 分離共享內存: 當進程不再需要訪問共享內存時,它可以調用系統調用(例如 shmdt)將共享內存從它的地址空間中分離。這并不會導致共享內存的刪除,只是使得該進程無法再訪問這塊內存。

  5. 刪除共享內存: 當不再需要使用共享內存時,一個進程可以調用系統調用(例如 shmctl)請求刪除共享內存。這會導致釋放共享內存所占用的系統資源。

特點和注意事項:

  • 共享內存提供了高效的進程間通信方式,因為數據直接存儲在物理內存中,無需復制或轉移。
  • 進程需要謹慎地協調對共享內存的訪問,以避免數據一致性問題。例如,可以使用互斥鎖等同步機制。
  • 共享內存的使用需要確保不同進程使用相同的數據結構和協議,以便正確地進行數據交換和共享。
  • 在使用共享內存時,應注意防范競態條件和死鎖等并發編程的問題。

🐧共享內存相關函數

  1. 創建共享內存區域: 使用 shmget 函數創建一個共享內存區域。該函數的原型為:

    #include <sys/ipc.h>
    #include <sys/shm.h>int shmget(key_t key, size_t size, int shmflg);
    
    • key 是一個用于標識共享內存的鍵值。
    • size 是要分配的共享內存的大小(字節數)。
    • shmflg 是一組標志,通常使用 IPC_CREAT 表示如果內存不存在則創建。
  2. 連接到共享內存區域: 使用 shmat 函數將進程連接到已經存在的共享內存區域。該函數的原型為:

    #include <sys/types.h>
    #include <sys/shm.h>void *shmat(int shmid, const void *shmaddr, int shmflg);
    
    • shmid 是共享內存區域的標識符,由 shmget 返回。
    • shmaddr 通常設置為 NULL,讓系統自動選擇合適的地址。
    • shmflg 可以為 0。
  3. 使用共享內存: 一旦連接到共享內存,進程就可以直接在這塊內存中讀寫數據。

  4. 分離共享內存: 使用 shmdt 函數將進程與共享內存脫離。該函數的原型為:

    #include <sys/types.h>
    #include <sys/shm.h>int shmdt(const void *shmaddr);
    
    • shmaddr 是連接到共享內存區域的地址。
  5. 刪除共享內存區域: 使用 shmctl 函數可以刪除或控制共享內存區域的屬性。如果不再需要共享內存,可以使用 shmctl 函數的 IPC_RMID 命令刪除它。函數原型為:

    #include <sys/ipc.h>
    #include <sys/shm.h>int shmctl(int shmid, int cmd, struct shmid_ds *buf);
    
    • shmid 是共享內存區域的標識符。
    • cmd 為控制命令,可以使用 IPC_RMID 表示刪除共享內存。

這些函數提供了對共享內存的創建、連接、使用和刪除的基本操作。

🐦key 與 shmid 區別

在上面所示的接口中,shmget 函數需要一個 key_t 類型的參數 key。而其他的函數多數用到 shmid 而不會用到 key,那么這兩個參數分別是什么,有什么區別呢?

  1. key(鍵值):

    • key 是一個整數,用于在共享內存創建過程中唯一標識一個共享內存段。
    • 它并不是由系統自動生成的,而是由應用程序提供的,通常以某種方式與程序的邏輯相關。
    • 可以使用 ftok 函數將路徑名和一個整數標識符轉換為 key,以便在創建共享內存時使用。
    • key 通常用于在不同的進程之間共享相同的內存塊,因此它是創建共享內存的關鍵參數之一。
  2. shmid(共享內存標識符):

    • shmid 是一個由系統生成的標識符,用于標識已經創建的共享內存段。
    • 在使用 shmget 函數創建共享內存時,它通過返回值返回給調用者,用于后續的操作。
    • shmid 是由系統內核分配的,通常是一個唯一的整數。
    • 通過 shmat 函數將進程連接到共享內存時,需要使用 shmid 作為參數。

總的來說,key 是在共享內存創建時由應用程序指定的用戶定義的標識符,而 shmid 是由系統內核在共享內存創建時自動生成的系統級標識符。key 用于唯一標識共享內存的名字,而 shmid 用于在程序運行時標識特定的共享內存實例。

shm可以用于多個進程之間通信,在同一時刻,可能有多個共享內存被用來進行通信。所以系統中一定會有很多個共享內存同時存在,那么系統就會采取一定措施來管理這些共享內存。所以共享內存并不是單單的一塊內存空間,系統會為它構建一個結構體對象來描述它。

  • 所以,共享內存 = 內核數據結構 + 真正開辟的內存空間;

共享內存數據結構

struct shmid_ds {
struct ipc_perm shm_perm; /* operation perms */
int shm_segsz; /* size of segment (bytes) */
__kernel_time_t shm_atime; /* last attach time */
__kernel_time_t shm_dtime; /* last detach time */
__kernel_time_t shm_ctime; /* last change time */
__kernel_ipc_pid_t shm_cpid; /* pid of creator */
__kernel_ipc_pid_t shm_lpid; /* pid of last operator */
unsigned short shm_nattch; /* no. of current attaches */
unsigned short shm_unused; /* compatibility */
void *shm_unused2; /* ditto - used by DIPC */
void *shm_unused3; /* unused */
};

兩個進程使用共享內存進行通信的前提是,如何讓兩個進程使用同一塊共享內存。內存中有許許多多的共享內存,我們如何讓兩個進程使用一個共享內存呢?

這就要提到另一個函數 ftok 了。

ftok 函數是一個用于生成System V IPC(Inter-Process Communication,進程間通信)的鍵值的函數。它的主要用途是在創建System V IPC對象(如消息隊列、信號量、共享內存)時,為這些對象生成唯一的鍵值。

函數原型如下:

#include <sys/types.h>
#include <sys/ipc.h>key_t ftok(const char *pathname, int proj_id);
  • pathname 是一個與文件相關的路徑名,用于生成鍵值。通常是指向一個存在的文件的路徑。
  • proj_id 是一個用戶定義的整數,用于區分不同的IPC對象。在不同的IPC對象中,如果 pathname 相同,而 proj_id 不同,生成的鍵值也會不同。

ftok 函數通過將 pathname 轉換為一個唯一的鍵值,以確保在不同的進程中使用相同的 pathnameproj_id 參數生成的鍵值是一致的。

一般來說,ftok 函數的使用場景是在創建System V IPC對象之前,通過調用 ftok 來生成一個唯一的鍵值。這個鍵值將被傳遞給諸如 msggetsemgetshmget 等函數,用于創建具體的消息隊列、信號量或共享內存段。

需要注意的是,ftok 存在一些限制和注意事項,比如需要確保 pathname 指向的文件是存在的,否則 ftok 會返回 -1。此外,由于 proj_id 是一個整數,因此其范圍應在0到255之間,以保證生成的鍵值在合理的范圍內。

所以,key 是在內核中使用的,類比文件的 inode 編號。而 shmid 是給用戶使用的,類比文件的文件描述符 fd。

🐧代碼實例

接下來,我們就通過一個簡單的代碼設計來熟悉共享內存的使用。該設計的內容是,通過共享內存讓兩個進程(Server 與 Client)進行通信。

communicate.hpp

該頭文件內提供shm所用到的函數方法。

/* communicate.hpp */#ifndef __COMM_HPP__
#define __COMM_HPP__#include <iostream>
#include <cerrno>
#include <cstdio>
#include <cstring>
#include <cassert>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/shm.h>
#include <unistd.h>using namespace std;#define PATHNAME "." // 文件路徑(用于key生成)
#define PROJID 0x12138 // 項目ID(用于key生成)const int gsize = 4096;key_t getKey() // 獲取鍵值
{key_t key = ftok(PATHNAME, PROJID);if (key == -1){cerr << "error: " << errno << " : " << strerror(errno) << endl;exit(1);}return key;
}static int createShmHelper(key_t key, int size, int flag)
{int shmid = shmget(key, gsize, flag);if (shmid == -1){cerr << "error: " << errno << " : " << strerror(errno) << endl;exit(2);}return shmid;
}int createShm(key_t key, int size) // 創建共享內存
{umask(0);return createShmHelper(key, size, IPC_CREAT | IPC_EXCL | 0666);
}int getShm(key_t key, int size) // 獲取共享內存
{umask(0);return createShmHelper(key, size, IPC_CREAT);
}char *attachShm(int shmid) // 關聯共享內存
{char *start = (char *)shmat(shmid, nullptr, 0); // 將共享內存段連接到進程地址空間return start;
}void detachShm(char *start) // 去關聯
{int n = shmdt(start); // 將共享內存段與當前進程脫離assert(n != -1);(void)n;
}void delShm(int shmid) // 釋放共享內存
{int n = shmctl(shmid, IPC_RMID, nullptr); // 釋放共享內存assert(n != -1);(void)n;
}#define SERVER 1
#define CLIENT 0class Init
{
public:Init(int type): type(type){key_t key = getKey();if (type == SERVER)shmid = createShm(key, gsize);elseshmid = getShm(key, gsize);start = attachShm(shmid);}char *getStart() { return start; }~Init(){detachShm(start);if (type == SERVER)delShm(shmid); // 只有共享內存創建者才負責釋放}private:char *start; // 起始地址int type;    // server or clientint shmid;
};#endif

Server

/* Server.cc */
#include "communicate.hpp"
#include <unistd.h>using namespace std;int main()
{   // 建立連接Init init(SERVER);char *start = init.getStart();// 開始通信int n = 0;while (n <= 30){cout << "client -> server# " << start << endl;sleep(1);n++;}return 0;
}

Client

/* Client.cc */
#include "communicate.hpp"
using namespace std;int main()
{// 建立連接Init init(CLIENT);char *start = init.getStart();// 開始通信char c = 'A';while (c <= 'Z'){start[c - 'A'] = c;c++;start[c - 'A'] = 0;sleep(1);}return 0;
}

效果展示

在這里插入圖片描述
注意

當我們運行完一次程序后,再次運行程序會發生錯誤:

$ ./server 
error: 17 : File exists

原因是,上次程序運行時創建的共享內存仍然存在,可以使用 ipcs -m 指令來查看已經有的共享內存:

$ ipcs -m------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status      
0x3801203f 0          hxy        666        4096       0                       

由此可見,共享內存的生命周期是隨系統的,不隨進程。

我們可以使用 ipcrm -m 指令刪除指定的共享內存:

$ ipcrm -m shmid

本章的內容到這里就結束了!如果覺得對你有所幫助的話,歡迎三連~

在這里插入圖片描述

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

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

相關文章

CentOS7安裝DockerCompose和Docker鏡像倉庫的配置

CentOS7安裝DockerCompose 1.下載 Linux下需要通過命令下載&#xff1a; # 安裝 curl -L https://github.com/docker/compose/releases/download/1.23.1/docker-compose-uname -s-uname -m > /usr/local/bin/docker-compose2.修改文件權限 修改文件權限&#xff1a; # …

YOLOv9獨家原創改進|加入幽靈卷積Ghost Convolution模塊,輕量化!

專欄介紹&#xff1a;YOLOv9改進系列 | 包含深度學習最新創新&#xff0c;主力高效漲點&#xff01;&#xff01;&#xff01; 一、論文摘要 由于內存和計算資源有限&#xff0c;在嵌入式設備上部署卷積神經網絡是困難的。特征圖中的冗余是那些成功的細胞神經網絡的一個重要特征…

【網站項目】158企業人事管理系統

&#x1f64a;作者簡介&#xff1a;擁有多年開發工作經驗&#xff0c;分享技術代碼幫助學生學習&#xff0c;獨立完成自己的項目或者畢業設計。 代碼可以私聊博主獲取。&#x1f339;贈送計算機畢業設計600個選題excel文件&#xff0c;幫助大學選題。贈送開題報告模板&#xff…

突破編程_C++_字符串算法(判斷字符串是否包含)

1 算法題 &#xff1a;判斷一個字符串是否包含另一個字符串的所有字符&#xff08;不一定連續&#xff09; 1.1 題目含義 判斷一個字符串&#xff08;稱為“主字符串”或“大字符串”&#xff09;是否包含另一個字符串&#xff08;稱為“子字符串”或“小字符串”&#xff09…

代碼隨想錄算法訓練營第31天—貪心算法05 | ● 435. 無重疊區間 ● *763.劃分字母區間 ● *56. 合并區間

435. 無重疊區間 https://programmercarl.com/0435.%E6%97%A0%E9%87%8D%E5%8F%A0%E5%8C%BA%E9%97%B4.html 考點 貪心算法重疊區間 我的思路 先按照區間左坐標進行排序&#xff0c;方便后續處理進行for循環&#xff0c;循環范圍是0到倒數第二個元素如果當前區間和下一區間重疊…

在Linux以命令行方式(靜默方式/非圖形化方式)安裝MATLAB(正版)

1.根據教程&#xff0c;下載windows版本matlab&#xff0c;打開圖形化界面&#xff0c;選擇linux版本的只下載不安裝 2.獲取安裝文件夾 3.獲取許可證 4.安裝 &#xff08;1&#xff09;跳過引用文章的2.2章節 &#xff08;2&#xff09;本文的安裝文件夾代替引用文章的解壓IS…

Java進階(鎖)——鎖的升級,synchronized與lock鎖區別

目錄 引出Java中鎖升級synchronized與lock鎖區別 緩存三兄弟&#xff1a;緩存擊穿、穿透、雪崩緩存擊穿緩存穿透緩存雪崩 總結 引出 Java進階&#xff08;鎖&#xff09;——鎖的升級&#xff0c;synchronized與lock鎖區別 Java中鎖升級 看一段代碼&#xff1a; public class…

Fastwhisper + Pyannote 實現 ASR + 說話者識別

文章目錄 前言一、faster-whisper簡單介紹二、pyannote.audio介紹三、faster-whisper pyannote.audio 實現語者識別四、多說幾句 前言 最近在研究ASR相關的業務&#xff0c;也是調研了不少模型&#xff0c;踩了不少坑&#xff0c;ASR這塊&#xff0c;目前中文普通話效果最好的…

Scrapy與分布式開發(1.1):課程導學

Scrapy與分布式開發&#xff1a;從入門到精通&#xff0c;打造高效爬蟲系統 課程大綱 在這個專欄中&#xff0c;我們將一起探索Scrapy框架的魅力&#xff0c;以及如何通過Scrapy-Redis實現分布式爬蟲的開發。在本課程導學中&#xff0c;我們將為您簡要介紹課程的學習目標、內容…

Verilog Coding Styles For Improved Simulation Efficiency論文學習記錄

原文基于Verilog-XL仿真器&#xff0c;測試了以下幾種方式對仿真效率的影響。 1. 使用 Case 語句而不是 if / else if 語句 八選一多路選擇器 case 實現效率比 if / else if 提升 6% 。 2. 如果可以盡量不使用 begin end 語句 使用 begin end 的 ff 觸發器比不使用 begin end …

初學者學習51還是STM32

初學者學習51還是STM32 在嵌入式系統領域&#xff0c;51和STM32是兩種常見的單片機架構。對于初學者來說&#xff0c;選擇學習哪種架構可能會成為一個難題。本文將對初學者學習51和STM32進行比較&#xff0c;以幫助讀者做出明智的選擇。 1. 51架構 51架構是指Intel 8051系列…

深度相機xyz點云文件三維坐標和jpg圖像文件二維坐標的相互變換函數

深度相機同時拍攝xyz點云文件和jpg圖像文件。xyz文件里面包含三維坐標[x,y,z]和jpg圖像文件包含二維坐標[x&#xff0c;y],但是不能直接進行變換&#xff0c;需要一定的步驟來推演。 下面函數是通過box二維框[xmin, ymin, xmax, ymax, _, _ ]去截取xyz文件中對應box里面的點云…

MyCAT學習——在openEuler22.03中安裝MyCAT2(網盤下載版)

準備工作 因為MyCAT 2基于JDK 1.8開發。也需要在虛擬機中安裝JDK&#xff08;JDK官網就能下載&#xff0c;我這提供一個捷徑&#xff09; jdk-8u401-linux-x64.rpmhttps://pan.baidu.com/s/1ywcDsxYOmfZONpmH9oDjfw?pwdrhel下載對應的tar安裝包,以及對應的jar包 安裝程序包…

九州金榜|孩子厭學要怎么辦?

孩子從小學到初中再到高中&#xff0c;孩子出現厭學情緒很正常&#xff0c;但是孩子出現厭學情緒后&#xff0c;就必然會影響到孩子學習成績&#xff0c;孩子產生厭學情緒的原因有哪些呢&#xff1f;只有找準孩子厭學原因才能去幫助孩子怎樣去克服孩子厭學情緒&#xff0c;下面…

ajax請求servlet成功但接收不到返回數據問題

ajax請求servlet成功但接收不到返回數據問題 javaweb初學者&#xff0c;最近老師布置的課設&#xff0c;所有功能都完成了&#xff0c;唯獨ajax與servlet交互出現問題&#xff0c;無論怎么調試都收不到數據 查詢兩天無果&#xff0c;剛才無意間看到 Crabime前輩的文章才恍然大…

深入解析YOLO:實時目標檢測技術的革命者

深入解析YOLO&#xff1a;實時目標檢測技術的革命者 目標檢測作為計算機視覺領域的一個核心任務&#xff0c;一直以來都是研究的熱點。而YOLO&#xff08;You Only Look Once&#xff09;技術作為其中的杰出代表&#xff0c;以其獨特的處理方式和卓越的性能&#xff0c;成為了…

day34貪心算法 part03

1005. K 次取反后最大化的數組和 簡單 給你一個整數數組 nums 和一個整數 k &#xff0c;按以下方法修改該數組&#xff1a; 選擇某個下標 i 并將 nums[i] 替換為 -nums[i] 。 重復這個過程恰好 k 次。可以多次選擇同一個下標 i 。 以這種方式修改數組后&#xff0c;返回數…

力扣棧隊列篇

以下思路來自代碼隨想錄以及官方題解。 文章目錄 232.用棧實現隊列225.用隊列實現棧20.有效的括號1047.刪除字符串中所有相鄰重復項150.逆波蘭表達式求值347.前K個高頻元素 232.用棧實現隊列 請你僅使用兩個棧實現先入先出隊列。隊列應當支持一般隊列支持的所有操作&#xff…

OSError: [WinError 1455] 頁面文件太小,無法完成操作。

[問題描述]&#xff1a;OSError: [WinError 1455] 頁面文件太小&#xff0c;無法完成操作。 原因1&#xff1a;線程數太大 方法&#xff1a;改小線程&#xff08;workers&#xff09;數。 原因2&#xff1a;虛擬內存太小或為0&#xff0c;調大虛擬內存。 方法&#xff1a;右鍵…

mysql索引過長Specialed key was too long的解決方法

在創建要給表的時候遇到一個有意思的問題&#xff0c;提示Specified key was too long; max key length is 767 bytes&#xff0c;從描述上來看&#xff0c;是Key太長&#xff0c;超過了指定的 767字節限制。通常出現在嘗試創建一個過長的唯一鍵&#xff08;UNIQUE KEY&#xf…