偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

認(rèn)真分析mmap:是什么 為什么 怎么用

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

mmap基礎(chǔ)概念

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

  

由上圖可以看出,進(jìn)程的虛擬地址空間,由多個(gè)虛擬內(nèi)存區(qū)域構(gòu)成。虛擬內(nèi)存區(qū)域是進(jìn)程的虛擬地址空間中的一個(gè)同質(zhì)區(qū)間,即具有同樣特性的連續(xù)地址范圍。上圖中所示的text數(shù)據(jù)段(代碼段)、初始數(shù)據(jù)段、BSS數(shù)據(jù)段、堆、棧和內(nèi)存映射,都是一個(gè)獨(dú)立的虛擬內(nèi)存區(qū)域。而為內(nèi)存映射服務(wù)的地址空間處在堆棧之間的空余部分。

linux內(nèi)核使用vm_area_struct結(jié)構(gòu)來(lái)表示一個(gè)獨(dú)立的虛擬內(nèi)存區(qū)域,由于每個(gè)不同質(zhì)的虛擬內(nèi)存區(qū)域功能和內(nèi)部機(jī)制都不同,因此一個(gè)進(jìn)程使用多個(gè)vm_area_struct結(jié)構(gòu)來(lái)分別表示不同類型的虛擬內(nèi)存區(qū)域。各個(gè)vm_area_struct結(jié)構(gòu)使用鏈表或者樹形結(jié)構(gòu)鏈接,方便進(jìn)程快速訪問(wèn),如下圖所示:

 

vm_area_struct結(jié)構(gòu)中包含區(qū)域起始和終止地址以及其他相關(guān)信息,同時(shí)也包含一個(gè)vm_ops指針,其內(nèi)部可引出所有針對(duì)這個(gè)區(qū)域可以使用的系統(tǒng)調(diào)用函數(shù)。這樣,進(jìn)程對(duì)某一虛擬內(nèi)存區(qū)域的任何操作需要用要的信息,都可以從vm_area_struct中獲得。mmap函數(shù)就是要?jiǎng)?chuàng)建一個(gè)新的vm_area_struct結(jié)構(gòu),并將其與文件的物理磁盤地址相連。具體步驟請(qǐng)看下一節(jié)。

mmap內(nèi)存映射原理

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

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

1、進(jìn)程在用戶空間調(diào)用庫(kù)函數(shù)mmap,原型:void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);

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

3、為此虛擬區(qū)分配一個(gè)vm_area_struct結(jié)構(gòu),接著對(duì)這個(gè)結(jié)構(gòu)的各個(gè)域進(jìn)行了初始化

4、將新建的虛擬區(qū)結(jié)構(gòu)(vm_area_struct)插入進(jìn)程的虛擬地址區(qū)域鏈表或樹中

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

5、為映射分配了新的虛擬地址區(qū)域后,通過(guò)待映射的文件指針,在文件描述符表中找到對(duì)應(yīng)的文件描述符,通過(guò)文件描述符,鏈接到內(nèi)核“已打開文件集”中該文件的文件結(jié)構(gòu)體(struct file),每個(gè)文件結(jié)構(gòu)體維護(hù)著和這個(gè)已打開文件相關(guān)各項(xiàng)信息。

6、通過(guò)該文件的文件結(jié)構(gòu)體,鏈接到file_operations模塊,調(diào)用內(nèi)核函數(shù)mmap,其原型為:int mmap(struct file *filp, struct vm_area_struct *vma),不同于用戶空間庫(kù)函數(shù)。

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

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

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

注:前兩個(gè)階段僅在于創(chuàng)建虛擬區(qū)間并完成地址映射,但是并沒(méi)有將任何文件數(shù)據(jù)的拷貝至主存。真正的文件讀取是當(dāng)進(jìn)程發(fā)起讀或?qū)懖僮鲿r(shí)。

9、進(jìn)程的讀或?qū)懖僮髟L問(wèn)虛擬地址空間這一段映射地址,通過(guò)查詢頁(yè)表,發(fā)現(xiàn)這一段地址并不在物理頁(yè)面上。因?yàn)槟壳爸唤⒘说刂酚成洌嬲挠脖P數(shù)據(jù)還沒(méi)有拷貝到內(nèi)存中,因此引發(fā)缺頁(yè)異常。

10、缺頁(yè)異常進(jìn)行一系列判斷,確定無(wú)非法操作后,內(nèi)核發(fā)起請(qǐng)求調(diào)頁(yè)過(guò)程。

11、調(diào)頁(yè)過(guò)程先在交換緩存空間(swap cache)中尋找需要訪問(wèn)的內(nèi)存頁(yè),如果沒(méi)有則調(diào)用nopage函數(shù)把所缺的頁(yè)從磁盤裝入到主存中。

12、之后進(jìn)程即可對(duì)這片主存進(jìn)行讀或者寫的操作,如果寫操作改變了其內(nèi)容,一定時(shí)間后系統(tǒng)會(huì)自動(dòng)回寫臟頁(yè)面到對(duì)應(yīng)磁盤地址,也即完成了寫入到文件的過(guò)程。

注:修改過(guò)的臟頁(yè)面并不會(huì)立即更新回文件中,而是有一段時(shí)間的延遲,可以調(diào)用msync()來(lái)強(qiáng)制同步, 這樣所寫的內(nèi)容就能立即保存到文件里了。

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

對(duì)linux文件系統(tǒng)不了解的朋友,請(qǐng)參閱我之前寫的博文《從內(nèi)核文件系統(tǒng)看文件讀寫過(guò)程》,我們首先簡(jiǎn)單的回顧一下常規(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)核已打開文件集上的文件信息,從而找到此文件的inode。

3、inode在address_space上查找要請(qǐng)求的文件頁(yè)是否已經(jīng)緩存在頁(yè)緩存中。如果存在,則直接返回這片文件頁(yè)的內(nèi)容。

4、如果不存在,則通過(guò)inode定位到文件磁盤地址,將數(shù)據(jù)從磁盤復(fù)制到頁(yè)緩存。之后再次發(fā)起讀頁(yè)面過(guò)程,進(jìn)而將頁(yè)緩存中的數(shù)據(jù)發(fā)給用戶進(jìn)程。

總結(jié)來(lái)說(shuō),常規(guī)文件操作為了提高讀寫效率和保護(hù)磁盤,使用了頁(yè)緩存機(jī)制。這樣造成讀文件時(shí)需要先將文件頁(yè)從磁盤拷貝到頁(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ù)。寫操作也是一樣,待寫入的buffer在內(nèi)核空間不能直接訪問(wèn),必須要先拷貝至內(nèi)核空間對(duì)應(yīng)的主存,再寫回磁盤中(延遲寫回),也是需要兩次數(shù)據(jù)拷貝。

而使用mmap操作文件中,創(chuàng)建新的虛擬內(nèi)存區(qū)域和建立文件磁盤地址和虛擬內(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ù)拷貝,就從磁盤中將數(shù)據(jù)傳入內(nèi)存的用戶空間中,供進(jìn)程使用。

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

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

由上文討論可知,mmap優(yōu)點(diǎn)共有一下幾點(diǎn):

1、對(duì)文件的讀取操作跨過(guò)了頁(yè)緩存,減少了數(shù)據(jù)的拷貝次數(shù),用內(nèi)存讀寫取代I/O讀寫,提高了文件讀取效率。

2、實(shí)現(xiàn)了用戶空間和內(nèi)核空間的高效交互方式。兩空間的各自修改操作可以直接反映在映射的區(qū)域內(nèi),從而被對(duì)方空間及時(shí)捕捉。

3、提供進(jìn)程間共享內(nèi)存及相互通信的方式。不管是父子進(jìn)程還是無(wú)親緣關(guān)系的進(jìn)程,都可以將自身用戶空間映射到同一個(gè)文件或匿名映射到同一片區(qū)域。從而通過(guò)各自對(duì)映射區(qū)域的改動(dòng),達(dá)到進(jìn)程間通信和進(jìn)程間共享的目的。

     同時(shí),如果進(jìn)程A和進(jìn)程B都映射了區(qū)域C,當(dāng)A***次讀取C時(shí)通過(guò)缺頁(yè)從磁盤復(fù)制文件頁(yè)到內(nèi)存中;但當(dāng)B再讀C的相同頁(yè)面時(shí),雖然也會(huì)產(chǎn)生缺頁(yè)異常,但是不再需要從磁盤中復(fù)制文件過(guò)來(lái),而可直接使用已經(jīng)保存在內(nèi)存中的文件數(shù)據(jù)。

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

mmap相關(guān)函數(shù)

函數(shù)原型

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

返回說(shuō)明

成功執(zhí)行時(shí),mmap()返回被映射區(qū)的指針。失敗時(shí),mmap()返回MAP_FAILED[其值為(void *)-1],error被設(shè)為以下的某個(gè)值:

返回錯(cuò)誤類型

參數(shù)

start:映射區(qū)的開始地址

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

prot:期望的內(nèi)存保護(hù)標(biāo)志,不能與文件的打開模式?jīng)_突。是以下的某個(gè)值,可以通過(guò)or運(yùn)算合理地組合在一起

prot

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

flag

fd:有效的文件描述詞。如果MAP_ANONYMOUS被設(shè)定,為了兼容問(wèn)題,其值應(yīng)為-1

offset:被映射對(duì)象內(nèi)容的起點(diǎn)

相關(guān)函數(shù)

int munmap( void * addr, size_t len ) 

成功執(zhí)行時(shí),munmap()返回0。失敗時(shí),munmap返回-1,error返回標(biāo)志和mmap一致;

該調(diào)用在進(jìn)程地址空間中解除一個(gè)映射關(guān)系,addr是調(diào)用mmap()時(shí)返回的地址,len是映射區(qū)的大??;

當(dāng)映射關(guān)系解除后,對(duì)原來(lái)映射地址的訪問(wèn)將導(dǎo)致段錯(cuò)誤發(fā)生。 

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

一般說(shuō)來(lái),進(jìn)程在映射空間的對(duì)共享內(nèi)容的改變并不直接寫回到磁盤文件中,往往在調(diào)用munmap()后才執(zhí)行該操作。

可以通過(guò)調(diào)用msync()實(shí)現(xiàn)磁盤上文件內(nèi)容與共享內(nèi)存區(qū)的內(nèi)容一致。

 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從磁盤到虛擬地址空間的映射也必須是頁(yè)。

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的地址,不是文件本身,和文件句柄無(wú)關(guān)。同時(shí)可用于進(jìn)程間通信的有效地址空間不完全受限于被映射文件的大小,因?yàn)槭前错?yè)映射。

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

情形一:一個(gè)文件的大小是5000字節(jié),mmap函數(shù)從一個(gè)文件的起始位置開始,映射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)系如下圖所示:

  

此時(shí):

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

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

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

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

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

此時(shí):

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

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

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

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

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

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

但是如果,每次操作ptr讀寫前,先增加文件的大小,那么ptr在文件大小內(nèi)部的操作就是合法的。例如,文件擴(kuò)充4096字節(jié),ptr就能操作ptr ~ [ (char)ptr + 4095]的空間。只要文件擴(kuò)充的范圍在1000個(gè)物理頁(yè)(映射范圍)內(nèi),ptr都可以對(duì)應(yīng)操作相同的大小。

這樣,方便隨時(shí)擴(kuò)充文件空間,隨時(shí)寫入文件,不造成空間浪費(fèi)。

責(zé)任編輯:倪明 來(lái)源: 博客園
相關(guān)推薦

2017-04-14 10:11:37

閃存備份用例

2023-05-04 11:39:17

經(jīng)營(yíng)分析流量項(xiàng)目

2022-03-31 11:38:09

經(jīng)營(yíng)分析傳統(tǒng)企業(yè)運(yùn)營(yíng)商

2022-09-16 11:33:40

數(shù)據(jù)分析MVP

2021-09-26 05:45:52

邊緣分析數(shù)據(jù)分析網(wǎng)絡(luò)邊緣

2021-03-14 15:17:13

前端開發(fā)架構(gòu)

2018-02-07 00:00:00

數(shù)字化轉(zhuǎn)型

2020-07-28 08:09:02

領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)

2022-04-07 11:27:15

數(shù)字孿生VR系統(tǒng)AI

2018-08-02 15:24:05

RPCJava微服務(wù)

2020-11-06 13:25:38

React Concu

2023-04-04 07:15:01

2018-07-18 15:02:54

混合云云戰(zhàn)略安全

2018-07-09 14:44:27

存儲(chǔ)

2022-07-14 07:17:11

LXCDocker語(yǔ)言

2009-12-23 13:50:28

WPF是什么

2024-06-05 09:26:07

2024-12-23 13:00:00

MySQLMVCC數(shù)據(jù)庫(kù)

2024-01-09 13:43:05

CMSCRM

2009-09-10 17:53:50

LINQ是什么
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)