Linux操作系統--進程間通信(system V共享內存)

目錄

1.system V共享內存

2.共享內存數據結構

3.共享內存函數

4.實例代碼:


1.system V共享內存

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

共享內存區它是指多個進程可以訪問和共享同一塊內存區域,從而實現進程間的數據交換和通信。共享內存區通常被認為是最快的IPC形式,因為它避免了數據的復制和傳輸,進程可以直接在共享的內存區域中讀寫數據,速度較快

system V的特性:

  1. 共享內存沒有同步互斥之類的保護機制
  2. 共享內存是所有進程間通信中,速度最快的
  3. 共享內存內部的數據由用戶自己維護

2.共享內存數據結構

進程間通信的本質:先讓不同的進程,看到同一份資源

操作系統要不要管理所有的共享內存?要,先描述再組織

1.?防止沖突

如果沒有操作系統統一管理共享內存,多個進程可能會同時嘗試訪問同一塊內存區域,從而引發數據競爭或不一致的情況。這種情況下可能導致程序崩潰甚至整個系統不穩定。

2.?保護隱私與安全

操作系統通過權限控制機制確保只有授權的進程能夠訪問特定的共享內存區域。這不僅有助于維護不同應用程序之間的隔離性,還增強了整體系統的安全性。

3.?優化性能

通過虛擬內存技術和工作集理論的支持,操作系統可以更高效地調度和分配共享內存給各個需要它的進程。這樣不僅可以減少不必要的物理內存占用,還能提升多任務環境下的響應速度和吞吐量。

4.?簡化編程模型

對于開發者而言,擁有一個由操作系統提供并保障的一致性和可靠性高的共享內存抽象層是非常重要的。它可以讓程序員專注于業務邏輯而非底層復雜的同步細節。

struct shmid_ds {struct ipc_perm shm_perm;        /* Ownership and permissions */size_t          shm_segsz;       /* Size of segment (bytes) */time_t          shm_atime;       /* Last attach time */time_t          shm_dtime;       /* Last detach time */time_t          shm_ctime;       /* Last change time */unsigned short  shm_cpid;        /* PID of creator */unsigned short  shm_lpid;        /* PID of last operator */unsigned short  shm_nattch;      /* Number of current attaches */
};
struct ipc_perm {key_t          __key;    /* Key supplied to shmget(2) */uid_t          uid;      /* Effective UID of owner */gid_t          gid;      /* Effective GID of owner */uid_t          cuid;     /* Effective UID of creator */gid_t          cgid;     /* Effective GID of creator */unsigned short mode;     /* Permissions + SHM_DEST andSHM_LOCKED flags */unsigned short __seq;    /* Sequence number */};

3.共享內存函數

ftok函數

功能:生成一個key

首先,我們需要談談 什么是key

  1. key是一個數字,這個數字是幾不重要,關鍵在于它必須在內核中具有唯一性,能夠讓不同的進程進行唯一性標識
  2. 第一個進程可以通過key創建共享內存,第二個之后的進程,只要拿著同一個key就可以和第一個進程看到同一個共享內存
  3. 對于一個已經創建好的共享內存key在哪?key在共享內存的描述對象中
  4. 第一次創建的時候必須有一個key了
  5. key--類似路徑--唯一

參數

????????pathname:一個已存在的文件路徑名,函數會依據該文件的inode號參與生成鍵值。要確保調用ftok的進程對該文件有讀權限

????????proj_id:一個整數,通常使用0 - 255(8 位)范圍內的值,作為項目標識符進一步確保生成鍵值的唯一性

返回值:key值

shmget函數

功能:用來創建共享內存
原型
????????int shmget(key_t key, size_t size, int shmflg);
參數
????????key:這個共享內存段名字
????????size:共享內存大小(單位是字節)
????????shmflg:由九個權限標志構成,它們的用法和創建文件時使用的mode模式標志是一樣的
返回值:成功返回一個非負整數,即該共享內存段的標識碼;失敗返回-1

shmat函數

功能:將共享內存段連接到進程地址空間
原型
????????void *shmat(int shmid, const void *shmaddr, int shmflg);
參數
????????shmid: 共享內存標識
????????shmaddr:指定共享內存出現在進程內存地址的位置
????????shmflg:它的兩個可能取值是SHM_RND和SHM_RDONLY
返回值:成功返回一個指針,指向共享內存第一個節;失敗返回-1

說明:

shmaddr為NULL,核心自動選擇一個地址
shmaddr不為NULL且shmflg無SHM_RND標記,則以shmaddr為連接地址。
shmaddr不為NULL且shmflg設置了SHM_RND標記,則連接的地址會自動向下調整為SHMLBA的整數倍。公式:shmaddr -
(shmaddr % SHMLBA)
shmflg=SHM_RDONLY,表示連接操作用來只讀共享內存

shmdt函數

功能:將共享內存段與當前進程脫離
原型
????????int shmdt(const void *shmaddr);
參數
????????shmaddr: 由shmat所返回的指針
返回值:成功返回0;失敗返回-1


注意:將共享內存段與當前進程脫離不等于刪除共享內存段

shmctl函數

功能:用于控制共享內存
原型
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
參數
shmid:由shmget返回的共享內存標識碼
cmd:將要采取的動作(有三個可取值)
buf:指向一個保存著共享內存的模式狀態和訪問權限的數據結構
返回值:成功返回0;失敗返回-1

cmd:控制命令,常用值如下:

  • IPC_RMID:標記共享內存段為 “待刪除” 狀態(實際刪除發生在所有進程斷開連接后)。
  • IPC_STAT:將共享內存的狀態信息復制到?buf中。
  • IPC_SET:使用中的buf值更新共享內存的權限和所有者信息。
  • IPC_INFO:獲取系統范圍內共享內存的限制和狀態(需 Linux 特定參數)。

注意:
共享內存的生命周期是隨內核的!

用戶不主動關閉,共享內存會一直存在

除非內核重啟(用戶釋放)

ipcs -m

指令用于顯示系統中當前存在的共享內存段的詳細信息。通過運行這個指令,可以查看系統中的共享內存段的標識符、大小、權限等信息。這對于診斷和監視系統中共享內存的使用情況非常有用。

4.實例代碼:

makefile

.PHONY:all
all:processa processbprocessa:processa.ccg++ -o $@ $^ -g -std=c++11
processb:processb.ccg++ -o $@ $^ -g -std=c++11.PHONY:clean
clean:rm -f processa processb

comm.hpp

#ifndef __COMM_HPP__
#define __COMM_HPP__#include <iostream>
#include<string>
#include<cstdlib>
#include<cstring>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<sys/types.h>
#include<sys/stat.h>#include "log.hpp"using namespace std;Log log;
//共享內存的大小一般建議是4096的整數倍
//4097,實際上操作系統給你的是4096*2的大小
const int size = 4096;
const string pathname="/home/xchild";
const int proj_id = 0x6666;key_t GetKey()
{key_t k = ftok(pathname.c_str(), proj_id);if(k < 0){log(Fatal,"ftok error: %s",strerror(errno));exit(1);}log(Info, "ftok success, key is : 0x%x",k);return k;
}int GetShareMemHelper(int flag)
{key_t k = GetKey();int shmid = shmget(k, size, flag);if(shmid < 0){log(Fatal,"create share memory error: %s",strerror(errno));exit(2);}log(Info,"create share memory success, shmid: %d",shmid);return shmid;
}int CreateShm()
{return GetShareMemHelper(IPC_CREAT);
}int GetShm()
{return GetShareMemHelper(IPC_CREAT);
}#define FIFO_FILE "./myfifo"
#define MODE 0664enum{FIFO_CREATE_ERR = 1,FIFO_DELETE_ERR,FIFO_OPEN_ERR
};class Init
{public:Init(){//創建管道int n = mkfifo(FIFO_FILE, MODE);if(n == -1){perror("mkfifo");exit(FIFO_CREATE_ERR);}}~Init(){int m = unlink(FIFO_FILE);if(m == -1){perror("unlink");exit(FIFO_DELETE_ERR);}}
};#endif

processa.cc

#include"comm.hpp"extern Log log;int main()
{Init init;int shmid = CreateShm();char *shmaddr = (char*)shmat(shmid,nullptr,0);//ipc code 在這里!!//一旦有人把數據寫入到共享內存,其實我們立馬能看到了//不需要經過系統調用,直接就能看到數據了int fd = open(FIFO_FILE, O_RDONLY);//等待寫入方打開之后,自己才會打開文件,向后執行,open阻塞了if(fd < 0){log(Fatal,"error string: %s, error code: %d", strerror(errno),errno);exit(FIFO_OPEN_ERR);}struct shmid_ds shmds;while(true){char c;ssize_t s = read(fd, &c, 1);if(s == 0)break;else if(s < 0)break;cout << "client say@ " <<shmaddr <<endl;//直接訪問共享內存sleep(1);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;}shmdt(shmaddr);shmctl(shmid, IPC_RMID,nullptr);close(fd);return 0;
}

processb.cc

#include "comm.hpp"int main()
{int shmid = GetShm();char *shmaddr = (char*)shmat(shmid,nullptr,0);int fd = open(FIFO_FILE,O_WRONLY);//等待寫入方打開之后,自己才會打開文件,向后執行,open阻塞了if(fd < 0){log(Fatal,"error string: %s, error code: %d",strerror(errno),errno);exit(FIFO_OPEN_ERR);}//一旦有了共享內存,掛接到自己的地址空間中,你直接把他當作你的內存空間來用即可//不需要調用系統調用//ipc codewhile(true){cout<<"Please Enter@ ";fgets(shmaddr,4096,stdin);write(fd,"c",1);//通知對方}shmdt(shmaddr);close(fd);return 0;
}

log.hpp

#pragma once#include <iostream>
#include <time.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>#define SIZE 1024#define Info 0
#define Debug 1
#define Warning 2
#define Error 3
#define Fatal 4#define Screen 1
#define Onefile 2
#define Classfile 3#define LogFile "log.txt"class Log
{
public:Log(){printMethod = Screen;path = "./log/";}void Enable(int method){printMethod = method;}std::string levelToString(int level){switch (level){case Info:return "Info";case Debug:return "Debug";case Warning:return "Waring";case Error:return "Error";case Fatal:return "Fatal";default:return "None";}}void printLog(int level, const std::string &logtxt){switch (printMethod){case Screen:std::cout << logtxt << std::endl;break;case Onefile:printOneFile(LogFile, logtxt);break;case Classfile:printClassFile(level, logtxt);break;}}void printOneFile(const std::string &logname, const std::string &logtxt){std::string _logname = path + logname;int fd = open(_logname.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0666);if (fd < 0)return;write(fd, logtxt.c_str(), logtxt.size());close(fd);}void printClassFile(int level, const std::string &logtxt){std::string filename = LogFile;filename += ".";filename += levelToString(level);printOneFile(filename, logtxt);}~Log(){}void operator()(int level, const char *format, ...){time_t t = time(nullptr);struct tm *ctime = localtime(&t);char leftbuffer[SIZE];snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%d-%d-%d %d:%d:%d]", levelToString(level).c_str(),ctime->tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday,ctime->tm_hour, ctime->tm_min, ctime->tm_sec);va_list s;va_start(s, format);char rightbuffer[SIZE];vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);va_end(s);// 格式:默認部分+自定義部分char logtxt[SIZE * 2];snprintf(logtxt, sizeof(logtxt), "%s %s\n", leftbuffer, rightbuffer);printLog(level, logtxt);}private:int printMethod;std::string path;
};

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

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

相關文章

【C++】類與對象

目錄 1、類的定義 2、類的訪問限定符及封裝 3、類的實例化 4、類和對象的大小 5、this 指針 6、類的六個默認成員函數 構造函數 析構函數 拷貝構造函數 賦值重載函數 取地址運算符的重載函數 7、運算符重載 8、const 成員函數 9、 static 成員 10、友元 11、…

現代簡約中式通用,民國畫報風,中國風PPT模版8套一組分享

中國風PPT模版分享&#xff1a;中國風PPT模版分享https://pan.quark.cn/s/abbf75507c5f 第1套PPT模版&#xff1a;棕色調中式窗欞封面&#xff0c;水墨山水背景配白梅與燈籠流蘇&#xff0c;適用于教學課件目錄設計&#xff0c;展現濃郁的書卷氣息。 第2套PPT模版&#xff1a;米…

django擴展練習記錄

一、Django 中使用 django-apscheduler 實現定時任務 可以方便地管理周期性任務&#xff08;如每天清理緩存、定時發送郵件等&#xff09; 1. 安裝 pip install django-apscheduler -i https://pypi.tuna.tsinghua.edu.cn/simple #0.7.02.添加到應用&#xff0c;python m…

Guided Filtering相關記錄

一、背景介紹 以前折騰保邊濾波時候&#xff0c;刷了一些Guided Filtering相關資料。這里主要是對它們做個算法效果復現和資料簡單整理。 二、Guided Filtering 1、基本原理 原版Guided Filtering的提出&#xff0c;主要是為了改善雙邊濾波做保邊平滑濾波器時候的梯度翻轉偽影…

知識圖譜系列(2):知識圖譜的技術架構與組成要素

1. 引言 知識圖譜作為一種強大的知識表示和組織方式,已經在搜索引擎、推薦系統、智能問答等多個領域展現出巨大的價值。在之前的上一篇文章中,我們介紹了知識圖譜的基礎概念與發展歷程,了解了知識圖譜的定義、核心特征、發展歷史以及在AI發展中的地位與作用。 要深入理解和…

操作系統|| 虛擬內存頁置換算法

題目 寫一個程序來實現 FIFO 和 LRU 頁置換算法。首先&#xff0c;產生一個隨機的頁面引用序列&#xff0c;頁面數從 0~9。將這個序列應用到每個算法并記錄發生的頁錯誤的次數。實現這個算法時要將頁幀的數量設為可變。假設使用請求調頁。可以參考所示的抽象類。 抽象類&…

開發與AI融合的Windsurf編輯器

Windsurf編輯器是開發人員和人工智能真正融合在一起的地方&#xff0c;提供了一種感覺像文字魔術的編碼體驗。 手冊&#xff1a;Windsurf - Getting Started 下載鏈接&#xff1a;Download Windsurf Editor for Windows | Windsurf (formerly Codeium) 下載安裝 從上面的下載…

【Java】網絡編程(Socket)

網絡編程 Socket 我們開發的網絡應用程序位于應用層&#xff0c;TCP和UDP屬于傳輸層協議&#xff0c;在應用層如何使用傳輸層的服務呢&#xff1f;在應用層和傳輸層之間&#xff0c;則使用套接字Socket來進行分離 套接字就像是傳輸層為應用層開的一個小口&#xff0c;應用程…

【教程】Docker方式本地部署Overleaf

轉載請注明出處&#xff1a;小鋒學長生活大爆炸[xfxuezhagn.cn] 如果本文幫助到了你&#xff0c;歡迎[點贊、收藏、關注]哦~ 目錄 背景說明 下載倉庫 初始化配置 修改監聽IP和端口 自定義網站名稱 修改數據存放位置 更換Docker源 更換Docker存儲位置 啟動Overleaf 創…

根據用戶ID獲取所有子節點數據或是上級直屬節點數據

一、根據用戶ID獲取所有子節點&#xff0c;通過存儲過程來實現 CREATE DEFINERcrmeb% PROCEDURE proc_get_user_all_children( IN rootUid INTEGER, -- 要查詢的根用戶ID IN includeSelf BOOLEAN -- 是否包含自身(1包含,0不包含) ) BEGIN -- 聲明變…

計算機組成原理——數據的表示

2.1數據的表示 整理自Beokayy_ 1.進制轉換 十六進制與二進制的轉換 一位十六進制等于四位二進制 四位二進制等于一位十六進制 0x173A4C0001 0111 0011 1010 0100 1100 十六進制與十進制的轉換 十六轉十&#xff1a;每一位數字乘以相應的16的冪再相加 十轉十六&#xff1a…

基于MATLAB-GUI圖形界面的數字圖像處理

基于MATLAB GUI的數字圖像處理系統實現方案&#xff0c;包含常見圖像處理功能。代碼分為兩部分&#xff1a;GUI界面設計和回調函數實現。 %% 第一部分&#xff1a;創建GUI界面 (使用GUIDE) % 1. 打開GUIDE: guide % 2. 創建新GUI&#xff0c;添加以下控件&#xff1a; % - …

從裸機開發到實時操作系統:FreeRTOS詳解與實戰指南

從裸機開發到實時操作系統&#xff1a;FreeRTOS詳解與實戰指南 本文將帶你從零開始&#xff0c;深入理解嵌入式系統中的裸機開發與實時操作系統&#xff0c;以FreeRTOS為例&#xff0c;全面剖析其核心概念、工作原理及應用場景。無論你是嵌入式新手還是希望提升技能的開發者&am…

zabbix7.2最新版本 nginx自定義監控(三) 設置觸發器

安裝zabbix-get服務 在zabbix-server端口安裝zabbix-get服務 [rootlocalhost ~]# dnf install -y zabbix-get Last metadata expiration check: 1:55:49 ago on Wed 14 May 2025 09:24:49 AM CST. Dependencies resolved. Package Architectur…

在 Kotlin 中,什么是解構,如何使用?

在 Kotlin 中&#xff0c;解構是一種語法糖&#xff0c;允許將一個對象分解為多個獨立的變量。 這種特性可以讓代碼更簡潔、易讀&#xff0c;尤其適用于處理數據類、集合&#xff08;如 Pair、Map&#xff09;或其他結構化數據。 1 解構的核心概念 解構通過定義 componentN()…

html的鼠標點擊事件有哪些寫法

在HTML中&#xff0c;鼠標點擊事件的實現方式多樣&#xff0c;以下從基礎語法到現代實踐為您詳細梳理&#xff1a; 一、基礎寫法&#xff1a;直接內聯事件屬性 在HTML標簽內通過on前綴事件屬性綁定處理函數&#xff0c;適合簡單交互場景&#xff1a; <!-- 單擊事件 -->…

基于EFISH-SCB-RK3576/SAIL-RK3576的智能垃圾分類站技術方案

&#xff08;國產化替代J1900的環保物聯網解決方案&#xff09; 一、硬件架構設計? ?多模態感知系統? ?高精度識別模塊?&#xff1a; 雙光譜成像&#xff08;RGB近紅外&#xff09;融合NPU加速ResNet50模型&#xff0c;支持40垃圾品類識別&#xff08;準確率>99.5%&am…

PYTHON訓練營DAY27

裝飾器 編寫一個裝飾器 logger&#xff0c;在函數執行前后打印日志信息&#xff08;如函數名、參數、返回值&#xff09; logger def multiply(a, b):return a * bmultiply(2, 3) # 輸出: # 開始執行函數 multiply&#xff0c;參數: (2, 3), {} # 函數 multiply 執行完畢&a…

Android Studio 中 build、assemble、assembleDebug 和 assembleRelease 構建 aar 的區別

上一篇&#xff1a;Tasks中沒有build選項的解決辦法 概述&#xff1a; 在構建 aar 包時通常會在下面的選項中進行構建&#xff0c;但是對于如何構建&#xff0c;選擇哪種方式構建我還是處于懵逼狀態&#xff0c;所以我整理了一下幾種構建方式的區別以及如何選擇。 1. build…

視頻質量分析時,遇到不同分辨率的對照視頻和源視頻,分辨率對齊的正確順序。

背景 我們平時在做視頻轉碼后&#xff0c;會用VMAF/PSNR得評分工具進行視頻對比的評分&#xff0c;但是這幾種客觀評分方式都有一個要求就是分辨率要一模一樣&#xff0c;因為這樣才對像素點做數學運算。 但是分辨率對齊其實有兩種選擇&#xff0c;例如源視頻是1080P&#xf…