日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

linux進程通信實現機制有很多,也有各自優缺點和適用場景,關于它們之間的對比,等各種通信機制一一介紹后,再來一個匯總,俗話說“沒有對比就沒有傷害”,通過“傷害”讓大家徹底了解正確使用姿勢。

背景:

原由一:對內存的管理。

大家熟悉的Glibc庫提供的有:malloc、realloc、calloc(三者各自區別是什么,后續專題解說);

可能熟悉的有:google的Tcmalloc、FaceBook的Jemalloc,以及優化升級版的Ptmalloc;

brk、sbrk(用戶和內核均可用來“申請內存”,這塊后續專題介紹);

mmap,今天的主咖,一起看下她的前世今生,來龍去脈。

Slab緩存機制、Buddy伙伴算法(這塊后續專題介紹);

內核申請內存:kmalloc(內核空間,物理連續)、vmalloc(內核空間,物理不連續,虛擬連續。思考malloc單次調用申請的內存在物理上連續不);

內核管理頁表:pgd_offset(mm, addr)得到一級頁表入口、pmd_offset(pgd, addr)得到二級頁表入口

、通過pte_offset_map(pmd, addr)得到目標頁表項;

get_zeroed_page(unsigned int flags);指向一個清零的新page、 __get_free_page(unsigned int flags);指向新頁但不清零,實際上是用了order為0的下一個函數、__get_free_pages(unsigned int flags, unsigned int order);獲取多個pages數量是2^order,不清零;

virt_to_phys()實現內核虛擬向物理地址的轉化(感興趣可以自行查看)。

原由二:進程高效通信和文件讀寫

無名知道對于像有名/無名管道和消息隊列等通信方式,需要在內核和用戶空間進行兩次運行級別切換(系統調用導致保護和恢復進程上下文環境)+四次數據拷貝,而共享內存則只拷貝兩次數據: 一次從輸入文件到共享內存區,另一次從共享內存區到輸出文件。實際上,進程之間在共享內存時,并不總是讀寫少量數據后就解除映射,有新的通信時,不用再重新建立共享內存區域,而是保持共享區域,直到通信完畢為止,這樣,數據內容一直保存在共享內存中,并沒有寫回文件(內核通過一定策略刷盤,后續專題介紹)。共享內存中的數據往往是在解除映射時才寫回文件的。因此,采用共享內存的通信方式效率是非常高的。


一起細數mmap

1.共享內存的概念

共享內存可以說是最有用的進程間通信方式,也是最快的IPC形式。兩個不同進程A、B共享內存的意思是,同一塊物理內存被映射到進程A、B各自的進程地址空間。進程A可以(通過flag設置)即時看到進程B對共享內存中數據的更新,反之亦然。

2.mmap工作原理

mmap是一種內存映射文件的方法,即將一個文件或者其它對象映射到進程的地址空間,實現文件磁盤地址和進程虛擬地址空間中一段虛擬地址的一一對映關系。實現這樣的映射關系后,進程就可以采用指針的方式讀寫操作這一段內存,而系統會自動回寫臟頁面到對應的文件磁盤上,即完成了對文件的操作而不必再調用read,write等系統調用函數。相反,內核空間對這段區域的修改也直接反映用戶空間,從而可以實現不同進程間的文件共享。如下圖所示:

Linux進程通信之mmap

mmap映射概貌.png

mmap內存映射的實現過程,總的來說可以分為三個階段:

①進程啟動映射過程,并在虛擬地址空間中為映射創建虛擬映射區域

進程在用戶空間調用mmap函數。

在當前進程的虛擬地址空間中,尋找一段空閑的滿足要求的連續的虛擬地址。

為此虛擬去分配一個vm_area_struct結構,并對該結構各個域進行初始化。

將新建的vm_area_struct插入到進程的虛擬地址區域鏈表或樹中。

②調用內核空間的系統調用函數mmap(不同于用戶空間函數),實現文件物理地址和進程虛擬地址的一一映射關系

為映射分配了新的虛擬地址區域后,通過待映射的文件指針,在文件描述符表中找到對應的文件描述符,通過文件描述符,連接到內核“已打開文集”中該文件的文件結構體struct file,每個文件結構體維護著和這個已打開文件的相關信息(從open函數調用到內核返回文件描述符這塊隸屬于文件系統的知識,后續專題補充)。

通過該文件的文件結構體,連接到file_operations模塊,調用 內核函數mmap,int mmap(struct file *filep,struct vm_area_struct *vma)。

內核mmap函數通過虛擬文件系統inode模塊定位到文件磁盤物理地址。

通過remap_pfn_range函數建立頁表,即實現了文件地址和虛擬地址區域的映射,此時這片虛擬地址區域沒有任何數據關聯到主存中

③進程發起對這片映射空間的訪問,引發缺頁異常,實現文件內容到物理內存(主存)的拷貝

進程的讀寫操作訪問虛擬地址空間的這一段映射地址,通過查詢頁表,發現這一段地址不在物理頁面上,因為只是建立了地址映射,真正的磁盤數據還沒有拷貝到內存中,因此引發缺頁異常缺頁異常進行一系列判斷,確定無非法操作后,內核發起請求調頁過程調頁過程先在交換緩存空間中尋找需要訪問的內存頁,如果沒有則調用nopage函數把所缺的頁面從磁盤裝入主存中之后進程可對這片主存進行讀或寫操作,如果寫操作改變了內容,一定時間后系統會自動回寫臟頁面到對應的磁盤地址,也就是完成了寫入到文件的過程修改過的臟頁面不會立即更新到文件中,而是有一段時間的延遲,可以調用msync來強制同步,這樣所寫的內容就立即保存到文件里了。

對于共享內存映射情況,缺頁異常處理程序首先在swap cache中尋找目標頁(符address_space以及偏移量的物理頁),如果找到,則直接返回地址;如果沒有找到,則判斷該頁是否在交換區(swap area),如果在,則執行一個換入操作;如果上述兩種情況都不滿足,處理程序將分配新的物理頁面,并把它插入到page cache中。進程最終將更新進程頁表。

注:對于映射普通文件情況(非共享映射),缺頁異常處理程序首先會在page cache中根據address_space以及數據偏移量尋找相應的頁面。如果沒有找到,則說明文件數據還沒有讀入內存,處理程序會從磁盤讀入相應的頁面,并返回相應地址,同時,進程頁表也會更新。 所有進程在映射同一個共享內存區域時,情況都一樣,在建立線性地址與物理地址之間的映射之后,不論進程各自的返回地址如何,實際訪問的必然是同一個共享內存區域對應的物理頁面。

函數原型:

mmap函數

void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);

使用mmap目的有三:

使用普通文件以提供內存映射IO;

使用特殊文件以提供匿名內存映射;

使用shm_open以提供無親緣關系的進程間的Poxis共享內存區;

mmap的作用是映射文件描述符fd指定文件的 [offset,offset + length]區域至調用進程的[start, start + length]的內存區域。

函數返回成功時指向目標內存區域的指針,失敗返回MAP_FAILED((void*)-1)并設置錯誤碼errorno的值。

參數介紹:

start:映射區開始地址,用戶可以給定。建議設定NULL,有內核給定起始地址。

length:映射區的長度。

prot:期望的內存保護標志,不能與文件打開模式沖突

PROT_EXEC :頁內容可以被執行

PROT_READ :頁內容可以被讀取

PROT_WRITE :也可以被寫入

PROT_NONE :也不可訪問

fd:有效的文件描述符。參數fd為即將映射到進程空間的文件描述字,一般由open()返回,同時,fd可以指定為-1,此時須指定flags參數中的MAP_ANON,表明進行的是匿名映射(不涉及具體的文件名,避免了文件的創建及打開,很顯然只能用于具有親緣關系的進程間通信,即用于通過fork/vfork/clone系統調用創建的子進程之間的匿名映射通信。這里要強調的是雖說子進程繼承父進程的Address Space孵化自己,但是任何一方對共享變量發生寫操作,就會產生COW。所以為了有助于大家理解,可以說是父子進程“公用”調用fork/vfork/clone函數之前所定義的變量對象,而不是共享。

offset:設置文件從何處開始映射(對于不需要讀入整個文件的情況)。

flags:指定映射對象的類型,映射選項和映射頁是否可以共享。它的值可以是一個或者多個以下位的組合體,具體如下:

MAP_FIXED使用指定的映射起始地址,如果由start和length參數指定的內存區重疊于現存的映射空間,重疊部分將會被丟棄。如果指定的起始地址不可用,操作將會失敗。并且起始地址必須落在頁的邊界上。

MAP_SHARED,與其它所有映射這個對象的進程共享映射空間。對共享區的寫入,相當于輸出到文件。直到msync()或者munmap()被調用,文件實際上不會被更新。

MAP_PRIVATE建立一個寫入時拷貝的私有映射。內存區域的寫入不會影響到原文件。這個標志和以上標志是互斥的,只能使用其中一個。

MAP_DENYWRITE這個標志被忽略。

MAP_EXECUTABLE同上。

MAP_NORESERVE不要為這個映射保留交換空間。當交換空間被保留,對映射區修改的可能會得到保證。當交換空間不被保留,同時內存不足,對映射區的修改會引起短違例信號。

MAP_LOCKED鎖定映射區的頁面,從而防止頁面被交換出內存。

MAP_GROWSDOWN用于堆棧,告訴內核VM系統,映射區可以向下擴展。

MAP_ANONYMOUS匿名映射,映射區不與任何文件關聯。

MAP_ANON,MAP_ANONYMOUS的別稱,不再被使用。

MAP_FILE兼容標志,被忽略。

MAP_32BIT將映射區放在進程地址空間的第2GB,MAP_FIXED指定時會被忽略。當前這個標志只在x86-64平臺上得到支持。

MAP_POPULATE為文件映射通過預讀的方式準備好頁表。隨后對映射區的訪問不會被頁圍例阻塞。

MAP_NONBLOCK僅和MAP_POPULATE一起使用時才有意義。不執行預讀,只為已存在于內存中的頁面建立頁表。

munmap函數

int munmap(void *addr, size_t len);

從某個進程的地址空間刪除一個映射關系。再次訪問這些地址會導致調用產生一個SIGSEGV信號(這里假設后續mmap調用沒有恰巧重用這部分地址空間)。如果被映射去是使用MAP_PRIVATE標示映射的,那么調用進程對她所作的變動都被丟棄。如果是MAP_SHARED映射,內核的虛擬內存算法保障內存映射文件(一般在磁盤上)與內存映射區(在用戶態內存中)的同步。然而有時候在業務上需要確保硬盤上的文件內容和內存映射區中內容一致,可以調用masync函數來“手動”執行同步。 參數介紹 addr參數是由mmap函數返回的地址,len是映射區大小。

msync函數

int msync( void *addr, size_t len, int flags )。

一般說來,進程在映射空間的對共享內容的改變并不直接寫回到磁盤文件中,往往在調用munmap()后才執行該操作。可以通過調用msync()實現磁盤上文件內容與共享內存區的內容一致。

3.mmap和常規文件操作的區別

常規文件系統操作(調用read/fread等類函數)中,函數的調用過程:

1.進程發起讀文件請求。

2.內核通過查找進程文件符表,定位到內核已打開文件集上的文件信息,從而找到此文件的inode(這塊后續有專題去介紹)。一個具體的文件在打開后,內核會在內存中為之建立一個struct inode結構,其中的i_mApping域指向一個address_space結構。

3.inode在address_space上查找要請求的文件頁是否已經緩存在頁緩存中。如果存在,則直接返回這片文件頁的內容。一個文件對應一個address_space結構,一個address_space與一個偏移量能夠確定一個page cache 或swap cache中的一個頁面 。因此,當要尋址某個數據時,很容易根據給定的文件及數據在文件內的偏移量而找到相應的頁面。

4.如果不存在,則通過inode定位到文件磁盤地址,將數據從磁盤復制到頁緩存。之后再次發起讀頁面過程,進而將頁緩存中的數據通過copy_to_user把數據copy到用戶進程cache。

總結來說,常規文件操作為了提高讀寫效率和保護磁盤,使用了頁緩存機制。這樣造成讀文件時需要先將文件頁從磁盤拷貝到頁緩存中,由于頁緩存處在內核空間,不能被用戶進程直接尋址,所以還需要將頁緩存中數據頁再次拷貝到內存對應的用戶空間中。這樣,通過兩次數據拷貝過程,才能完成進程對文件內容的獲取任務。寫操作也是一樣,待寫入的buffer在內核空間不能直接訪問,必須要先拷貝至內核空間對應的主存,再寫回磁盤中(延遲寫回),也是需要兩次數據拷貝。

而使用mmap操作文件中,創建新的虛擬內存區域和建立文件磁盤地址和虛擬內存區域映射這兩步,沒有任何文件拷貝操作。而之后訪問數據時發現內存中并無數據而發起的缺頁異常過程,可以通過已經建立好的映射關系,只使用一次數據拷貝,就從磁盤中將數據傳入內存的用戶空間中,供進程使用。

mmap優點總結

1.對文件的讀取操作跨過了頁緩存,減少了數據的拷貝次數。用內存讀寫取代I/O讀寫,提高了文件讀取效率。兩空間的各自修改操作可以直接反映在映射的區域內,從而被對方空間及時捕捉。

2.實現了用戶空間和內核空間的高效交互方式。

3.提供進程間共享內存以及相互通信的方式。兩個進程將自身的用戶空間映射到同一片區域,從而達到通信或者共享的目的。(進程A和進程B都映射了區域C,當A第一次讀取C時通過缺頁異常從磁盤復制文件頁到內存中,但當B再去讀取C的時候,雖然會產生缺頁異常,但是不需要再從磁盤復制文件,而是可以直接使用保存在內存中的文件數據)。

4、可用于實現高效的大規模數據傳輸。內存空間不足,是制約大數據操作的一個方面,解決方案往往是借助硬盤空間協助操作,補充內存的不足。但是進一步會造成大量的文件I/O操作,極大影響效率。這個問題可以通過mmap映射很好地解決。換句話說,但凡是需要用磁盤空間代替內存的時候,mmap都可以發揮其功效。

總而言之,常規文件操作需要從磁盤到頁緩存再到用戶主存的兩次數據拷貝。而mmap操控文件,只需要從磁盤到用戶主存的一次數據拷貝過程。說白了,mmap的關鍵點是實現了用戶空間和內核空間的數據直接交互而省去了空間不同數據不同的繁瑣過程。因此mmap效率更高。

4.mmap細節及使用舉例

1、使用mmap需要注意的一個關鍵點是,mmap映射區域大小必須是物理頁大小(page_size)的整倍數(32位系統中通常是4k字節)。原因是,內存的最小粒度是頁,而進程虛擬地址空間和內存的映射也是以頁為單位。為了匹配內存的操作,mmap從磁盤到虛擬地址空間的映射也必須是頁。

再啰嗦幾句:

linux采用的是頁式管理機制。對于用mmap()映射普通文件來說,進程會在自己的地址空間新增一塊空間,空間大小由mmap()的len參數指定,注意,進程并不一定能夠對全部新增空間都能進行有效訪問。進程能夠訪問的有效地址大小取決于文件被映射部分的大小。簡單地說,能夠容納文件被映射部分大小的最少頁面個數決定了進程從mmap()返回的地址開始,能夠有效訪問的地址空間大小。超過這個空間大小,內核會根據超過的嚴重程度返回發送不同的信號給進程。

2、內核可以跟蹤被內存映射的底層對象(文件)的大小,進程可以合法的訪問在當前文件大小以內又在內存映射區以內的那些字節。也就是說,如果文件的大小一直在擴張,只要在映射區域范圍內的數據,進程都可以合法得到,這和映射建立時文件的大小無關。具體情形參見“情形三”。

3、映射建立之后,即使文件關閉,映射依然存在。因為映射的是磁盤的地址,不是文件本身,和文件句柄無關。同時可用于進程間通信的有效地址空間不完全受限于被映射文件的大小,因為是按頁映射。

在上面的知識前提下,我們下面看看如果大小不是頁的整倍數的具體情況:


情形一:一個文件的大小是5000字節,mmap函數從一個文件的起始位置開始,映射5000字節到虛擬內存中。

分析:因為單位物理頁面的大小是4096字節,雖然被映射的文件只有5000字節,但是對應到進程虛擬地址區域的大小需要滿足整頁大小,因此mmap函數執行后,實際映射到虛擬內存區域8192個 字節,5000~8191的字節部分用零填充。映射后的對應關系如下圖所示:


Linux進程通信之mmap

消耗兩個內存頁.png

此時:

(1)讀/寫前5000個字節(0~4999),會返回操作文件內容。

(2)讀字節5000~8191時,結果全為0。寫5000~8191時,進程不會報錯,但是所寫的內容不會寫入原文件中 。

(3)讀/寫8192以外的磁盤部分,會返回一個SIGSECV錯誤。

情形二:一個文件的大小是5000字節,mmap函數從一個文件的起始位置開始,映射15000字節到虛擬內存中,即映射大小超過了原始文件的大小。

分析:由于文件的大小是5000字節,和情形一一樣,其對應的兩個物理頁。那么這兩個物理頁都是合法可以讀寫的,只是超出5000的部分不會體現在原文件中。由于程序要求映射15000字節,而文件只占兩個物理頁,因此8192字節~15000字節都不能讀寫,操作時會返回異常。如下圖所示:

Linux進程通信之mmap

映射大于物理文件大小將發生異常.png

此時:

(1)進程可以正常讀/寫被映射的前5000字節(0~4999),寫操作的改動會在一定時間后反映在原文件中。

(2)對于5000~8191字節,進程可以進行讀寫過程,不會報錯。但是內容在寫入前均為0,另外,寫入后不會反映在文件中。

(3)對于8192~14999字節,進程不能對其進行讀寫,會報SIGBUS錯誤。

(4)對于15000以外的字節,進程不能對其讀寫,會引發SIGSEGV錯誤。

情形三:一個文件初始大小為0,使用mmap操作映射了1000*4K的大小,即1000個物理頁大約4M字節空間,mmap返回指針ptr。

分析:如果在映射建立之初,就對文件進行讀寫操作,由于文件大小為0,并沒有合法的物理頁對應,如同情形二一樣,會返回SIGBUS錯誤。

但是如果,每次操作ptr讀寫前,先增加文件的大小,那么ptr在文件大小內部的操作就是合法的。例如,文件擴充4096字節,ptr就能操作ptr到 [ (char)ptr + 4095]的空間。只要文件擴充的范圍在1000個物理頁(映射范圍)內,ptr都可以對應操作相同的大小。這樣,方便隨時擴充文件空間,隨時寫入文件,不造成空間浪費

分享到:
標簽:mmap
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定