Linux進(jìn)程通信之mmap

Linux進(jìn)程通信實(shí)現(xiàn)機(jī)制有很多名惩,也有各自優(yōu)缺點(diǎn)和適用場(chǎng)景泌绣,關(guān)于她們之間的對(duì)比钮追,等各種通信機(jī)制一一介紹后,再來(lái)一個(gè)匯總赞别,俗話說(shuō)“沒(méi)有對(duì)比就沒(méi)有傷害”畏陕,通過(guò)“傷害”讓大家徹底了解正確使用姿勢(shì)。

背景:

原由一:對(duì)內(nèi)存的管理仿滔。

大家熟悉的Glibc庫(kù)提供的有:malloc惠毁、realloc、calloc(三者各自區(qū)別是什么崎页,后續(xù)專題解說(shuō))鞠绰;

可能熟悉的有:Google的Tcmalloc、FaceBook的Jemalloc飒焦,以及優(yōu)化升級(jí)版的Ptmalloc蜈膨;

brk、sbrk(用戶和內(nèi)核均可用來(lái)“申請(qǐng)內(nèi)存”牺荠,這塊后續(xù)專題介紹)翁巍;

mmap,今天的主咖休雌,一起看下她的前世今生灶壶,來(lái)龍去脈。

Slab緩存機(jī)制杈曲、Buddy伙伴算法(這塊后續(xù)專題介紹)驰凛;

內(nèi)核申請(qǐng)內(nèi)存:kmalloc(內(nèi)核空間胸懈,物理連續(xù))、vmalloc(內(nèi)核空間恰响,物理不連續(xù)趣钱,虛擬連續(xù)。思考malloc單次調(diào)用申請(qǐng)的內(nèi)存在物理上連續(xù)不)胚宦;

內(nèi)核管理頁(yè)表:pgd_offset(mm, addr)得到一級(jí)頁(yè)表入口首有、pmd_offset(pgd, addr)得到二級(jí)頁(yè)表入口

、通過(guò)pte_offset_map(pmd, addr)得到目標(biāo)頁(yè)表項(xiàng)枢劝;

get_zeroed_page(unsigned int flags);指向一個(gè)清零的新page绞灼、 __get_free_page(unsigned int flags);指向新頁(yè)但不清零,實(shí)際上是用了order為0的下一個(gè)函數(shù)呈野、__get_free_pages(unsigned int flags, unsigned int order);獲取多個(gè)pages數(shù)量是2^order,不清零印叁;

virt_to_phys()實(shí)現(xiàn)內(nèi)核虛擬向物理地址的轉(zhuǎn)化(感興趣可以自行查看)被冒。

原由二:進(jìn)程高效通信和文件讀寫(xiě)

無(wú)名知道對(duì)于像有名/無(wú)名管道和消息隊(duì)列等通信方式,需要在內(nèi)核和用戶空間進(jìn)行兩次運(yùn)行級(jí)別切換(系統(tǒng)調(diào)用導(dǎo)致保護(hù)和恢復(fù)進(jìn)程上下文環(huán)境)+四次數(shù)據(jù)拷貝轮蜕,而共享內(nèi)存則只拷貝兩次數(shù)據(jù): 一次從輸入文件到共享內(nèi)存區(qū)昨悼,另一次從共享內(nèi)存區(qū)到輸出文件。實(shí)際上跃洛,進(jìn)程之間在共享內(nèi)存時(shí)率触,并不總是讀寫(xiě)少量數(shù)據(jù)后就解除映射,有新的通信時(shí)汇竭,不用再重新建立共享內(nèi)存區(qū)域葱蝗,而是保持共享區(qū)域,直到通信完畢為止细燎,這樣两曼,數(shù)據(jù)內(nèi)容一直保存在共享內(nèi)存中,并沒(méi)有寫(xiě)回文件(內(nèi)核通過(guò)一定策略刷盤(pán)玻驻,后續(xù)專題介紹)悼凑。共享內(nèi)存中的數(shù)據(jù)往往是在解除映射時(shí)才寫(xiě)回文件的。因此璧瞬,采用共享內(nèi)存的通信方式效率是非常高的户辫。


一起細(xì)數(shù)mmap

1.共享內(nèi)存的概念

共享內(nèi)存可以說(shuō)是最有用的進(jìn)程間通信方式,也是最快的IPC形式嗤锉。兩個(gè)不同進(jìn)程A渔欢、B共享內(nèi)存的意思是,同一塊物理內(nèi)存被映射到進(jìn)程A档冬、B各自的進(jìn)程地址空間膘茎。進(jìn)程A可以(通過(guò)flag設(shè)置)即時(shí)看到進(jìn)程B對(duì)共享內(nèi)存中數(shù)據(jù)的更新桃纯,反之亦然。

2.mmap工作原理

mmap是一種內(nèi)存映射文件的方法披坏,即將一個(gè)文件或者其它對(duì)象映射到進(jìn)程的地址空間态坦,實(shí)現(xiàn)文件磁盤(pán)地址和進(jìn)程虛擬地址空間中一段虛擬地址的一一對(duì)映關(guān)系。實(shí)現(xiàn)這樣的映射關(guān)系后棒拂,進(jìn)程就可以采用指針的方式讀寫(xiě)操作這一段內(nèi)存伞梯,而系統(tǒng)會(huì)自動(dòng)回寫(xiě)臟頁(yè)面到對(duì)應(yīng)的文件磁盤(pán)上,即完成了對(duì)文件的操作而不必再調(diào)用read,write等系統(tǒng)調(diào)用函數(shù)帚屉。相反谜诫,內(nèi)核空間對(duì)這段區(qū)域的修改也直接反映用戶空間,從而可以實(shí)現(xiàn)不同進(jìn)程間的文件共享攻旦。如下圖所示:

mmap映射概貌.png

mmap內(nèi)存映射的實(shí)現(xiàn)過(guò)程喻旷,總的來(lái)說(shuō)可以分為三個(gè)階段:

①進(jìn)程啟動(dòng)映射過(guò)程,并在虛擬地址空間中為映射創(chuàng)建虛擬映射區(qū)域

進(jìn)程在用戶空間調(diào)用mmap函數(shù)牢屋。

在當(dāng)前進(jìn)程的虛擬地址空間中且预,尋找一段空閑的滿足要求的連續(xù)的虛擬地址。

為此虛擬去分配一個(gè)vm_area_struct結(jié)構(gòu)烙无,并對(duì)該結(jié)構(gòu)各個(gè)域進(jìn)行初始化锋谐。

將新建的vm_area_struct插入到進(jìn)程的虛擬地址區(qū)域鏈表或樹(shù)中。

②調(diào)用內(nèi)核空間的系統(tǒng)調(diào)用函數(shù)mmap(不同于用戶空間函數(shù))截酷,實(shí)現(xiàn)文件物理地址和進(jìn)程虛擬地址的一一映射關(guān)系

為映射分配了新的虛擬地址區(qū)域后涮拗,通過(guò)待映射的文件指針,在文件描述符表中找到對(duì)應(yīng)的文件描述符迂苛,通過(guò)文件描述符三热,連接到內(nèi)核“已打開(kāi)文集”中該文件的文件結(jié)構(gòu)體struct file,每個(gè)文件結(jié)構(gòu)體維護(hù)著和這個(gè)已打開(kāi)文件的相關(guān)信息(從open函數(shù)調(diào)用到內(nèi)核返回文件描述符這塊隸屬于文件系統(tǒng)的知識(shí)三幻,后續(xù)專題補(bǔ)充)康铭。

通過(guò)該文件的文件結(jié)構(gòu)體,連接到file_operations模塊赌髓,調(diào)用 內(nèi)核函數(shù)mmap从藤,int mmap(struct file *filep,struct vm_area_struct *vma)。

內(nèi)核mmap函數(shù)通過(guò)虛擬文件系統(tǒng)inode模塊定位到文件磁盤(pán)物理地址锁蠕。

通過(guò)remap_pfn_range函數(shù)建立頁(yè)表夷野,即實(shí)現(xiàn)了文件地址和虛擬地址區(qū)域的映射,此時(shí)這片虛擬地址區(qū)域沒(méi)有任何數(shù)據(jù)關(guān)聯(lián)到主存中

③進(jìn)程發(fā)起對(duì)這片映射空間的訪問(wèn)荣倾,引發(fā)缺頁(yè)異常悯搔,實(shí)現(xiàn)文件內(nèi)容到物理內(nèi)存(主存)的拷貝

進(jìn)程的讀寫(xiě)操作訪問(wèn)虛擬地址空間的這一段映射地址,通過(guò)查詢頁(yè)表舌仍,發(fā)現(xiàn)這一段地址不在物理頁(yè)面上妒貌,因?yàn)?b>只是建立了地址映射通危,真正的磁盤(pán)數(shù)據(jù)還沒(méi)有拷貝到內(nèi)存中,因此引發(fā)缺頁(yè)異常缺頁(yè)異常進(jìn)行一系列判斷灌曙,確定無(wú)非法操作后菊碟,內(nèi)核發(fā)起請(qǐng)求調(diào)頁(yè)過(guò)程調(diào)頁(yè)過(guò)程先在交換緩存空間中尋找需要訪問(wèn)的內(nèi)存頁(yè),如果沒(méi)有則調(diào)用nopage函數(shù)把所缺的頁(yè)面從磁盤(pán)裝入主存中之后進(jìn)程可對(duì)這片主存進(jìn)行讀或?qū)懖僮髟诖蹋绻麑?xiě)操作改變了內(nèi)容逆害,一定時(shí)間后系統(tǒng)會(huì)自動(dòng)回寫(xiě)臟頁(yè)面到對(duì)應(yīng)的磁盤(pán)地址,也就是完成了寫(xiě)入到文件的過(guò)程修改過(guò)的臟頁(yè)面不會(huì)立即更新到文件中蚣驼,而是有一段時(shí)間的延遲魄幕,可以調(diào)用msync來(lái)強(qiáng)制同步,這樣所寫(xiě)的內(nèi)容就立即保存到文件里了颖杏。

對(duì)于共享內(nèi)存映射情況纯陨,缺頁(yè)異常處理程序首先在swap cache中尋找目標(biāo)頁(yè)(符address_space以及偏移量的物理頁(yè)),如果找到留储,則直接返回地址队丝;如果沒(méi)有找到,則判斷該頁(yè)是否在交換區(qū)(swap area)欲鹏,如果在,則執(zhí)行一個(gè)換入操作臭墨;如果上述兩種情況都不滿足赔嚎,處理程序?qū)⒎峙湫碌奈锢眄?yè)面,并把它插入到page cache中胧弛。進(jìn)程最終將更新進(jìn)程頁(yè)表尤误。?

注:對(duì)于映射普通文件情況(非共享映射),缺頁(yè)異常處理程序首先會(huì)在page cache中根據(jù)address_space以及數(shù)據(jù)偏移量尋找相應(yīng)的頁(yè)面结缚。如果沒(méi)有找到损晤,則說(shuō)明文件數(shù)據(jù)還沒(méi)有讀入內(nèi)存,處理程序會(huì)從磁盤(pán)讀入相應(yīng)的頁(yè)面红竭,并返回相應(yīng)地址尤勋,同時(shí),進(jìn)程頁(yè)表也會(huì)更新茵宪。? ? ? ? ? ? ? ? ?所有進(jìn)程在映射同一個(gè)共享內(nèi)存區(qū)域時(shí)最冰,情況都一樣,在建立線性地址與物理地址之間的映射之后稀火,不論進(jìn)程各自的返回地址如何暖哨,實(shí)際訪問(wèn)的必然是同一個(gè)共享內(nèi)存區(qū)域?qū)?yīng)的物理頁(yè)面。??

函數(shù)原型:

mmap函數(shù)

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

使用mmap目的有三:

使用普通文件以提供內(nèi)存映射IO凰狞;

使用特殊文件以提供匿名內(nèi)存映射篇裁;

使用shm_open以提供無(wú)親緣關(guān)系的進(jìn)程間的Poxis共享內(nèi)存區(qū)沛慢;

mmap的作用是映射文件描述符fd指定文件的 [offset,offset + length]區(qū)域至調(diào)用進(jìn)程的[start, start + length]的內(nèi)存區(qū)域。

函數(shù)返回成功時(shí)指向目標(biāo)內(nèi)存區(qū)域的指針达布,失敗返回MAP_FAILED((void*)-1)并設(shè)置錯(cuò)誤碼errorno的值团甲。

參數(shù)介紹:

start:映射區(qū)開(kāi)始地址,用戶可以給定往枣。建議設(shè)定NULL伐庭,有內(nèi)核給定起始地址。

length:映射區(qū)的長(zhǎng)度分冈。

prot:期望的內(nèi)存保護(hù)標(biāo)志圾另,不能與文件打開(kāi)模式?jīng)_突

?PROT_EXEC :頁(yè)內(nèi)容可以被執(zhí)行

?PROT_READ :頁(yè)內(nèi)容可以被讀取

?PROT_WRITE :頁(yè)可以被寫(xiě)入

?PROT_NONE :頁(yè)不可訪問(wèn)

fd:有效的文件描述符。參數(shù)fd為即將映射到進(jìn)程空間的文件描述字雕沉,一般由open()返回集乔,同時(shí),fd可以指定為-1坡椒,此時(shí)須指定flags參數(shù)中的MAP_ANON扰路,表明進(jìn)行的是匿名映射(不涉及具體的文件名,避免了文件的創(chuàng)建及打開(kāi)倔叼,很顯然只能用于具有親緣關(guān)系的進(jìn)程間通信汗唱,即用于通過(guò)fork/vfork/clone系統(tǒng)調(diào)用創(chuàng)建的子進(jìn)程之間的匿名映射通信。這里要強(qiáng)調(diào)的是雖說(shuō)子進(jìn)程繼承父進(jìn)程的Address Space孵化自己丈攒,但是任何一方對(duì)共享變量發(fā)生寫(xiě)操作哩罪,就會(huì)產(chǎn)生COW。所以為了有助于大家理解巡验,可以說(shuō)是父子進(jìn)程“公用”調(diào)用fork/vfork/clone函數(shù)之前所定義的變量對(duì)象际插,而不是共享。

offset:設(shè)置文件從何處開(kāi)始映射(對(duì)于不需要讀入整個(gè)文件的情況)显设。

flags:指定映射對(duì)象的類型框弛,映射選項(xiàng)和映射頁(yè)是否可以共享。它的值可以是一個(gè)或者多個(gè)以下位的組合體捕捂,具體如下:

MAP_FIXED使用指定的映射起始地址瑟枫,如果由start和length參數(shù)指定的內(nèi)存區(qū)重疊于現(xiàn)存的映射空間,重疊部分將會(huì)被丟棄指攒。如果指定的起始地址不可用力奋,操作將會(huì)失敗。并且起始地址必須落在頁(yè)的邊界上幽七。

MAP_SHARED景殷,與其它所有映射這個(gè)對(duì)象的進(jìn)程共享映射空間。對(duì)共享區(qū)的寫(xiě)入,相當(dāng)于輸出到文件猿挚。直到msync()或者munmap()被調(diào)用咐旧,文件實(shí)際上不會(huì)被更新。

MAP_PRIVATE建立一個(gè)寫(xiě)入時(shí)拷貝的私有映射绩蜻。內(nèi)存區(qū)域的寫(xiě)入不會(huì)影響到原文件铣墨。這個(gè)標(biāo)志和以上標(biāo)志是互斥的,只能使用其中一個(gè)办绝。

MAP_DENYWRITE這個(gè)標(biāo)志被忽略伊约。

MAP_EXECUTABLE同上。

MAP_NORESERVE不要為這個(gè)映射保留交換空間孕蝉。當(dāng)交換空間被保留屡律,對(duì)映射區(qū)修改的可能會(huì)得到保證。當(dāng)交換空間不被保留降淮,同時(shí)內(nèi)存不足超埋,對(duì)映射區(qū)的修改會(huì)引起段違例信號(hào)。

MAP_LOCKED鎖定映射區(qū)的頁(yè)面佳鳖,從而防止頁(yè)面被交換出內(nèi)存霍殴。

MAP_GROWSDOWN用于堆棧,告訴內(nèi)核VM系統(tǒng)系吩,映射區(qū)可以向下擴(kuò)展来庭。

MAP_ANONYMOUS匿名映射,映射區(qū)不與任何文件關(guān)聯(lián)穿挨。

MAP_ANON月弛,MAP_ANONYMOUS的別稱,不再被使用絮蒿。

MAP_FILE兼容標(biāo)志,被忽略叁鉴。

MAP_32BIT將映射區(qū)放在進(jìn)程地址空間的低2GB土涝,MAP_FIXED指定時(shí)會(huì)被忽略。當(dāng)前這個(gè)標(biāo)志只在x86-64平臺(tái)上得到支持幌墓。

MAP_POPULATE為文件映射通過(guò)預(yù)讀的方式準(zhǔn)備好頁(yè)表但壮。隨后對(duì)映射區(qū)的訪問(wèn)不會(huì)被頁(yè)違例阻塞。

MAP_NONBLOCK僅和MAP_POPULATE一起使用時(shí)才有意義常侣。不執(zhí)行預(yù)讀蜡饵,只為已存在于內(nèi)存中的頁(yè)面建立頁(yè)表。

munmap函數(shù)

int munmap(void *addr, size_t len);?

從某個(gè)進(jìn)程的地址空間刪除一個(gè)映射關(guān)系胳施。再次訪問(wèn)這些地址會(huì)導(dǎo)致調(diào)用產(chǎn)生一個(gè)SIGSEGV信號(hào)(這里假設(shè)后續(xù)mmap調(diào)用沒(méi)有恰巧重用這部分地址空間)溯祸。如果被映射去是使用MAP_PRIVATE標(biāo)示映射的,那么調(diào)用進(jìn)程對(duì)她所作的變動(dòng)都被丟棄。如果是MAP_SHARED映射焦辅,內(nèi)核的虛擬內(nèi)存算法保障內(nèi)存映射文件(一般在磁盤(pán)上)與內(nèi)存映射區(qū)(在用戶態(tài)內(nèi)存中)的同步博杖。然而有時(shí)候在業(yè)務(wù)上需要確保硬盤(pán)上的文件內(nèi)容和內(nèi)存映射區(qū)中內(nèi)容一致,可以調(diào)用masync函數(shù)來(lái)“手動(dòng)”執(zhí)行同步筷登。 參數(shù)介紹 addr參數(shù)是由mmap函數(shù)返回的地址剃根,len是映射區(qū)大小。

msync函數(shù)

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

一般說(shuō)來(lái)狈醉,進(jìn)程在映射空間的對(duì)共享內(nèi)容的改變并不直接寫(xiě)回到磁盤(pán)文件中,往往在調(diào)用munmap()后才執(zhí)行該操作惠险∶绺担可以通過(guò)調(diào)用msync()實(shí)現(xiàn)磁盤(pán)上文件內(nèi)容與共享內(nèi)存區(qū)的內(nèi)容一致。

3.mmap和常規(guī)文件操作的區(qū)別

常規(guī)文件系統(tǒng)操作(調(diào)用read/fread等類函數(shù))中莺匠,函數(shù)的調(diào)用過(guò)程:

1.進(jìn)程發(fā)起讀文件請(qǐng)求金吗。

2.內(nèi)核通過(guò)查找進(jìn)程文件符表,定位到內(nèi)核已打開(kāi)文件集上的文件信息趣竣,從而找到此文件的inode(這塊后續(xù)有專題去介紹)摇庙。一個(gè)具體的文件在打開(kāi)后,內(nèi)核會(huì)在內(nèi)存中為之建立一個(gè)struct inode結(jié)構(gòu)遥缕,其中的i_mapping域指向一個(gè)address_space結(jié)構(gòu)卫袒。

3.inode在address_space上查找要請(qǐng)求的文件頁(yè)是否已經(jīng)緩存在頁(yè)緩存中。如果存在单匣,則直接返回這片文件頁(yè)的內(nèi)容夕凝。一個(gè)文件對(duì)應(yīng)一個(gè)address_space結(jié)構(gòu),一個(gè)address_space與一個(gè)偏移量能夠確定一個(gè)page cache 或swap cache中的一個(gè)頁(yè)面 户秤。因此码秉,當(dāng)要尋址某個(gè)數(shù)據(jù)時(shí),很容易根據(jù)給定的文件及數(shù)據(jù)在文件內(nèi)的偏移量而找到相應(yīng)的頁(yè)面鸡号。

4.如果不存在转砖,則通過(guò)inode定位到文件磁盤(pán)地址,將數(shù)據(jù)從磁盤(pán)復(fù)制到頁(yè)緩存鲸伴。之后再次發(fā)起讀頁(yè)面過(guò)程府蔗,進(jìn)而將頁(yè)緩存中的數(shù)據(jù)通過(guò)copy_to_user把數(shù)據(jù)copy到用戶進(jìn)程cache。

總結(jié)來(lái)說(shuō)汞窗,常規(guī)文件操作為了提高讀寫(xiě)效率和保護(hù)磁盤(pán)姓赤,使用了頁(yè)緩存機(jī)制。這樣造成讀文件時(shí)需要先將文件頁(yè)從磁盤(pán)拷貝到頁(yè)緩存中仲吏,由于頁(yè)緩存處在內(nèi)核空間不铆,不能被用戶進(jìn)程直接尋址蝌焚,所以還需要將頁(yè)緩存中數(shù)據(jù)頁(yè)再次拷貝到內(nèi)存對(duì)應(yīng)的用戶空間中。這樣狂男,通過(guò)兩次數(shù)據(jù)拷貝過(guò)程综看,才能完成進(jìn)程對(duì)文件內(nèi)容的獲取任務(wù)。寫(xiě)操作也是一樣岖食,待寫(xiě)入的buffer在內(nèi)核空間不能直接訪問(wèn)红碑,必須要先拷貝至內(nèi)核空間對(duì)應(yīng)的主存,再寫(xiě)回磁盤(pán)中(延遲寫(xiě)回)泡垃,也是需要兩次數(shù)據(jù)拷貝析珊。

而使用mmap操作文件中,創(chuàng)建新的虛擬內(nèi)存區(qū)域和建立文件磁盤(pán)地址和虛擬內(nèi)存區(qū)域映射這兩步蔑穴,沒(méi)有任何文件拷貝操作忠寻。而之后訪問(wèn)數(shù)據(jù)時(shí)發(fā)現(xiàn)內(nèi)存中并無(wú)數(shù)據(jù)而發(fā)起的缺頁(yè)異常過(guò)程,可以通過(guò)已經(jīng)建立好的映射關(guān)系存和,只使用一次數(shù)據(jù)拷貝奕剃,就從磁盤(pán)中將數(shù)據(jù)傳入內(nèi)存的用戶空間中,供進(jìn)程使用捐腿。

mmap優(yōu)點(diǎn)總結(jié)

1.對(duì)文件的讀取操作跨過(guò)了頁(yè)緩存纵朋,減少了數(shù)據(jù)的拷貝次數(shù)。用內(nèi)存讀寫(xiě)取代I/O讀寫(xiě)茄袖,提高了文件讀取效率操软。兩空間的各自修改操作可以直接反映在映射的區(qū)域內(nèi),從而被對(duì)方空間及時(shí)捕捉宪祥。

2.實(shí)現(xiàn)了用戶空間和內(nèi)核空間的高效交互方式聂薪。

3.提供進(jìn)程間共享內(nèi)存以及相互通信的方式。兩個(gè)進(jìn)程將自身的用戶空間映射到同一片區(qū)域蝗羊,從而達(dá)到通信或者共享的目的藏澳。(進(jìn)程A和進(jìn)程B都映射了區(qū)域C,當(dāng)A第一次讀取C時(shí)通過(guò)缺頁(yè)異常從磁盤(pán)復(fù)制文件頁(yè)到內(nèi)存中耀找,但當(dāng)B再去讀取C的時(shí)候翔悠,雖然會(huì)產(chǎn)生缺頁(yè)異常,但是不需要再?gòu)拇疟P(pán)復(fù)制文件涯呻,而是可以直接使用保存在內(nèi)存中的文件數(shù)據(jù))凉驻。

4腻要、可用于實(shí)現(xiàn)高效的大規(guī)模數(shù)據(jù)傳輸复罐。內(nèi)存空間不足,是制約大數(shù)據(jù)操作的一個(gè)方面雄家,解決方案往往是借助硬盤(pán)空間協(xié)助操作效诅,補(bǔ)充內(nèi)存的不足。但是進(jìn)一步會(huì)造成大量的文件I/O操作,極大影響效率乱投。這個(gè)問(wèn)題可以通過(guò)mmap映射很好的解決咽笼。換句話說(shuō),但凡是需要用磁盤(pán)空間代替內(nèi)存的時(shí)候戚炫,mmap都可以發(fā)揮其功效剑刑。

總而言之,常規(guī)文件操作需要從磁盤(pán)到頁(yè)緩存再到用戶主存的兩次數(shù)據(jù)拷貝双肤。而mmap操控文件施掏,只需要從磁盤(pán)到用戶主存的一次數(shù)據(jù)拷貝過(guò)程。說(shuō)白了茅糜,mmap的關(guān)鍵點(diǎn)是實(shí)現(xiàn)了用戶空間和內(nèi)核空間的數(shù)據(jù)直接交互而省去了空間不同數(shù)據(jù)不通的繁瑣過(guò)程七芭。因此mmap效率更高

4.mmap細(xì)節(jié)及使用舉例

1蔑赘、使用mmap需要注意的一個(gè)關(guān)鍵點(diǎn)是狸驳,mmap映射區(qū)域大小必須是物理頁(yè)大小(page_size)的整倍數(shù)(32位系統(tǒng)中通常是4k字節(jié))。原因是缩赛,內(nèi)存的最小粒度是頁(yè)耙箍,而進(jìn)程虛擬地址空間和內(nèi)存的映射也是以頁(yè)為單位。為了匹配內(nèi)存的操作峦筒,mmap從磁盤(pán)到虛擬地址空間的映射也必須是頁(yè)究西。

再啰嗦幾句:

linux采用的是頁(yè)式管理機(jī)制。對(duì)于用mmap()映射普通文件來(lái)說(shuō)物喷,進(jìn)程會(huì)在自己的地址空間新增一塊空間卤材,空間大小由mmap()的len參數(shù)指定,注意峦失,進(jìn)程并不一定能夠?qū)θ啃略隹臻g都能進(jìn)行有效訪問(wèn)扇丛。進(jìn)程能夠訪問(wèn)的有效地址大小取決于文件被映射部分的大小。簡(jiǎn)單的說(shuō)尉辑,能夠容納文件被映射部分大小的最少頁(yè)面?zhèn)€數(shù)決定了進(jìn)程從mmap()返回的地址開(kāi)始帆精,能夠有效訪問(wèn)的地址空間大小。超過(guò)這個(gè)空間大小隧魄,內(nèi)核會(huì)根據(jù)超過(guò)的嚴(yán)重程度返回發(fā)送不同的信號(hào)給進(jìn)程卓练。

2、內(nèi)核可以跟蹤被內(nèi)存映射的底層對(duì)象(文件)的大小购啄,進(jìn)程可以合法的訪問(wèn)在當(dāng)前文件大小以內(nèi)又在內(nèi)存映射區(qū)以內(nèi)的那些字節(jié)襟企。也就是說(shuō),如果文件的大小一直在擴(kuò)張狮含,只要在映射區(qū)域范圍內(nèi)的數(shù)據(jù)顽悼,進(jìn)程都可以合法得到曼振,這和映射建立時(shí)文件的大小無(wú)關(guān)。具體情形參見(jiàn)“情形三”蔚龙。

3冰评、映射建立之后,即使文件關(guān)閉木羹,映射依然存在甲雅。因?yàn)橛成涞氖谴疟P(pán)的地址,不是文件本身坑填,和文件句柄無(wú)關(guān)务荆。同時(shí)可用于進(jìn)程間通信的有效地址空間不完全受限于被映射文件的大小,因?yàn)槭前错?yè)映射穷遂。

在上面的知識(shí)前提下函匕,我們下面看看如果大小不是頁(yè)的整倍數(shù)的具體情況:


情形一:一個(gè)文件的大小是5000字節(jié),mmap函數(shù)從一個(gè)文件的起始位置開(kāi)始蚪黑,映射5000字節(jié)到虛擬內(nèi)存中盅惜。

分析:因?yàn)閱挝晃锢眄?yè)面的大小是4096字節(jié),雖然被映射的文件只有5000字節(jié)忌穿,但是對(duì)應(yīng)到進(jìn)程虛擬地址區(qū)域的大小需要滿足整頁(yè)大小抒寂,因此mmap函數(shù)執(zhí)行后,實(shí)際映射到虛擬內(nèi)存區(qū)域8192個(gè) 字節(jié)掠剑,5000~8191的字節(jié)部分用零填充屈芜。映射后的對(duì)應(yīng)關(guān)系如下圖所示:


消耗兩個(gè)內(nèi)存頁(yè).png

此時(shí):

(1)讀/寫(xiě)前5000個(gè)字節(jié)(0~4999),會(huì)返回操作文件內(nèi)容朴译。

(2)讀字節(jié)5000~8191時(shí)井佑,結(jié)果全為0。寫(xiě)5000~8191時(shí)眠寿,進(jìn)程不會(huì)報(bào)錯(cuò)躬翁,但是所寫(xiě)的內(nèi)容不會(huì)寫(xiě)入原文件中 。

(3)讀/寫(xiě)8192以外的磁盤(pán)部分盯拱,會(huì)返回一個(gè)SIGSECV錯(cuò)誤盒发。

情形二:一個(gè)文件的大小是5000字節(jié),mmap函數(shù)從一個(gè)文件的起始位置開(kāi)始狡逢,映射15000字節(jié)到虛擬內(nèi)存中宁舰,即映射大小超過(guò)了原始文件的大小。

分析:由于文件的大小是5000字節(jié)奢浑,和情形一一樣蛮艰,其對(duì)應(yīng)的兩個(gè)物理頁(yè)。那么這兩個(gè)物理頁(yè)都是合法可以讀寫(xiě)的殷费,只是超出5000的部分不會(huì)體現(xiàn)在原文件中印荔。由于程序要求映射15000字節(jié),而文件只占兩個(gè)物理頁(yè)详羡,因此8192字節(jié)~15000字節(jié)都不能讀寫(xiě)仍律,操作時(shí)會(huì)返回異常。如下圖所示:

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

此時(shí):

(1)進(jìn)程可以正常讀/寫(xiě)被映射的前5000字節(jié)(0~4999)实柠,寫(xiě)操作的改動(dòng)會(huì)在一定時(shí)間后反映在原文件中水泉。

(2)對(duì)于5000~8191字節(jié),進(jìn)程可以進(jìn)行讀寫(xiě)過(guò)程窒盐,不會(huì)報(bào)錯(cuò)草则。但是內(nèi)容在寫(xiě)入前均為0,另外蟹漓,寫(xiě)入后不會(huì)反映在文件中炕横。

(3)對(duì)于8192~14999字節(jié),進(jìn)程不能對(duì)其進(jìn)行讀寫(xiě)葡粒,會(huì)報(bào)SIGBUS錯(cuò)誤份殿。

(4)對(duì)于15000以外的字節(jié),進(jìn)程不能對(duì)其讀寫(xiě)嗽交,會(huì)引發(fā)SIGSEGV錯(cuò)誤卿嘲。

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

分析:如果在映射建立之初盒让,就對(duì)文件進(jìn)行讀寫(xiě)操作梅肤,由于文件大小為0,并沒(méi)有合法的物理頁(yè)對(duì)應(yīng)邑茄,如同情形二一樣凭语,會(huì)返回SIGBUS錯(cuò)誤。

但是如果撩扒,每次操作ptr讀寫(xiě)前似扔,先增加文件的大小,那么ptr在文件大小內(nèi)部的操作就是合法的搓谆。例如炒辉,文件擴(kuò)充4096字節(jié),ptr就能操作ptr到 [ (char)ptr + 4095]的空間泉手。只要文件擴(kuò)充的范圍在1000個(gè)物理頁(yè)(映射范圍)內(nèi)黔寇,ptr都可以對(duì)應(yīng)操作相同的大小。這樣斩萌,方便隨時(shí)擴(kuò)充文件空間缝裤,隨時(shí)寫(xiě)入文件屏轰,不造成空間浪費(fèi)


其他實(shí)戰(zhàn)實(shí)例憋飞,我會(huì)補(bǔ)充在GitHub霎苗。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市榛做,隨后出現(xiàn)的幾起案子唁盏,更是在濱河造成了極大的恐慌,老刑警劉巖检眯,帶你破解...
    沈念sama閱讀 216,496評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件厘擂,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡锰瘸,警方通過(guò)查閱死者的電腦和手機(jī)刽严,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)避凝,“玉大人港庄,你說(shuō)我怎么就攤上這事∷∏” “怎么了鹏氧?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,632評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)佩谣。 經(jīng)常有香客問(wèn)我把还,道長(zhǎng),這世上最難降的妖魔是什么茸俭? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,180評(píng)論 1 292
  • 正文 為了忘掉前任吊履,我火速辦了婚禮,結(jié)果婚禮上调鬓,老公的妹妹穿的比我還像新娘艇炎。我一直安慰自己,他們只是感情好腾窝,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布缀踪。 她就那樣靜靜地躺著,像睡著了一般虹脯。 火紅的嫁衣襯著肌膚如雪驴娃。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,165評(píng)論 1 299
  • 那天循集,我揣著相機(jī)與錄音唇敞,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛疆柔,可吹牛的內(nèi)容都是我干的咒精。 我是一名探鬼主播,決...
    沈念sama閱讀 40,052評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼旷档,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼模叙!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起彬犯,我...
    開(kāi)封第一講書(shū)人閱讀 38,910評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎查吊,沒(méi)想到半個(gè)月后谐区,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,324評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡逻卖,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評(píng)論 2 332
  • 正文 我和宋清朗相戀三年宋列,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片评也。...
    茶點(diǎn)故事閱讀 39,711評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡炼杖,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出盗迟,到底是詐尸還是另有隱情坤邪,我是刑警寧澤,帶...
    沈念sama閱讀 35,424評(píng)論 5 343
  • 正文 年R本政府宣布罚缕,位于F島的核電站艇纺,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏邮弹。R本人自食惡果不足惜黔衡,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦伍绳、人聲如沸切端。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,668評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春硝岗,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背袋毙。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,823評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工型檀, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人听盖。 一個(gè)月前我還...
    沈念sama閱讀 47,722評(píng)論 2 368
  • 正文 我出身青樓胀溺,卻偏偏與公主長(zhǎng)得像裂七,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子仓坞,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • 一.管道機(jī)制(pipe) 1.Linux的fork操作 在計(jì)算機(jī)領(lǐng)域中背零,尤其是Unix及類Unix系統(tǒng)操作系統(tǒng)中,...
    Geeks_Liu閱讀 3,687評(píng)論 1 9
  • UNIX網(wǎng)絡(luò)編程第二卷進(jìn)程間通信對(duì)mmap函數(shù)進(jìn)行了說(shuō)明无埃。該函數(shù)主要用途有三個(gè):1徙瓶、將一個(gè)普通文件映射到內(nèi)存中,通...
    宇文黎琴閱讀 3,501評(píng)論 0 4
  • 轉(zhuǎn)自認(rèn)真分析mmap:是什么 為什么 怎么用 閱讀目錄mmap基礎(chǔ)概念mmap內(nèi)存映射原理mmap和常規(guī)文件操作的...
    扎Zn了老Fe閱讀 840評(píng)論 0 3
  • 前言 ??最近自定義控件的實(shí)踐相對(duì)多一些嫉称,看到了別人app上實(shí)現(xiàn)的效果就想自己動(dòng)手嘗試下侦镇,看自己能不能做到。本文是...
    yizhanzjz閱讀 1,191評(píng)論 0 1
  • 1 因?yàn)閷?xiě)出了《穿過(guò)大半個(gè)中國(guó)去睡你》等爆款詩(shī)歌,草根詩(shī)人余秀華火遍中國(guó)大江南北荔棉。因?yàn)槌雒竽致蛨?jiān)決和丈夫尹世平...
    滄桑不是傷閱讀 1,406評(píng)論 35 30