Linux 程式設計1:深入淺出 Linux 共用記憶體

来源:https://www.cnblogs.com/happenlee/archive/2019/01/02/10210396.html
-Advertisement-
Play Games

筆者最近在閱讀 "Aerospike" 論文時,發現了Aerospike是利用了Linux 共用記憶體機制來實現的存儲索引快速重建的。這種方式比傳統利用索引文件進行快速重啟的方式大大提高了效率。( 減少了磁碟 i/o,但是缺點是耗費記憶體,並且伺服器一旦重啟之後就只能冷重啟了~~ )而目前筆者工作之中維 ...


筆者最近在閱讀Aerospike 論文時,發現了Aerospike是利用了Linux 共用記憶體機制來實現的存儲索引快速重建的。這種方式比傳統利用索引文件進行快速重啟的方式大大提高了效率。(減少了磁碟 i/o,但是缺點是耗費記憶體,並且伺服器一旦重啟之後就只能冷重啟了~~)而目前筆者工作之中維護的 NoSQL 資料庫也是通過同樣的機制實現存儲索引的快速重建的,工欲善其事,必先利其器。所以筆者花時間調研了一下Linux共用記憶體的機制,希望對各位有所幫助~~

1.共用記憶體簡介

說到共用記憶體,有過操作系統學習的童靴應該十分熟悉,往往聊到進程之間通信的4種方式時就能脫口而出(面試最常見的問題之一啊,哈哈哈~~):

  • 共用記憶體
  • 消息隊列
  • 信號量
  • Socket

今天我們的主角是共用記憶體。如下圖所示,所謂的共用記憶體,就是由多個進程的虛擬記憶體空間共同地映射到同一段物理記憶體空間,來實現記憶體的共用。
共用記憶體

共用記憶體通常是 ipc 之中效率最高的方式。Linux 之中實現共用記憶體的方式通常有如下幾類:

  • mmap記憶體共用映射 (通常用於父子進程之間的記憶體共用,存在一定局限性,後文不表
  • System V的共用記憶體
  • POSIX共用記憶體

我們平時討論主要的共用記憶體就是後面兩者,但是其實無論是 System V 還是 POSIX 形式的共用記憶體,底層都是基於記憶體文件系統tmpfs實現的,二者的主要區別是在介面設計上,POSIX旨在提供所有系統都一致的介面,遵循了 Linux 系統之中一切皆為文件的理念。而System V只實現自己的一套內生的IPC邏輯,所以兩者在使用上存在一些差異,由於 Aerospike 之中沿用了 System V 的機制,所以筆者後續的介紹也以 System V 的共用記憶體來展開。

共用記憶體雖然給了多進程通信的效率帶來了質的飛躍,但是存在的問題也很明顯:每一個參與使用共用記憶體的進程,都可以讀取寫入數據,這自然而然帶來了記憶體空間等競爭的問題。雖然這裡可以通過類似於管道的機制來單向通信來規避競爭的問題,但是額外引入的複雜度和記憶體占用同樣也是問題)所以這裡我們也可以反思共用記憶體真的是用來進程間通信的嗎?筆者這裡反而是這樣認為的:通過通信來共用記憶體,而不是通過共用記憶體來通信

2.共用記憶體的設置與查看

使用共用記憶體,需要在系統層面進行一些設置。這章需要介紹一些共用記憶體相關的設置,在 Linux 系統之中和共用記憶體有關的文件有:

  /proc/sys/kernel/shmmni:限制整個系統可創建共用記憶體段個數。

  /proc/sys/kernel/shmall: 限制系統用在共用記憶體上的記憶體的頁數。

  /proc/sys/kernel/shmmax:限制一個共用記憶體段的最大長度,位元組為單位。

在使用共用記憶體時,我們可以修改上述文件來滿足我們的設置需求。這裡要註意的是,上述的配置文件是臨時性的,重啟之後就失效了。如果需要永久性設置這些參數,可以修改/etc/sysctl.conf來完成共用記憶體的設置。

共用記憶體本質上是對記憶體空間的使用,同時也是 ipc 的方式之一,所以我們可以使用對應的 Linux 命令來查看對應共用記憶體的使用:

free 可以顯示系統的記憶體占用,共用記憶體的記憶體占用會歸類在 shared,buffer/cache
free 命令查看共用記憶體
而更為詳盡的共用記憶體的數據,可以通過ipcs -m的命令來進行展示。
共用記憶體的使用狀況
這裡簡單介紹一下,共用記憶體各個列所代表的含義:

  • key:共用記憶體的key,後文會通過程式來解釋 key 的含義。
  • shmil:共用記憶體的編號。
  • owner:創建的共用記憶體的用戶。
  • perms:共用記憶體的許可權,基於用戶的。
  • bytes:共用記憶體的大小。
  • nattch:連接到共用記憶體的進程數。
  • status:共用記憶體的狀態,顯示“dest”表示共用記憶體段已經被刪除,但是還有別的引用,共用記憶體是通過引用計數的方式來決定生命周期,一旦程式應用記憶體地址的計數為0,操作系統會回收對應的記憶體資源。

在這裡如果需要清理對應的共用記憶體,可以藉助命令ipcrm -m [shmid]來回收對應的記憶體空間。

3.共用記憶體的使用

int shmget(key_t key, size_t size, int shmflg);

int shmctl(int shmid, int cmd, struct shmid_ds *buf);

void *shmat(int shmid, const void *shmaddr, int shmflg);

int shmdt(const void *shmaddr);

extern key_t ftok (const char *__pathname, int __proj_id)

萬事俱備,現在我們要來介紹一下如何在對應的代碼之中使用共用記憶體,主要涉及上述五個函數,我們通過一個簡單的 demo 來介紹這些函數:
int shmget(key_t key, size_t size, int shmflg)是申請共用記憶體的函數,這裡需要理解的是key這個參數,它本身是一個 int 類型,這個 key_t參數是通過key_t ftok (const char *__pathname, int __proj_id)產生的,這裡的pathname指的是一個固定的路徑,proj_id則表示對應項目的 id。所以在一個操作系統內,如何讓兩個不相關(沒有父子關係)的進程可以共用一個記憶體段呢?Bingo!就是通過這個 key_t類型讓所有的進程都唯一映射到對應記憶體空間,這裡就是通過對應的文件路徑項目 id來產生對應的key
所以說,在一個使用到共用記憶體的程式之中,需要程式設定一個文件路徑和一個項目的proj_id,來獲取系統之中確定一段共用記憶體的key。這裡需要註意的是ftok需要指定一個存在並且進程可以訪問的pathname路徑。因為 ftok使用的是指定文件的inode編號。所以,用了不同的文件名同樣可能得到相同的key,因為可以通過硬鏈接的方式讓不同的文件名指向相同 inode 編號文件。

    key_t shm_key;

    proj_id = 111;

    if ((shm_key = ftok("/home/happen", proj_id)) == -1) {
        exit(1);
    }

    shm_id = shmget(shm_key, sizeof(int), IPC_CREAT|IPC_EXCL|0600);
    if (shm_id < 0) {
        exit(1);
    }

ok,獲取了共用記憶體之後,我們需要將這部分共用記憶體的地址映射到當前進程的記憶體空間之上,需要藉助這個函數void *shmat(int shmid, const void *shmaddr, int shmflg)返回對應進程記憶體空間的指針,來對這部分記憶體進行操作。

   shm_p = (int *)shmat(shm_id, NULL, 0);
    if ((void *)shm_p == (void *)-1) {
        exit(1);
    }

這裡可以用過shmflg來設定對應記憶體空間的讀寫許可權,這裡我們取的是0,代表對應的空間有讀寫許可權。SHM_RDONLY可以設置為只讀許可權。之後我們就可以對對應的記憶體空間進行操作了:

    *shm_p = 100;

   
    if (shmdt(shm_p) < 0) {
        perror("shmdt()");
        exit(1);
    }

    if (shmctl(shm_id, IPC_RMID, NULL) < 0) {
        perror("shmctl()");
        exit(1);
    }

   return 0;

在使用完共用記憶體之後,需要使用int shmdt(const void *shmaddr)解除記憶體空間的映射,否則虛擬記憶體地址的泄漏,導致沒有可用地址可用。shmdt僅僅只是解除共用記憶體空間和進程地址的映射,而想要刪除一個共用記憶體需要使用int shmctl(int shmid, int cmd, struct shmid_ds *buf)函數進行處理同時也可以在命令行中使用第二小節的ipcrm命令來刪除指定的共用記憶體。在這裡必須強調的是,如果沒有顯式用shmctl或ipcrm命令刪除的話,那麼對應的共用記憶體將一直保留直到系統被關閉。

4.小結

到此為止,筆者展開聊了聊 Linux 共用記憶體的作用,並且對如何操作共用記憶體進行了介紹,同時希望大家能夠在實際開發工作之後能夠很好的掌握共用記憶體這個「利器」,讓開發工作事倍功半~~


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 本文以 ASP.NET Web API 為後臺框架,利用 EF6 連接 postgreSQL 資料庫,使用 swagger 來生成 REST APIs文檔。文章分二個部分,第一部分主要講如何用 EF6 連接 postgreSQL,第二部分是介紹如何集成 swagger,如何屏蔽 swagger 預設 ...
  • 希望現在學習Orleans不會晚,畢竟Server Fabric都開源了。本篇博客從Sample的HelloWorld示常式序來解讀Orleans的Grains。 Server配置參考 : https://dotnet.github.io/orleans/Documentation/clusters ...
  • public static void EncryptWithPassword(string unEncryptedWordPath, string password) { Word.Application wordApp = null; Word.Document document = null; ... ...
  • surging 是一個分散式微服務引擎,提供高性能RPC遠程服務調用,服務引擎支持http、TCP、WS、Mqtt協議,採用Zookeeper、Consul作為surging服務的註冊中心,集成了哈希一致性,隨機,輪詢、壓力最小優先作為負載均衡的演算法,底層協議集成採用的組件是dotnetty、web ...
  • 配置nfs NFS服務的主要配置文件為/etc/exports。/etc/exports文件內容格式: <輸出目錄> 客戶端(選項:訪問許可權,用戶映射,其他) 1、輸出目錄 輸出目錄是指NFS系統中所定義的共用給客戶端使用的文件系統 2、客戶端 客戶端是定義網路中可以訪問這個NFS共用目錄的IP地址 ...
  • 啟動 rewrite mod:sudo a2enmod rewrite;重啟Apache2:sudo /etc/init.d/apache2 restart。 ...
  • 1、下載maven http://maven.apache.org/download.cgi 2、解壓maven tar -zxvf apache-maven-3.6.0 3、配置maven環境變數 vi /etc/profile 添加環境變數 export MAVEN_HOME=/usr/loca ...
  • 不久前Elasticsearch發佈了最新安全公告, Elasticsearch Kibana 6.4.3之前版本和5.6.13之前版本中的Console插件存在嚴重的本地文件包含漏洞可導致拒絕服務攻擊、任意文件讀取攻擊、配合第三方應用反彈SHELL攻擊,下文筆者對其漏洞背景、攻擊原理和行為進行分析 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...