1. 程式人生 > >IPC之Posix共享記憶體詳解

IPC之Posix共享記憶體詳解

1.概念

共享記憶體區,按標準可分為Posix共享記憶體區和System V共享記憶體區,兩者在概念上類似。

Posix 表示可移植作業系統介面(Portable Operating System Interface ,縮寫為 POSIX ),POSIX標準定義了作業系統應該為應用程式提供的介面標準,是IEEE為要在各種UNIX作業系統上執行的軟體而定義的一系列API標準的總稱,其正式稱呼為IEEE 1003,而國際標準名稱為ISO/IEC 9945。

System V,曾經也被稱為 AT&T System V,是Unix作業系統眾多版本中的一支。它最初由 AT&T 開發,在1983年第一次釋出。一共發行了4個 System V 的主要版本:版本1、2、3 和 4。

2.Posix.1提供了兩種在無親緣關係程序間共享記憶體區的方法

a)記憶體對映檔案(memory-mapped file),由open函式開啟,由mmap函式把所得到的描述符對映到當前程序空間地址中的一個檔案。

b)共享記憶體區物件(shared-memory object),由shm_open函式開啟一個Posix.1 IPC名字,所返回的描述符由mmap函式對映到當前程序的地址空間。


這兩種共享記憶體區的區別在於共享的資料的載體(底層支撐物件)不一樣:

記憶體對映檔案的資料載體是物理檔案。
共享記憶體區物件,也就是共享的資料載體是實體記憶體。

3.我們經常說的共享記憶體,一般是指共享記憶體區物件,也就是共享實體記憶體。

4.關鍵函式說明

Posix共享記憶體區涉及以下兩個步驟要求。

(1)指定一個名字引數呼叫shm_open,以建立一個新的共享記憶體區物件或開啟一個已存在的共享記憶體區物件。

(2)呼叫mmap把這個共享記憶體區對映到呼叫程序的地址空間。

傳遞給shm_open函式的名字引數隨後由希望共享該記憶體區的任何其他程序使用。

SHM_OPEN(3)                Linux Programmer's Manual               SHM_OPEN(3)

NAME
       shm_open,  shm_unlink  -  create/open  or  unlink  POSIX  shared memory
       objects

SYNOPSIS
       #include <sys/mman.h>
       #include <sys/stat.h>        /* For mode constants */
       #include <fcntl.h>           /* For O_* constants */

       int shm_open(const char *name, int oflag, mode_t mode);

       Link with -lrt.
返回:若成功則非負描述符,若出錯則為-1

oflag引數與open函式的flags一樣,必須含有O_RDONLY或O_RDWR標準。

mode引數與open函式的mode一樣,是指定許可權位。如果沒有指定O_CREAT標誌,那麼該引數可以指定為0。

shm_unlink函式刪除一個共享記憶體區物件的名字,刪除一個名字不會影響對於其底層支撐物件的現有引用,直到對於該物件的引用全部關閉為止。

SHM_OPEN(3)                Linux Programmer's Manual               SHM_OPEN(3)

NAME
       shm_open,  shm_unlink  -  create/open  or  unlink  POSIX  shared memory
       objects

SYNOPSIS
       #include <sys/mman.h>
       #include <sys/stat.h>        /* For mode constants */
       #include <fcntl.h>           /* For O_* constants */

       int shm_unlink(const char *name);

       Link with -lrt.
返回:若成功則為0,若出錯則為-1

處理mmap的時候,普通檔案或共享記憶體區物件的大小都可以通過呼叫ftruncate函式修改。

TRUNCATE(2)                Linux Programmer's Manual               TRUNCATE(2)

NAME
       truncate, ftruncate - truncate a file to a specified length

SYNOPSIS
       #include <unistd.h>
       #include <sys/types.h>

       int truncate(const char *path, off_t length);
       int ftruncate(int fd, off_t length);
返回:若成功則為0,若出錯則為-1

我們呼叫ftruncate來指定新建立的共享記憶體區物件的大小,或者修改已存在的物件的大小。當開啟一個已存在的共享記憶體區物件時,我們可呼叫fstat來獲取有關該物件的資訊。
STAT(2)                    Linux Programmer's Manual                   STAT(2)

NAME
       stat, fstat, lstat - get file status

SYNOPSIS
       #include <sys/types.h>
       #include <sys/stat.h>
       #include <unistd.h>

       int fstat(int fd, struct stat *buf);
返回:若成功則為0,若出錯則為-1

stat結構有12個或以上的成員,然而當fd指代一個記憶體共享區物件時,只有四個成員含有資訊。

struct stat {
  mode_t    st_mode;    /* protection */
  uid_t     st_uid;     /* user ID of owner */
  gid_t     st_gid;     /* group ID of owner */
  off_t     st_size;    /* total size, in bytes */
};

/* mmap函式把一個檔案或者一個Posix共享記憶體區物件對映至呼叫程序的地址空間 */
MMAP(2)                    Linux Programmer's Manual                   MMAP(2)  
  
NAME  
       mmap, munmap - map or unmap files or devices into memory  
  
SYNOPSIS  
       #include <sys/mman.h>  
  
       void *mmap(void *addr, size_t length, int prot, int flags,  
                  int fd, off_t offset);  
  
       See NOTES for information on feature test macro requirements.
返回:若成功則為被對映區的起始地址,若出錯則為MAP_FAILED

其中addr可以指定描述符fd應被對映到程序內空間的起始地址,它通常被指定為一個空指標,這樣告訴核心自己去選擇起始地址,無論哪種情況下,該函式的返回值都是描述符fd所對映到記憶體區的其實地址。這裡需要注意的是,檔案需要初始化長度,否則對記憶體操作時會產生SIGBUS資訊(硬體錯誤)。

length是對映到呼叫程序地址空間中位元組數,它從被對映檔案開頭offset個位元組出開始算。offset通常設定為0。

記憶體對映區的保護由port引數指定,通常設定為PROT_READ | PROT_WRITE(可讀與可寫):

PORT_READ    -> 可讀

PORT_WRITE  -> 可寫

PORT_EXEC    -> 可執行

PORT_NONE   -> 資料不可訪問

flags用於設定記憶體對映區的資料被修改時,是否改變其底層支撐物件(這裡的物件是檔案),MAP_SHAREDMAP_PRIVATE必須指定一個

MAP_SHARED  -> 變動是共享的

MAP_PRIVATE  -> 變動是私自的

MAP_FIXED        -> 準確的解析addr引數

舉例:當flags設定為MAP_SHARED時,在記憶體中對檔案的修改會同步到物理檔案中,可通過less檢視

flags設定為MAP_PRIVATE時,在記憶體中對檔案的修改不會同步到物理檔案中,可通過less檢視

mmap成功返回後,fd引數可以關閉。該操作對由於mmap建立的對映關係沒有影響。

/* 從某個程序空間刪除一個對映關係 */

MMAP(2)                    Linux Programmer's Manual                   MMAP(2)  
  
NAME  
       mmap, munmap - map or unmap files or devices into memory  
  
SYNOPSIS  
       #include <sys/mman.h>  
  
       int munmap(void *addr, size_t length);  
  
       See NOTES for information on feature test macro requirements.  
返回:若成功則為0,若出錯則為-1

其中addr引數是由mmap返回的地址,len是對映區的大小。再次訪問這些地址將導致向呼叫程序產生一個SIGSEGV訊號。

5.特別提醒
共享記憶體並未提供同步機制,也就是說,在第一個程序結束對共享記憶體的寫操作之前,並無自動機制可以阻止第二個程序開始對它進行讀寫。所以我們通常需要同步機制控制對共享記憶體的訪問,比如訊號量

例子1,建立修改輸出刪除共享記憶體區。

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <errno.h>
#include <stdlib.h>

#define MAXSIZE 1024*1024*16   /*共享記憶體的大小,建議設定成記憶體頁的整數倍*/
#define FILENAME "shm.test"

int main()
{
    /* 建立共享物件,可以檢視/dev/shm目錄 */
    int fd = shm_open(FILENAME, O_CREAT | O_TRUNC | O_RDWR, 0777);
    if (fd == -1) {
        perror("open failed:");
        exit(1);
    }

    /* 調整大小 */
    if (ftruncate(fd, MAXSIZE) == -1) {
        perror("ftruncate failed:");
        exit(1);
    }

    /* 獲取屬性 */
    struct stat buf;
    if (fstat(fd, &buf) == -1) {
        perror("fstat failed:");
        exit(1);
    }
    printf("the shm object size is %ld\n", buf.st_size);

    sleep(30);

    /* 如果引用計數為0,系統釋放記憶體物件 */
    if (shm_unlink(FILENAME) == -1) {
        perror("shm_unlink failed:");
        exit(1);
    }
    printf("shm_unlink %s success\n", FILENAME);

    return 0;
}

編譯執行:


例子2,一個程序寫,一個程序讀:

寫程序:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>

#define MAXSIZE 1024*4   /*共享記憶體的大小,建議設定成記憶體頁的整數倍*/
#define FILENAME "shm.test"

int main()
{
    /* 建立共享物件,可以檢視/dev/shm目錄 */
    int fd = shm_open(FILENAME, O_CREAT | O_TRUNC | O_RDWR, 0777);
    if (fd == -1) {
        perror("open failed:");
        exit(1);
    }

    /* 調整大小 */
    if (ftruncate(fd, MAXSIZE) == -1) {
        perror("ftruncate failed:");
        exit(1);
    }

    /* 獲取屬性 */
    struct stat buf;
    if (fstat(fd, &buf) == -1) {
        perror("fstat failed:");
        exit(1);
    }
    printf("the shm object size is %ld\n", buf.st_size);

    /* 建立對映關係 */
    char *ptr = (char*)mmap(NULL, MAXSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (ptr == MAP_FAILED) {
        perror("mmap failed:");
        exit(1);
    }
    printf("mmap %s success\n", FILENAME);
    close(fd); /* 關閉套接字 */

    /* 寫入資料 */
    char *content = "hello world";
    strncpy(ptr, content, strlen(content));

    sleep(30);

    return 0;
}

讀程序:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>

#define FILENAME "shm.test"

int main()
{
    /* 建立共享物件,可以檢視/dev/shm目錄 */
    int fd = shm_open(FILENAME, O_RDONLY, 0);
    if (fd == -1) {
        perror("open failed:");
        exit(1);
    }

    /* 獲取屬性 */
    struct stat buf;
    if (fstat(fd, &buf) == -1) {
        perror("fstat failed:");
        exit(1);
    }
    printf("the shm object size is %ld\n", buf.st_size);

    /* 建立對映關係 */
    char *ptr = (char*)mmap(NULL, buf.st_size, PROT_READ, MAP_SHARED, fd, 0);
    if (ptr == MAP_FAILED) {
        perror("mmap failed:");
        exit(1);
    }
    printf("mmap %s success\n", FILENAME);
    close(fd); /* 關閉套接字 */

    printf("the read msg is:%s\n", ptr);

    sleep(30);

    return 0;
}

由於只有一個程序寫,而且寫程序先執行,所以不需要進行同步。

編譯執行:


參考:《unix網路程式設計》·卷2

End;

相關推薦

IPCPosix共享記憶體

1.概念 共享記憶體區,按標準可分為Posix共享記憶體區和System V共享記憶體區,兩者在概念上類似。 Posix 表示可移植作業系統介面(Portable Operating System Interface ,縮寫為 POSIX ),POSIX標準定義了作業系統

IPCPosix訊息佇列

基本概念:     訊息佇列可認為是一個訊息連結串列。有足夠寫許可權的執行緒可往佇列中放置訊息,有足夠讀許可權的執行緒可從佇列中取走訊息,每個訊息都是一個記錄(非位元組流式,也就是不需要自定義邊界),它由傳送者賦予一個優先順序。在某個程序往一個佇列寫入訊息之前,並不需要另

【Linux】程序間通訊(IPC共享記憶體與測試用例

學習環境centos6.5 Linux核心2.6 什麼是共享記憶體 共享記憶體允許兩個或更多程序訪問同一塊記憶體。當一個程序改變了這塊記憶體中的內容的的時候,其他程序都會察覺到這個更改。 效率: 因為所有程序共享同一塊記憶體,共享記憶體在各種程序

IPC通訊:Posix共享記憶體

http://www.cnblogs.com/polestar/archive/2012/04/23/2466003.html  共享記憶體區是最快的可用IPC形式。它允許多個不相關的程序去訪問同一部分邏輯記憶體。如果需要在兩個執行中的程序之間傳輸資料,共享記憶體將是一

Linux程序間通訊POSIX共享記憶體

共享記憶體是最高效的IPC機制,因為它不涉及程序之間的任何資料傳輸。這種高效率帶來的問題是,我們必須用其他輔助手段來同步程序對共享記憶體的訪問,否則會產生競態條件。因此,共享記憶體通常和其他程序間通訊方式一起使用。 Linux下有三種共享記憶體的IPC技術:S

IPC通訊:Posix共享記憶體1

 共享記憶體區是最快的可用IPC形式。它允許多個不相關的程序去訪問同一部分邏輯記憶體。如果需要在兩個執行中的程序之間傳輸資料,共享記憶體將是一種效率極高的解決方案。一旦這樣的記憶體區對映到共享它的程序的地址空間,這些程序間資料的傳輸就不再涉及核心。這樣就可以減少系統呼叫

IPCPosix共享記憶體區與mmap記憶體對映

共享記憶體是一種IPC形式,與其它IPC機制如管道、訊息佇列等相比,資料不必在程序與核心間多次交換,程序間通訊的速度更快。當共享記憶體區對映到共享它的程序的地址空間時,再加以一些同步控制,這些程序就可以進行資料傳送了。mmap函式提供了記憶體對映功能,可以把一個

linux網路程式設計POSIX 共享記憶體和 系列函式

在前面介紹了system v 共享記憶體的相關知識,現在來稍微看看posix 共享記憶體 和系列函式。 共享記憶體簡單來說就是一塊真正的實體記憶體區域,可以使用一些函式將這塊區域對映到程序的地址空間進行讀寫,而posix 共享記憶體與system v 共享記憶體不同的是它是

linux進程間通信Posix共享內存用法及代碼舉例

函數 ini 復制代碼 define 進程 a.out IV 使用 init Posix共享內存有兩種非親緣進程間的共享內存方法:1). 使用內存映射文件,由open函數打開,再由mmap函數把返回的文件描述符映射到當前進程空間中的一個文件。2). 使用共享內存區對象,由

Oracle記憶體三 Shared pool 共享

一. Shared Pool 概述             在之前的blog對Oracle 的記憶體架構也做了一個概述,參考:             在網上搜到一篇介紹shared pool 非常詳細的pdf資料。 原文連結以找不到,但還是要感謝作者Kamus的辛勤

POSIX 執行緒(1)——一種支援記憶體共享的簡捷工具

級別: 初級 2000 年 7 月 01 日 POSIX(可移植作業系統介面)執行緒是提高程式碼響應和效能的有力手段。在本系列中,Daniel Robbins 向您精確地展示在程式設計中如何使用執行緒。其中還涉及大量幕後細節,讀完本系列文章,您完全可以運用 POSIX 執

POSIX 執行緒 一種支援記憶體共享的簡捷工具

執行緒是有趣的 瞭解如何正確運用執行緒是每一個優秀程式設計師必備的素質。執行緒類似於程序。如同程序,執行緒由核心按時間分片進行管理。在單處理器系統中,核心使用時間分片來模擬執行緒的併發執行,這種方式和程序的相同。而在多處理器系統中,如同多個程序,執行緒實際上一樣可以併

Oracle記憶體四 Buffer Cache 資料緩衝區

一. 官網說明 Memory Architecture              The database buffer cache is the portion of the SGA that holds copies of data blocks read from

Oracle記憶體 Library cache 庫緩衝

Oracle記憶體詳解之 Library cache 庫緩衝 2017年11月09日 11:38:39 閱讀數:410更多 個人分類: 體系結構 Library cache是Shared pool的一部分,它幾乎是Oracle記憶體結構中最複雜的一部分,主要存放shared curo

Oracle記憶體二 Library cache 庫緩衝

Library cache是Shared pool的一部分,它幾乎是Oracle記憶體結構中最複雜的一部分,主要存放shared curosr(SQL)和PLSQL物件(function,procedure,trigger)的資訊,以及這些物件所依賴的table,inde

java叢集redis sentinel、nginx、session共享配置

說明 1、看此篇文章之前,你需要對spring 基礎,redis等相關技術基礎. 2、redis 服務端使用2主2從,分佈交叉部署至2臺伺服器,且分佈部署2臺tomcat web應用,兩臺伺服器分別部署2臺nginx來實現負載均衡。 3、你需要對叢集的概念有所

跨域資源共享CORS

附加 accep 不發送 地址 code 克服 通信 數據 ror 簡介 CORS是一個W3C標準,全稱是"跨域資源共享"(Cross-origin resource sharing)。 它允許瀏覽器向跨源服務器,發出XMLHttpRequest請求,從而克服了AJAX

MVCAjax.BeginForm使用更新列表

分布 use html text col 返回 uno pts scripts 1.首先,請在配置文件設置如下:(該項默認都存在且為true) <add key="UnobtrusiveJavaScriptEnabled" value="true" /> 2

Android應用開發所有動畫使用

factory 技術分享 resource bsp phi 顯示 程序 恢復 分享 題外話:有段時間沒有更新博客了,這篇文章也是之前寫了一半一直放在草稿箱,今天抽空把剩余的補上的。消失的這段時間真的好忙,節奏一下子有些適應不過來,早晨七點四十就得醒來,晚上九點四十才準備下班

mysqlgroup_concat函數

sel style 舉例 back spa -1 逗號 src asc 函數語法: group_concat([DISTINCT] 要連接的字段 [ORDER BY 排序字段 ASC/DESC] [SEPARATOR ‘分隔符‘]) 下面舉例說明: select * fro