經(jīng)過以前的學(xué)習(xí)湾碎,了解到了內(nèi)存的管理策略宙攻。不管是分頁,還是分段介褥,或者是頁表的管理策略座掘。這些都有一個(gè)共同的目標(biāo):將多個(gè)進(jìn)程都保存在內(nèi)存中,這一操作柔滔,都是為了保證在進(jìn)程啟動時(shí)溢陪,進(jìn)程處于內(nèi)存中。
但是虛擬內(nèi)存技術(shù)允許執(zhí)行進(jìn)程不必完全處于內(nèi)存廊遍。這就使得程序可以完全大于物理內(nèi)存,從而在開發(fā)的過程中贩挣,程序員不用擔(dān)心內(nèi)存的限制喉前。
基本概念
虛擬內(nèi)存:虛擬內(nèi)存可以是一種邏輯上擴(kuò)充物理內(nèi)存的技術(shù)。它會把進(jìn)程的邏輯地址空間進(jìn)行擴(kuò)展王财。(書中說的虛擬內(nèi)存卵迂,可以看做是假象的邏輯內(nèi)存,雖然在平常使用top 命令看到了虛擬內(nèi)存表示一些具體的物理實(shí)體绒净,但是而書上一直用虛擬內(nèi)存這個(gè)詞個(gè)人認(rèn)為不是很好 )见咒。
虛擬地址空間: 其實(shí)這里的虛擬地址空間 感覺還是可以用邏輯地址空間的概念來代替。它表示進(jìn)程在內(nèi)存中的邏輯地址范圍挂疆。
請求調(diào)頁
我們在把進(jìn)程加載到內(nèi)存的過程中改览, 并不是把進(jìn)程的所有執(zhí)行代碼都加載到內(nèi)存,而是在需要執(zhí)行的時(shí)候才加載進(jìn)內(nèi)存缤言,這種技術(shù)叫請求調(diào)頁宝当,常常用于虛擬內(nèi)存系統(tǒng)。
對于請求頁的邏輯內(nèi)存胆萧,頁面只有在程序執(zhí)行期間被請求時(shí)才被加載庆揩。因此,從未訪問的那些頁從不加載到物理內(nèi)存中。而這種交換的方式也被稱為 惰性交換订晌。
基本執(zhí)行過程:當(dāng)換入進(jìn)程時(shí)虏辫,調(diào)頁程序會猜測在該進(jìn)程被再次換出之前,會用到哪些頁锈拨。調(diào)頁程序不是調(diào)入整個(gè)進(jìn)程砌庄,而是把哪些要使用的頁調(diào)入內(nèi)存。從而減少交換時(shí)間推励,而且還能避免物理內(nèi)存空間的浪費(fèi)鹤耍。
具體實(shí)現(xiàn):在使用這種方案的時(shí)候,需要一定的硬件支持验辞。以區(qū)分內(nèi)存的頁面和磁盤的頁面稿黄。通常在頁面上會有一個(gè)保護(hù)位,它的最后一位就是用于提供這個(gè)功能的跌造。這個(gè)位是有效-無效位杆怕。
當(dāng)這個(gè)位被置為有效位時(shí), 相關(guān)的頁面是合法的壳贪,并且在內(nèi)存中陵珍。
當(dāng)這個(gè)位被置為無效位時(shí),頁面無效或者是有效但是在磁盤上违施。
當(dāng)進(jìn)程在執(zhí)行過程中的時(shí)候互纯,如果訪問內(nèi)存駐留的頁面時(shí),會一切順利磕蒲。但是當(dāng)訪問到尚未調(diào)入的頁面的時(shí)候留潦,對,標(biāo)記為無效的頁面會產(chǎn)生缺頁錯(cuò)誤辣往。分頁硬件在通過頁表轉(zhuǎn)換地址時(shí)兔院,會發(fā)現(xiàn)無效位被設(shè)置,從而陷入操作系統(tǒng)站削。
一般缺頁錯(cuò)誤的處理很簡單:
- 檢查這個(gè)進(jìn)程的內(nèi)部表坊萝,以確認(rèn)該引用是有效的還是無效的內(nèi)存訪問。
- 如果引用無效许起,那么終止進(jìn)程十偶。如果引用有效但是沒有調(diào)入內(nèi)存,那么現(xiàn)在就調(diào)入园细。
- 找到一個(gè)空閑幀扯键。
- 調(diào)度一個(gè)磁盤操作,以將所需頁面讀到剛分配的幀珊肃。
- 磁盤讀取完成荣刑,修改進(jìn)程內(nèi)部表和頁表馅笙。以指示該頁現(xiàn)在處于內(nèi)存中。
- 重新啟動被陷入中斷的指令厉亏。該進(jìn)程能訪問所需要的頁面董习,就好像原來他就在內(nèi)存中一樣。
交換空間
在進(jìn)程執(zhí)行過程中爱只,需要一個(gè)輔助設(shè)備皿淋,用于保存不在內(nèi)存中的那么頁面。這種外存通常為高速硬盤恬试,稱為交換設(shè)備窝趣,用于交換的這部分磁盤叫做交換空間。
請求調(diào)頁的中使用的交換空間的磁盤I/O 通常要快于文件系統(tǒng)的训柴。交換空間的文件系統(tǒng)更快哑舒,因?yàn)樗前锤蟮膲K來分配的。并且不采用文件查找和間接分配方法幻馁。
因此洗鸵,系統(tǒng)可以在進(jìn)程啟動時(shí),將整個(gè)文件映像復(fù)制到交換空間仗嗦,然后從交換空間執(zhí)行請求調(diào)頁膘滨,從而獲取到好的分頁吞吐量。
另一個(gè)選擇是開始時(shí)在文件系統(tǒng)進(jìn)行請求調(diào)頁稀拐,但是在置換頁面時(shí)將換出的頁面寫入交換空間火邓。這保證了后續(xù)的調(diào)頁都是從交換空間完成的。
寫時(shí)復(fù)制
在父進(jìn)程調(diào)用fork()創(chuàng)建子進(jìn)程時(shí)后德撬,我們曾說子進(jìn)程應(yīng)該創(chuàng)建一個(gè)父進(jìn)程地址空間的副本铲咨,復(fù)制屬于父進(jìn)程的頁面。然而考慮到了許多子進(jìn)程在創(chuàng)建之后立馬執(zhí)行了exec()函數(shù)替換了砰逻,所以復(fù)制父進(jìn)程的頁面可能沒有必要鸣驱。因此我們采用了寫時(shí)復(fù)制技術(shù)泛鸟。他通過允許父進(jìn)程和子進(jìn)程最初共享相同的頁面來進(jìn)行工作蝠咆。這些頁面是共享頁面,被標(biāo)記為寫時(shí)復(fù)制北滥,這意味著任何進(jìn)程寫入共享頁面刚操,那么就創(chuàng)建一個(gè)共享頁面的副本。
可以看到使用寫時(shí)復(fù)制技術(shù)再芋,僅復(fù)制任何一個(gè)進(jìn)程修改的頁面菊霜,所有未修改的頁面可以由父進(jìn)程和子進(jìn)程共享。
還要注意济赎,只有可以修改的頁面才需要標(biāo)記寫時(shí)復(fù)制鉴逞。不能修改的頁面可以父子共享(比如代碼段)记某。
在實(shí)現(xiàn)上,許多操作系統(tǒng)都為這類請求提供了一個(gè)空閑的頁面池(和個(gè)人認(rèn)為和內(nèi)存池差不多)构捡。當(dāng)進(jìn)程的堆椧耗希或者堆要進(jìn)行擴(kuò)展時(shí),或者有寫時(shí)復(fù)制的請求時(shí)勾徽,通常分配這里的空閑頁面滑凉。操作系統(tǒng)分配這些頁面通常采用按需填0的技術(shù)。以清理原來的內(nèi)存喘帚。
Linux 提供了 fork()的變種畅姊,vfork()。vfork()的操作不同于寫時(shí)復(fù)制的fork()吹由。它會將父進(jìn)程掛起若未,子進(jìn)程使用父進(jìn)程的地址空間。
由于vfork()不會發(fā)生寫時(shí)復(fù)制溉知,所以他的修改的頁面在父進(jìn)程中是可以看到的陨瘩。當(dāng)子進(jìn)程在創(chuàng)建后立即會被exec()的情況下,可以使用vfork()级乍。因?yàn)樗麄冇袕?fù)制頁面舌劳,可以高效的啟動新進(jìn)程。