Unix Linux的記憶體對映

2021-06-27 12:51:44 字數 4501 閱讀 6415

共享記憶體可以說是最有用的程序間通訊方式,也是最快的ipc形式;兩個不同的程序a和b共享記憶體的意思就是:同一塊物理記憶體即被對映到程序a的位址空間中又內對映到程序b的位址空間中.程序a可以實時地看到程序b對共享記憶體中資料的更新,反之,程序b也可以實時地看到程序a對共享記憶體的更新;由於多個程序同時訪問同一塊共享記憶體區域,那就需要某種同步機制來保證多個不同程序對共享記憶體的訪問,互斥鎖、訊號量/訊號燈、訊號量集都可以;

採用共享記憶體來實現程序間通訊的乙個很明顯的好處就是:程序可以直接讀寫記憶體,基本上不需要任何額外的資料拷貝.而對於像管道、訊息佇列之類的ipc方式,則需要在核心空間和使用者空間之間進行四次資料拷貝,而共享記憶體則只需要兩次拷貝:一次是從輸入檔案拷貝到共享記憶體區,另外一次是從共享記憶體區拷貝到輸出檔案中.實際上,程序之間在共享記憶體時,並不總是讀寫少量資料後就解除對映,有新的通訊時,再重新建立共享記憶體區域,而是保持共享區域,直到通訊完畢為止;這樣,資料內容一直儲存在共享記憶體中,並沒有寫回檔案.共享記憶體中的資料內容往往是在解除對映時才寫回檔案的.因此,採用共享記憶體的通訊方式是非常有效的;

linux的2.2.x以後的核心版本支援多種共享記憶體方式,比如:記憶體對映mmap、posix共享記憶體、system v共享記憶體;

一、核心怎樣保證各個程序定址到同一塊共享記憶體區域的記憶體頁面:

:對於對映普通檔案(非共享對映)的情況,缺頁處理程式首先會在page cache中根據struct address_space和偏移量尋找相應頁面.如果沒找到,則說明檔案資料還沒有讀入記憶體,處理程式會從磁碟讀入相應的檔案頁面,並返回相應的頁面位址,同時,程序頁表也會被更新;

換句話說,對於共享記憶體來說,缺頁處理程式是在swap cache和swap area中尋找相應的頁面,而對於非共享對映(對映普通檔案)來說,則是在page cache來尋找對應的頁面;

5、所有程序在對映同一塊共享記憶體區域時,情況都一樣,在建立線性位址與實體地址之間的對映之後,不論程序各自返回的位址如何,實際上訪問的必然都是同一塊共享記憶體區域對應的物理頁面.

注意:一塊共享區域可以看作是特殊檔案系統shm中的乙個特殊檔案,shm的安裝點在交換分割槽上;

二、記憶體對映:

實際上,記憶體對映機制並不是完全為了共享記憶體的目的而設計的,它本身提供了不同於一般普通檔案的訪問方式,程序可以像訪問記憶體一樣對普通檔案程序操作.而posix或system v共享記憶體ipc則純粹是用於共享記憶體的目的.當然記憶體對映實現共享記憶體,也是記憶體對映的應用之一;

記憶體對映機制的用途:a、以訪問記憶體的方式讀寫檔案; b、實現共享記憶體;

三、mmap()系統呼叫:

mmap()系統呼叫使得程序之間通過對映同乙個普通檔案而實現共享記憶體的目的.普通檔案被對映到程序的位址空間之後,程序就可以像訪問普通記憶體一樣對檔案進行訪問,不必再呼叫read()、write()等系統呼叫操作.

mmap()系統呼叫介紹:

void* mmap(void* addr, size_t len, int prot, int flags, int fd, off_t offset);

該函式在程序的位址空間與檔案物件或共享記憶體物件之間建立一種對映關係;

addr  :該引數指定檔案應該被對映到程序位址空間的起始位址,一般被指定為乙個空指標,此時,程式把選擇起始位址的任務留給核心來完成了.這個位址是程序位址空間中需要對映到檔案中的記憶體區域的首位址;也就是說,在程序位址空間中用於檔案對映的記憶體區域的首位址;

len   :檔案被對映到呼叫程序的位址空間中的位元組數,它從被對映檔案開頭offset個位元組處開始算起,取len個位元組,把檔案中的這len個位元組的檔案空間對映到程序的位址空間中;

port  :指定檔案被對映到記憶體中之後的訪問許可權.可取的值有:port_read(可讀)、port_write(可寫)、port_exec(可執行)、port_none(不可訪問);

flags :對映標記;取值如下:map_shared、map_private、map_fixed,其中,map_shared和map_private必選其一,而map_fixed則不推薦使用;

fd    :即將被對映到程序位址空間中的檔案的描述符.一般由系統呼叫open()返回;同時,fd可以指定為-1,此時,必須指定flags引數中的map_anon,表明程序的是匿名對映(不涉及具體的檔名,避免了檔案的建立及開啟,很顯然,只能用於具有親屬關係的程序之間的通訊).

offset:從檔案開頭計算offset個位元組處開始對映;也就是,檔案中需要被對映的檔案內容的起始位址,這個起始位址的計算是以檔案開頭為參照的;這個引數一般取值為0,表示從檔案開頭處開始對映;

返回值:檔案最終對映到程序位址空間中的起始位址;程序可直接以該位址為有效的起始位址進行操作;也就是檔案中開始對映的起始字節點到程序中對應對映記憶體區的起始位址點處的乙個對映;換句話就是說,在程序位址空間中用於檔案對映的記憶體區域的首位址;

四、系統呼叫mmap()用於共享記憶體的兩種方式:

a、使用普通檔案提供的記憶體對映/共享記憶體:適用於任何程序之間;此時,需要使用系統呼叫open()事先開啟或建立乙個檔案,然後再呼叫mmap():

fd = open(filename, flag, mode);

......

ptr = mmap(null, len, port_read|port_write, map_shared, fd, 0);

使用特殊檔案提供匿名記憶體對映:適用於具有親屬關係的程序之間;由於父子程序之間的這種特殊的父子關係,在父程序中先呼叫mmap(),然後呼叫fork(),那麼,在呼叫fork()之後,子程序繼承了父程序的所有資源,當然也包括匿名對映後的位址空間和mmap()返回的位址,這樣,父子程序就可以通過對映區域進行通訊了;

注意:這裡不是一般的繼承關係.一般來說,子程序單獨維護從父程序繼承下來的一些變數,而mmap()返回的位址卻是由父子程序共同維護的;對於具有親屬關係的程序之間實現共享記憶體的最好方式應該是採用匿名對映的方式.此時,不必指定具體的條件,只要設定相應的標誌即可.

五、解除記憶體對映關係:

當程序間通訊結束時,需要解除檔案頁面空間到程序位址空間之間的對映關係;也就說,程序通訊結束時,需要把掛載到程序位址空間上的檔案解除安裝下來;這個任務由系統呼叫munmap();

int munmap(void* addr, size_t len);

該系統呼叫用於在程序位址空間中結束對映關係;

addr:是呼叫mmap()返回的程序位址空間中用於檔案對映的記憶體區域的首位址;

len :程序位址空間中對映區域的大小,單位:位元組;

當對映關係解除之後,對原來對映位址的訪問將導致段錯誤發生;

返回值: -1:失敗; 0:成功;

六、記憶體對映的同步:

一般來說,程序在對映空間中對共享內容的修改並不會直接寫回到磁碟檔案中,往往在呼叫munmap()之後才會同步輸出到磁碟檔案中.那麼,在程式執行過程中,在呼叫munmap()之前,可以通過呼叫msync()來實現磁碟上檔案內容與共享記憶體區中的內容與一致;或者是把對共享記憶體區的修改同步輸出到磁碟檔案中;

注意:

1、最終被對映檔案內容的長度不會超過檔案本身的初始大小,即:記憶體對映操作不能改變檔案的大小;

2、可以用於程序間通訊的得有效位址空間大小大體上受限於被對映檔案的大小,但是並不完全受限於檔案大小.

在linux中,記憶體的保護機制是以記憶體頁為單位的,即使被對映的檔案只有乙個位元組的大小,核心也會為這個檔案的對映分配乙個頁面大小的記憶體空間.當被對映檔案的大小小於乙個頁面大小時,程序可以對mmap()返回位址開始的乙個頁面大小進行訪問,而不會出錯;但是,如果對乙個頁面之外的位址空間進行訪問,則導致錯誤發生.因此,可用於程序間通訊的有效位址空間的大小不會超過被對映檔案大小與乙個頁面大小的和;

3、檔案一旦被對映之後,呼叫mmap()的程序對返回位址空間的訪問就是對某一記憶體區域進行訪問,暫時脫離了磁碟上檔案的影響.所有對mmap()返回位址空間的操作只在記憶體範圍內有意義,只有在呼叫了munmap()或msync()之後,才會把對映記憶體中的相應內容寫回到磁碟檔案中,所寫內容的大小仍然不會超過被對映檔案的大小;

七、對mmap()返回的位址空間的訪問:

linux採用的是頁式管理機制.對於用mmap()對映普通檔案來說,程序會在自己的位址空間中新增加一塊空間,空間的大小由mmap()的len引數指定,注意:程序並不一定能夠對新增加的全部空間都進行有效的訪問.程序能夠訪問的有效位址空間的大小取決於檔案中被對映部分的大小.簡單地說,能夠容納檔案中被對映部分大小的最少頁面個數決定了程序從mmap()返回的位址開始,能夠訪問的有效位址空間大小.超過這個空間大小,核心會根據超過的嚴重程度返回傳送不同的訊號給程序.

注意:決定程序能夠訪問的有效位址空間大小的因素是檔案中被對映的部分,而不是整個檔案;另外,如果指定了檔案的偏移部分,一定要注意為頁面大小的整數倍;

總之:採用記憶體對映機制mmap()來實現程序間通訊是很方便的,在應用層上,呼叫介面非常簡單,內部實現機制涉及到了linux的儲存管理以及檔案系統等方面的內用;

檢視Unix Linux程序記憶體分布

摘自 sudo gdb p 1 gdb info process 1 start addr end addr size offset objfile 0x400000 0x401000 0x1000 0x0 usr bin runit 0x401000 0x480000 0x7f000 0x1000...

Unix Linux程序在記憶體中的布局

對於linux作業系統之上的程式而言,其執行的程序所使用的記憶體位址都是虛擬位址,是mmu經過對映後的位址,我們這裡所談及的記憶體也是虛擬記憶體,而不是物理記憶體。我們將編寫好的程式經過gcc編譯得到乙個可執行的檔案,然後將其執行起來,通過檢視程序的命令得到程序id ps aux 在得到程序id之後...

記憶體對映的用法

記憶體對映的作用之一是可以在程序之間進行通訊,鑑於自己當時搜尋資料時的痛苦,以及有很多雖然知道有這幾個函式,但是不知道怎麼結合用,下面我列出了乙個應用的例子 sdata是乙個結構體,建立記憶體對映 int ilengbyte sizeof sdata lpbyte lpdata if hmap nu...