Linux POSIX IPC 共用記憶體

来源:http://www.cnblogs.com/xiaojiang1025/archive/2016/10/08/5937323.html
-Advertisement-
Play Games

模型 1. 創建/獲取共用記憶體fd :shm_open() 2. 創建者調整文件大小 :ftruncate() 3. 映射fd到記憶體 :mmap() 4. 去映射fd :munmap() 5. 刪除共用記憶體 :shm_unlink() 頭文件 shm_open oflag Access Mode: ...


模型

  1. 創建/獲取共用記憶體fd :shm_open()
  2. 創建者調整文件大小 :ftruncate()
  3. 映射fd到記憶體 :mmap()
  4. 去映射fd :munmap()
  5. 刪除共用記憶體 :shm_unlink()

頭文件

#include <unistd.h>         //for fstat()
#include <sys/types.h>      //for fstat()
#include <sys/mman.h>
#include <sys/stat.h> 
#include <fcntl.h>

shm_open

//創建/獲取共用記憶體的文件描述符,成功返迴文件描述符,失敗返回-1
//Link with -lrt.
int shm_open(const char *name, int oflag, mode_t mode);

oflag

  • Access Mode:
  • O_RDONLY以只讀的方式打開共用記憶體對象
  • O_RDWR以讀寫的方式打開共用記憶體對象
  • Opening-time flags(Bitwise Or):
  • O_CREAT 表示創建共用記憶體對象,剛被創建的對象會被初始化為0byte可以使用ftuncate()調整大小
  • O_EXCL用來確保共用記憶體對象被成功創建,如果對象已經存在,那麼返回錯誤
  • O_TRUNC表示如果共用記憶體對象已經存在那麼把它清空

mode: eg,0664 etc

ftruncate()

//調整fd指向文件的大小,成功返回0,失敗返回-1設errno
//VS truncate()
int ftruncate(int fd, off_t length);

如果原文件大小>指定大小,原文件中多餘的部分會被截除

int res=ftruncate(fd,3*sizeof(Emp));//要用sizeof,且是Emp(類型)不是emp(對象)
if(-1==res)
        perror("ftruncate"),exit(-1);

fstat()

//獲取文件狀態,成功返回0,失敗返回-1設errno
//VS stat()
int lstat(const char *pathname,     struct stat *buf);
int fstat(int fd, struct stat *buf);

buf:stat類型的指針

struct stat {
    dev_t   st_dev;                 /* ID of device containing file */
    ino_t   st_ino;                 /* inode number */
    mode_t  st_mode;                /* protection */        八進位            usigned int o%
    nlink_t st_nlink;               /* number of hard links */
    uid_t       st_uid;             /* user ID of owner */
    gid_t       st_gid;             /* group ID of owner */
    dev_t       st_rdev;            /* device ID (if special file) */
    off_t       st_size;            /* total size, in bytes */                      ld%
    blksize_t   st_blksize;         /* blocksize for filesystem I/O */
    blkcnt_t    st_blocks;          /* number of 512B blocks allocated */
    struct timespec st_atim;        /* time of last access */   
    struct timespec st_mtim;    /* time of last modification */     ld%,秒
    struct timespec st_ctim;    /* time of last status change */
};
//eg:
st_mode=100664      //100是文件類型
                    //664是許可權, 通過100664和0777BitwiseAND得到
st_mtime=1462787968 //秒

mmap()

//映射文件或設備到進程的虛擬記憶體空間,映射成功後對相應的文件或設備操作就相當於對記憶體的操作
//映射以頁為基本單位,文件大小, mmap的參數 len 都不能決定進程能訪問的大小, 而是容納文件被映射部分的最小頁面數決定傳統文件訪問
//要求對文件進行可讀可寫的的打開!!!
//成功返回映射區的指針,失敗返回-1設errno           
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);   //prot:protection, 許可權

addr:映射的起始地址, 如果為NULL則由kernel自行選擇->最合適的方法
length:映射的區域長度
prot:映射記憶體的保護許可權

  • PROT_EXEC表示映射的記憶體頁可執行
  • PROT_READ表示映射的記憶體可被讀
  • PROT_WRITE表示映射的記憶體可被寫
  • PROT_NONE表示映射的記憶體不可訪問

flags

must include one of :

  • MAP_SHARED表示共用這塊映射的記憶體,讀寫這塊記憶體相當於直接讀寫文件,這些操作對其他進程可見,由於OS對文件的讀寫都有緩存機制,所以實際上不會立即將更改寫入文件,除非帶哦用msync()或mumap()
  • MAP_PRIVATE表示創建一個私有的copy-on-write的映射, 更新映射區對其他映射到這個文件的進程是不可見的

can be Bitwise ORed:

  • MAP_32BIT把映射區的頭2GB個位元組映射到進程的地址空間,僅限域x86-64平臺的64位程式,在早期64位處理器平臺上,可以用來提高上下文切換的性能。當設置了MAP_FIXED時此選項自動被忽略
  • MAP_ANONYMOUS映射不會備份到任何文件,fd和offset參數都被忽略,通常和MAP_SHARED連用
  • MAP_DENYWRITEignored.
  • MAP_EXECUTABLEignored
  • MAP_FILE用來保持相容性,ignored
  • MAP_FIXED不要對addr參數進行處理確確實實的放在addr指向的地址,此時addr一定時頁大小的整數倍,
  • MAP_GROWSDOWN用在棧中,告訴VMM映射區應該向低地址擴展
  • MAP_HUGETLB (since Linux 2.6.32)用於分配"大頁"

fd: file decriptor
offset: 文件中的偏移量

void* pv=mmap(NULL,4,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,0,0);
if(MAP_FAILED==pv)
    perror("mmap"),exit(-1);

映射機制小解

  • mmap()就是建立一個指針,這個指針指向頁高速緩存的一頁,並假設這個頁有我們想要訪問的文件內容(此時都在虛擬地址空間),當然,這個頁描述符會自動的加入的調用進程的頁表中。當我們第一次使用這個指針,去訪問這個虛擬地址的頁時,發現這個頁還沒有分配物理頁框,沒有想要的文件,引起缺頁中斷,系統會把相應的(fd)文件內容放到高速緩存的物理頁框(此時才會有對物理地址空間的讀寫)
  • 映射過程中使用的文件相當於藥引子,因為所有進程都是可以通過VFS訪問磁碟文件的,所以這個文件相當於對映射記憶體的一個標識,有了這個位於磁碟的藥引子,很多進程都可以根據它找到同一個物理頁框,進而實現記憶體的共用,並不是說就在磁碟上讀寫
  • 頁高速緩存的內容不會立即寫到磁碟中,會等幾秒,這種機制可以提高效率並保護磁碟
  • 只要記憶體夠大,頁高速緩存的內容就會一直存在記憶體中,以後再有進程對該緩存頁的內容訪問的需求,就不需要從磁碟中搜索,直接訪問緩存頁(把這個頁加入到進程的頁表)就行
  • mmap()是系統調用,使用一次的開銷比較大,但比文件讀寫的read()/write()進行內核空間到用戶空間的數據複製要快,通常只有需要分配的記憶體>128KB(malloc一次分配33page就是128KB)的時候才會使用mmap()
  • /shm是一個特殊的文件系統,它不對應磁碟中的區域,而是記憶體中,所以使用mmap()在這個文件系統中創
  • /proc 不占用任何磁碟空間
  • linux採用的是頁式管理機制。對於用mmap()映射普通文件來說,進程會在自己的地址空間新增一塊空間,空間大小由mmap()的len參數指定,註意,進程並不一定能夠對全部新增空間都能進行有效訪問。進程能夠訪問的有效地址大小取決於文件被映射部分的大小。
  • 簡單的說,能夠容納文件被映射部分大小的最少頁面個數決定了進程從mmap()返回的地址開始,能夠有效訪問的地址空間大小。超過這個空間大小,內核會根據超過的嚴重程度返回發送不同的信號給進程。
  • 經過內核!=在內核空間和用戶空間來回切換!=在內核空間和用戶空間傳遞複製的數據
  • 頁是記憶體映射的基本單位, 可以理解為實際分配給物理記憶體的基本單位, 但不是數據操作的基本單位;
  • 頁機制是操作系統和CPU約定好的一種方式,OS按照頁給CPU按頁發虛擬地址,CPU按頁解析並處理
  • 操作系統(包括Linux)大量使用的緩存的兩個原理:
    • CPU訪問記憶體的速度遠遠大於訪問磁碟的速度(訪問速度差距不是一般的大,差好幾個數量級)
    • 數據一旦被訪問,就有可能在短期內再次被訪問(臨時局部原理)
  • 頁高速緩存(page cache)是個記憶體區域,是Linux 內核使用的主要磁碟高速緩存,在絕大多數情況下,內核在讀寫磁碟時都引用頁高速緩存,新頁被追加到頁高速緩存以滿足用戶態進程的讀請求,如果頁不在高速緩存中,新頁就被加到高速緩存中,然後就從磁碟讀出的數據填充它,如果記憶體有足夠的空閑空間,就讓該頁在高速緩存中長期保留,使其他進程再使用該頁時不再訪問磁碟, 即磁碟上的文件緩存到記憶體後,它的虛擬記憶體地址可以有多個,但是物理記憶體地址卻只能有一個
  • 我們要讀寫磁碟文件時,實質是對頁高速緩存進行讀寫,所以無論讀寫,都會首先檢查頁高速緩存有沒有這個文件對應的頁,如果有,就直接訪問,如果沒有,就引起缺頁中斷,給OS發信號,讓它把文件放到高速緩存再進行讀寫,這個過程不經過內核空間到用戶空間複製數據
  • OS中的頁機制,對應到硬體中可不一定在主存中,也可以是高速緩存etc,但不會是磁碟,因為磁碟文件的地址和記憶體不一樣,不是按照32位編址的,而是按照ext2 etc方式編址的,需要使用文件管理系統,在Linux中使用VFS和實際文件管理系統來管理文件,所以對於Linux,有兩個方式使用系統資源:VMM,VFS,前者用來管理絕大部分的記憶體,後者用來管理所有的文件和部分特殊文件系統(eg:/shm是記憶體的一塊區域)
  • page cache可以看作二者的橋梁,把磁碟文件放到高速緩存,就可以按照記憶體的使用方式使用磁碟的文件,使用完再釋放或寫回磁碟page cache中的頁可能是下麵的類型:
    • 含有普通文件數據的頁
    • 含有目錄的頁
    • 含有直接從塊設備(跳過文件系統層)讀出的數據的頁
    • 含有用戶態進程數據的頁,但頁中的數據已經被交換到硬碟
    • 屬於他書文件系統文件的頁
  • 映射:一個線性區可以和磁碟文件系統的普通文件的某一部分或者塊設備文件相關聯,這就意味著內核把對區線性中頁內某個位元組的訪問轉換成對文件中相應位元組的操作
  • TLB(Translation Lookaside Buffer)高速緩存用於加快線性地址的轉換,當一個線性地址第一次被使用時,通過慢速訪問RAM中的頁表計算出相應的物理地址,同時,物理地址被存放在TLB表項(TLB entry),以便以後對同一個線性地址的引用可以快速得到轉換
  • 在初始化階段,內核必須建立一個物理地址映射來指定哪些物理地址範圍對內核可用,哪些不可用
  • swap(記憶體交換空間)的功能是應付物理記憶體不足的情況下所造成的記憶體擴展記錄的功能,CPU所讀取的數據都來自於記憶體,那當記憶體不足的時候,為了讓後續的程式可以順序運行,因此在記憶體中暫不使用的程式和數據就會被挪到swap中,此時記憶體就會空出來給需要執行的程式載入,由於swap是使用硬碟來暫時放置記憶體中的信息,所以用到swap時,主機硬碟燈就會開始閃個不同
  • Q:CPU只能對記憶體進行讀寫,但又是怎麼讀寫硬碟的呢???A:把數據寫入page cache,再經由。。。寫入磁碟(包括swap) --《鳥哥》P10
  • 記憶體本身沒有計算能力,定址之類的都是CPU的事,只是為了簡便起見,我們通常畫成從記憶體地址A跳到記憶體地址B
  • OS是軟體的核心,CPU是執行的核心
    • 前者給後者髮指令我要乾什麼,CPU把他的指令變成現實
    • 二者必須很好的匹配電腦才能很好的工作
  • Linux內核中與文件Cache操作相關的API有很多,按其使用方式可以分成兩類:一類是以拷貝方式操作的相關介面, 如read/write/sendfile等,其中sendfile在2.6系列的內核中已經不再支持;另一類是以地址映射方式操作的相關介面,如mmap等。
    • 第一種類型的API在不同文件的Cache之間或者Cache與應用程式所提供的用戶空間buffer之間拷貝數據,其實現原理如圖7所示。
    • 第二種類型的API將Cache項映射到用戶空間,使得應用程式可以像使用記憶體指針一樣訪問文件,Memory map訪問Cache的方式在內核中是採用請求頁面機制實現的,首先,應用程式調用mmap(),陷入到內核中後調用do_mmap_pgoff。該函數從應用程式的地址空間中分配一段區域作為映射的記憶體地址,並使用一個VMA(vm_area_struct)結構代表該區域,之後就返回到應用程。當應用程式訪問mmap所返回的地址指針時(圖中4),由於虛實映射尚未建立,會觸發缺頁中斷。之後系統會調用缺頁中斷處理函數,在缺頁中斷處理函數中,內核通過相應區域的VMA結構判斷出該區域屬於文件映射,於是調用具體文件系統的介面讀入相應的Page Cache項,並填寫相應的虛實映射表。經過這些步驟之後,應用程式就可以正常訪問相應的記憶體區域了。

mumap()

//接觸文件或設備對記憶體的映射,成功返回0,失敗返回-1設errno
int munmap(void *addr, size_t length);
//關閉進程打開的共用記憶體對象,成功返回0,失敗返回-1
//Link with -lrt.
int shm_unlink(const char *name);

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

-Advertisement-
Play Games
更多相關文章
  • 蘋果在iOS10開放了siriKit介面給第三方應用。目前,QQ已經率先適配了Siri的發消息和打電話功能。這意味著在iOS10中你可以直接告訴Siri讓它幫你發QQ消息和打QQ電話了,聽起來是不是很酷炫? 那麼第三方應用使用Siri的體驗究竟如何?哪些應用可以接入SiriKit?接入SiriKi... ...
  • 原文: http://www.2cto.com/kf/201512/455888.html http://blog.csdn.net/yangqingqo/article/details/48371123 http://inthecheesefactory.com/blog/things-you-n ...
  • 遇到這個問題 網上找到的解決辦法: 方法一:就是上面說的通過計算出來ListView或者GridView中的子列高度和 進行顯示:public void setListViewHeightBasedOnChildren(ListView listView) { ListAdapter listAda ...
  • self.mManager = [[CMMotionManager alloc]init]; self.mManager.deviceMotionUpdateInterval = 0.5; if (self.mManager.gyroAvailable) { [self.mManager start ...
  • 在前面的文章中,已經實現了“設置中心”第一欄的功能以及佈局 本文地址:http://www.cnblogs.com/wuyudong/p/5936016.html,轉載請註明出處。 自定義屬性聲明 接下來實現其他欄的佈局和功能,由於它們之間的功能和佈局類似,只是屬性名稱不同。所以本文在自定義控制項的基 ...
  • TCP : Transmission Control Protocol,傳輸控制協議,類似於打電話 UDP : User Datagram Protocol,用戶數據報協議,類似於寫信 IP : Internet Protocol互聯網協議,是上述兩種協議的底層協議 IP地址(IP Address) ...
  • 模型: 1. 初始化並打開有名信號量:sem_open() 創建/獲得無名信號量:sem_init() 操作信號量:sem_wait()/sem_trywait()/sem_timedwait()/sem_post()/sem_getvalue() 退出有名信號量:sem_close() 銷毀有名信 ...
  • 模型: 1. 創建/獲取消息隊列fd :mq_open() 設置/獲取消息隊列屬性 :mq_get() 發送/接收消息 :mq_send()/mq_receive() 脫接消息隊列 :mq_close() 刪除消息隊列 :mq_unlink() 頭文件 POSIX mq VS Sys V mq的優勢 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...