定義
虛擬內(nèi)存的目標(biāo)存儲(chǔ)器是磁盤,所以虛擬內(nèi)存區(qū)域是和磁盤中的文件對(duì)應(yīng)的。初始化虛擬內(nèi)存的內(nèi)容時(shí)倔叼,會(huì)把虛擬內(nèi)存區(qū)域和一個(gè)磁盤文件對(duì)象對(duì)應(yīng)起來竭宰,這個(gè)過程叫內(nèi)存映射空郊。被映射的對(duì)象稱為:共享對(duì)象。虛擬內(nèi)存可以映射的磁盤文件對(duì)象包括兩種:
- 一個(gè)普通的磁盤文件切揭,文件中的內(nèi)容被分成頁大小的塊狞甚,因?yàn)榘葱柽M(jìn)行頁面調(diào)度,只有真正需要讀取這些虛擬頁面時(shí)廓旬,才會(huì)交換到主存哼审。
- 一個(gè)匿名文件,匿名文件時(shí)內(nèi)核創(chuàng)建的,內(nèi)容全是二進(jìn)制0棺蛛,它相當(dāng)于一個(gè)占位符怔蚌,不會(huì)產(chǎn)生實(shí)際的磁盤流量。映射到匿名文件中的頁叫做請(qǐng)求二進(jìn)制零的頁旁赊。
作用
在多個(gè)進(jìn)程的虛擬內(nèi)存區(qū)域已和同一個(gè)共享對(duì)象建立映射關(guān)系的前提下桦踊,若其中一個(gè)進(jìn)程對(duì)該虛擬區(qū)域進(jìn)行寫操作,那么對(duì)于也把該共享對(duì)象映射到其自身虛擬內(nèi)存區(qū)域的進(jìn)程也是可見的终畅。
假設(shè)進(jìn)程1籍胯,2的虛擬內(nèi)存區(qū)域同時(shí)映射到同一個(gè)共享對(duì)象:
當(dāng)進(jìn)程1對(duì)其虛擬內(nèi)存進(jìn)行寫操作時(shí),也會(huì)被映射到進(jìn)程2的虛擬內(nèi)存區(qū)域中离福。
示意圖如下:
image.png
image.png
實(shí)現(xiàn)過程
- 內(nèi)存映射的實(shí)現(xiàn)過程主要時(shí)通過Linux系統(tǒng)下的調(diào)用函數(shù):mmap()
- 該函數(shù)的作用 = 創(chuàng)建虛擬內(nèi)存區(qū)域+與共享對(duì)象建立映射關(guān)系杖狼。
- 函數(shù)原型:
/**
* 函數(shù)原型
*/
void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
- 函數(shù)的使用:
/**
* 具體使用(用戶進(jìn)程調(diào)用mmap())
* 下述代碼即常見了一片大小 = MAP_SIZE的接收緩存區(qū) & 關(guān)聯(lián)到共享對(duì)象中(即建立映射)
*/
mmap(NULL, MAP_SIZE, PROT_READ, MAP_PRIVATE, fd, 0);
- 內(nèi)部原理:
步驟1:創(chuàng)建虛擬內(nèi)存區(qū)域
步驟2:實(shí)現(xiàn)地址映射關(guān)系,即:進(jìn)程的虛擬地址空間映射到共享對(duì)象妖爷。
注意:這個(gè)時(shí)候該虛擬地址并沒有任何數(shù)據(jù)關(guān)聯(lián)到文件中蝶涩,僅僅只是建立映射關(guān)系,當(dāng)其中一個(gè)進(jìn)程對(duì)虛擬內(nèi)存寫入數(shù)據(jù)時(shí)絮识,則真正實(shí)現(xiàn)了數(shù)據(jù)的可見绿聘。
特點(diǎn)
- 提高數(shù)據(jù)的讀,寫和傳輸?shù)男阅堋?br>
? 1.減少了數(shù)據(jù)拷貝次數(shù)
? 2.用戶空間和內(nèi)核空間的高效交互次舌。
? 3.用內(nèi)存讀寫代替I/O讀寫熄攘。 - 提高內(nèi)存利用率:通過虛擬內(nèi)存和共享對(duì)象。
應(yīng)用場(chǎng)景
1.實(shí)現(xiàn)內(nèi)存共享彼念,例如跨進(jìn)程通信挪圾。
2.提高數(shù)據(jù)讀/寫效率,如文件讀/寫操作逐沙。
實(shí)例講解
傳統(tǒng)的Linux系統(tǒng)文件操作流程如下:
- 1.用戶進(jìn)程發(fā)起讀文件請(qǐng)求
- 2.內(nèi)核通過查找進(jìn)程文件符表哲思,定位到內(nèi)核已打開文件集上的文件信息,從而找到此文件的inode酱吝。
-
3.inode在address_space上查找要請(qǐng)求的文件頁是否已經(jīng)存在緩存頁中也殖。如果存在,則直接返回這片文件頁的內(nèi)容务热。如果不存在忆嗜,則通過inode定位到文件磁盤地址,將數(shù)據(jù)從磁盤復(fù)制到頁緩存崎岂。之后再發(fā)起讀頁面過程捆毫,將頁面緩存中的數(shù)據(jù)返回給用戶進(jìn)程。
示意圖:
image.png
特點(diǎn):
- 常規(guī)文件操作為了提高讀寫效率&保護(hù)磁盤冲甘,使用了頁緩存機(jī)制绩卤。
- 需要兩次拷貝才能完成操作途样。
使用內(nèi)存映射的文件讀/寫操作:
?工作流程如下:
- 步驟1.創(chuàng)建虛擬映射區(qū)域
- 1.在當(dāng)前進(jìn)程的虛擬內(nèi)存空間中,尋找一段滿足要求大小的虛擬地址濒憋。
- 2.為此虛擬地址分配一個(gè)虛擬內(nèi)存區(qū)域
- 3.初始化該虛擬內(nèi)存區(qū)域
- 4.插入該虛擬內(nèi)存區(qū)域到進(jìn)程的虛擬內(nèi)存地址的鏈表/樹中何暇。
- 步驟2.實(shí)現(xiàn)地址映射關(guān)系
- 1.依次通過待映射文件指針,文件描述符和文件結(jié)構(gòu)體凛驮,最終調(diào)用內(nèi)核空間中系統(tǒng)調(diào)用函數(shù)mmap()
- 2.內(nèi)核空間的mmap()通過虛擬文件系統(tǒng)inode模塊定位到文件磁盤物理地址裆站。
3.通過remap_pfn_range()建立頁表,即實(shí)現(xiàn)了文件地址和虛擬地址區(qū)域的映射關(guān)系黔夭。
這兩個(gè)步驟創(chuàng)建虛擬空間和映射地址宏胯,但時(shí)并無將任何文件數(shù)據(jù)拷貝到主存;真正的數(shù)據(jù)拷貝時(shí)刻:當(dāng)進(jìn)程發(fā)起讀/寫操作時(shí)本姥。
- 步驟 3肩袍。進(jìn)程訪問該映射空間,實(shí)現(xiàn)文件內(nèi)容到物理內(nèi)存的數(shù)據(jù)拷貝婚惫。
- 1.進(jìn)程的讀/寫操作訪問虛擬地址空間這一段映射地址氛赐。
- 2.若進(jìn)程通過寫操作改變了其內(nèi)容,一定時(shí)間后系統(tǒng)會(huì)自動(dòng)回寫臟頁面到對(duì)應(yīng)的磁盤地址先舷,即完成了寫到文件的過程鹰祸。(修改的臟頁面不會(huì)立即更新,而是又延遲密浑,但是可通過msync()來強(qiáng)制同步,此時(shí)粗井,所寫的內(nèi)容就能立即保存到文件尔破。)
示意圖:
特點(diǎn):直接通過映射進(jìn)行交互,數(shù)據(jù)拷貝的次數(shù)只有一次浇衬,文件讀取的效率高懒构,可實(shí)現(xiàn)高效大規(guī)模數(shù)據(jù)傳輸。
跨進(jìn)程通信
使用傳統(tǒng)的跨進(jìn)程通信
工作流程:
- 1.發(fā)送進(jìn)程通過系統(tǒng)調(diào)用耘擂,將需要發(fā)送的數(shù)據(jù)拷貝到Linux進(jìn)程的內(nèi)核空間中的緩存區(qū)中胆剧。(數(shù)據(jù)拷貝1次,通過copy_from_user())醉冤。
- 2.內(nèi)核服務(wù)程序秩霍,喚醒接收進(jìn)程的接收線程,通過系統(tǒng)調(diào)用將數(shù)據(jù)發(fā)送到接收進(jìn)程的用戶空間中蚁阳,最終完成數(shù)據(jù)的傳輸铃绒。
示意圖:
缺點(diǎn):
- 需要2次拷貝數(shù)據(jù),效率低下
- 接收數(shù)據(jù)的緩存要由接收方提高螺捐,但是接收方不知道到底要多大的緩存才滿足要求颠悬。
使用內(nèi)存映射跨進(jìn)程通信
工作流程:
- 1.創(chuàng)建一塊共享的接收緩存區(qū)矮燎。
- 2.實(shí)現(xiàn)地址映射關(guān)系
- 3.發(fā)送進(jìn)程將數(shù)據(jù)發(fā)送搭配自身的虛擬內(nèi)存區(qū)域,拷貝數(shù)據(jù)1次
- 4.由于發(fā)送進(jìn)程的虛擬內(nèi)存空間核接收進(jìn)程的虛擬內(nèi)存地址存在映射關(guān)系赔癌,故相當(dāng)于也發(fā)送到了接收進(jìn)程的虛擬內(nèi)存地址中诞外。實(shí)現(xiàn)跨進(jìn)程通信
示意圖:
優(yōu)點(diǎn):
- 數(shù)據(jù)只拷貝一次,用戶空間之間直接通過共享對(duì)象直接交互灾票。
- 為接收進(jìn)程分配了不確定的大小的接收緩存區(qū)峡谊。