天天看點

Posix共享記憶體區

  共享記憶體方式

Posix共享記憶體區

  最快的ipc形式,這樣的記憶體區域映射到共享它的程序的位址空間,這些程序的資料傳輸就不再涉及核心(程序不再通過任何進入核心的系統調用來彼此傳遞資料,核心必須允許各個程序共享記憶體區域的記憶體映射關系然後一直處理該記憶體區域),但是在共享記憶體中存放或讀取資訊需要程序間的同步方式。

  客戶——伺服器互動資訊的步驟

Posix共享記憶體區

  使用共享記憶體方式:(共享記憶體區對象同時出現在客戶和伺服器的位址空間中)

伺服器使用一個信号量來擷取某個共享記憶體對象的權利

伺服器将資料從輸入檔案讀到共享記憶體區對象,read的第二個參數指定的資料緩沖區位址指向這個共享記憶體區對象

服務區器讀入完畢使用一個信号量通知客戶

客戶将這些資料從該共享記憶體區對象寫到輸出檔案中

Posix共享記憶體區

  具有至少随核心持續特性

  由open函數打開,由mmap函數把得到的描述符映射到調用程序位址空間的檔案。

  mmap函數把一個檔案或posix共享記憶體區對象映射到調用程序的位址空間。使得程序之間通過映射同一個普通檔案實作共享記憶體。使用該函數的三個目的:

使用普通檔案以提供記憶體映射i/o

使用特殊檔案以提供匿名記憶體映射

使用shm_open以提供無親緣關系間的程序posix共享記憶體區

  記憶體映射檔案(普通檔案):在open之後調用mmap把他映射到調用程序位址空間的某個檔案。使用記憶體映射檔案的一個特性是:所有的i/o都在核心的掩蓋下完成,我們隻需編寫存取記憶體映射區中各個值的代碼(就是不調用read和write執行i/o時那樣有核心直接參與i/o的完成,而是有核心在背後通過操縱頁表等方式間接參與,這樣使用者程序看來i/o不再涉及系統調用),絕不再使用read,write,lseek。但是不是所有的檔案都能進行記憶體映射的(終端或套接字映射到記憶體嗎就會導緻mmap傳回一個錯誤,這些類型的描述符必須使用read和write)。

  在無親緣關系的程序間共享記憶體區(特殊檔案匿名映射):所映射檔案的實際内容成了被共享記憶體區的初始内容,而且這些進城對該共享記憶體區所做的任何變動都複制會映射檔案(提供随檔案系統的持續性,要使用map_shared)。

  不能對終端或套接字描述符進行mmap,這類描述符必須使用read、write來通路。

addr:指定描述fd被映射到的程序内空間的起始位址,通常為空指針,讓核心自己去選擇起始位址,無論哪種情況下傳回值都是描述符fd所映射到記憶體的起始位址

fd:有效的檔案描述詞。一般是由open()函數傳回,其值也可以設定為-1,此時需要指定flags參數中的map_anon,表明進行的是匿名映射。

len:映射到調用程序位址空間的位元組數,他被映射檔案開頭起第offset個位元組處開始算,offest通常為0

port:記憶體映射保護;prot_read:資料可讀;prot_weite:資料可寫;prot_exec:資料可執行;prot_none:資料不可通路

flags:

    1>map_private:變動自私的,調用程序對被映射資料所修改隻對改程序可見,而不改變其底層支援對象(或是一個檔案對象或是一個共享記憶體區對象);

    2>map_shared:變動共享的,調用程序對被映射資料所修改對于共享該對象所有的程序可見,改變其底層支援對象(或是一個檔案對象或是一個共享記憶體區對象)以上這二者必須指定一個;  

    3>map_fixed:從移植方面來講不應該指定該标志,但addr不是一個空指針,那麼addr如何處置取決于實作,不為空的addr值通常被當作有關該記憶體區應是如何具體定位的線索,可移植性應該把addr指定一個空指針而且不指定該标志,使用指定的映射起始位址,如果由start和len參數指定的記憶體區重疊于現存的映射空間,重疊部分将會被丢棄。如果指定的起始位址不可用,操作将會失敗。并且起始位址必須落在頁的邊界上。

    4> map_noreserve :不要為這個映射保留交換空間。當交換空間被保留,對映射區修改的可能會得到保證。當交換空間不被保留,同時記憶體不足,對映射區的修改會引起段違例信号。

       5>map_locked:鎖定映射區的頁面,進而防止頁面被交換出記憶體。

       6>map_growsdown:用于堆棧,告訴核心vm系統,映射區可以向下擴充。

          7>map_anonymous:匿名映射,映射區不與任何檔案關聯。

       8>map_anon:map_anonymous的别稱,不再被使用。

       9>map_file:相容标志,被忽略。

         10>map_32bit:将映射區放在程序位址空間的低2gb,map_fixed指定時會被忽略。目前這個标志隻在x86-64平台上得到支援。

       11>map_populate:為檔案映射通過預讀的方式準備好頁表。随後對映射區的通路不會被頁違例阻塞

    12>map_nonblock:僅和map_populate一起使用時才有意義。不執行預讀,隻為已存在于記憶體中的頁面建立頁表入口。

函數傳回值,errno被設為以下的某個值

  eacces:通路出錯

  eagain:檔案已被鎖定,或者太多的記憶體已被鎖定

  ebadf:fd不是有效的檔案描述詞

  einval:一個或者多個參數無效

  enfile:已達到系統對打開檔案的限制

  enodev:指定檔案所在的檔案系統不支援記憶體映射

  enomem:記憶體不足,或者程序已超出最大記憶體映射數量

  eperm:權能不足,操作不允許

  etxtbsy:已寫的方式打開檔案,同時指定map_denywrite标志

  sigsegv:試着向隻讀區寫入

  sigbus:試着通路不屬于程序的記憶體區

  在fork之前可以先指定map_shared調用mmap。mmap成功傳回後可關閉fd,該操作對mmap建立的映射關系無影響。

addr:由mmap傳回的位址

len:映射區大小

再次通路這些位址将會導緻sigsegv信号,如果被映射區域是map_private标志,那麼調用程序對他的所有變動都會被丢棄

  核心的虛拟記憶體算法保持映射檔案(一般在硬碟上)與記憶體映射區(一般在記憶體中)的同步,前提他是一個map_shared記憶體區,也就是我們改了處于記憶體映射到某個檔案的記憶體區中某個位置的内容,那麼核心将在稍後某個時刻相應的更新檔案,然而我們需要确信硬碟上的檔案内容與記憶體區中的内容一緻,于是調用該函數來執行同步

 flags:

ms_async:執行異步寫,一旦寫操作已由核心排入隊列,該标志指定的函數立即傳回

ms_sync:執行同步寫(與async選其一,不能同時指定),該标志是等到寫操作完成後再傳回

ms_invalidate:使高速緩存的資料丢失,與其最終副本不一緻的檔案資料的所有記憶體中副本都失效,後續的引用将從檔案中取得資料

信号量父子程序中各有一份

Posix共享記憶體區

信号量在共享記憶體中

Posix共享記憶體區

匿名記憶體映射

使用dev/zero映射

代碼 github連接配接:​​https://github.com/tianzengblog/test/tree/master/ipc2/mmap​​​​

​​

通路記憶體映射區對象

  檔案大小等于記憶體映射區大小,但這個大小不是頁面大小的整數倍。比如記憶體映射區大小我5000,頁面的大小為4096,在程式中仍能通路第二頁(4096-8191),但通路第三頁引發sigesgv信号。是以核心允許讀寫最後一頁中映射區以遠的部分(核心的記憶體保護以頁為機關),但向這部分擴充區寫的任何内容都不會寫到檔案中,

  由shm_open打開一個ipc的名字(也許是在檔案系統中的某個路徑),所傳回的描述符由mmap函數映射到目前程序的位址空間。

指定一個參數名字調用shm_open,以建立一個新的共享記憶體區對象或打開一個已存在的共享記憶體區對象

調用mmap把這個共享記憶體區映射到調用程序的位址空間,傳遞給shm_open的名字參數随後由希望共享該記憶體區的任何其他程序調用

olfag:必須或者含有o_rdonly,或者含有o_rdwr,還可以指定o_creat,o_excl,或o_trunc。如果随o_rdwr指定o_trunc,且所需的共享記憶體已存在,那麼長度将會被截為0

mode:在指定o_creat标志前提下使用,如果沒有使用o_creat該參數置0,該标志必須指定。

删除一個共享記憶體區的對象名,跟其他所有unlink一樣(删除檔案系統的一個路徑名,删除一個posix消息隊列的mq_unlink,删除一個有名信号量sem_unlink)一樣,删除一個名字不會影響對其底層支援對象的現有引用,直到對該對象的全部引用關閉為止,删除一個名字僅僅為防止後續的open,mq_oepn,sem_open調用取得成功

共享記憶體在不同的程序中可以出現不同的位址

新建立的共享記憶體區對象大小應該為0,可用ftrucate改變共享記憶體對象大小,用fstat檢視共享記憶體對象大小

共享記憶體對象名不應該和消息隊列名相同

代碼連接配接:​​https://github.com/tianzengblog/test/tree/master/ipc2/mmap/shm​​​​

  普通檔案或共享記憶體區的大小都可以通過該函數修改,并且調用fstat獲得對象的資訊。

對于一個普通檔案,如果該檔案的大小大于lenght參數,額外的資料就會被丢棄,如果該檔案的大小小于lenght,那麼該檔案是否修改及其大小是否增長是未加說明的。實際上對于普通檔案來說,把他的大小擴充到length位元組的可移植方法是:先lseek到偏移位元組lenght-1處,然後write 1個位元組的資料,如果大小被擴充,那麼擴充的部分顯得好像已用0填寫過

對于一個共享記憶體區對象:ftruncate把該對象的大小設定為length位元組,如果共享記憶體區被擴充,那麼擴充部分全為0

記憶體映射檔案的資料載體是實體檔案。

共享記憶體區對象,也就是共享的資料載體是實體記憶體。