深入理解計(jì)算機(jī)系統(tǒng) 第九章

虛擬內(nèi)存


虛擬尋址

CPU 通 過生成 一個(gè)虛 擬地址 (Virtual Address?VA) 來訪 問主存 ,這個(gè)虛擬 地址在 被送到 內(nèi)存之 前先轉(zhuǎn) 換成適 當(dāng)?shù)奈?理地址 。將一 個(gè)虛擬 地址轉(zhuǎn) 換為物 理地址的任務(wù) 叫做地 址翻譯 (address translation)? 就像異 常處理 一樣, 地址翻 譯需要 CPU 硬件和 操作系 統(tǒng)之間 的緊密 合作。 CPU 芯片 上叫做 內(nèi)存管 理單元 (Memory ManagementUnit,MMU) 的專用 硬件, 利用存 放在主 存中的 查詢表 來動(dòng)態(tài) 翻譯虛 擬地址 ,該表 的內(nèi)容 由操作系統(tǒng) 管理。

虛擬內(nèi) 存被組 織為一 個(gè)由存 放在磁 盤上的 JV 個(gè)連 續(xù)的字 節(jié)大小 的單元組成 的數(shù)組 。每字 節(jié)都有 一個(gè)唯 一的虛 擬地址 ,作為 到數(shù)組 的索引 。磁 盤上 數(shù)組的 內(nèi)容被緩存在 主存中 。和 存儲(chǔ) 器層次 結(jié)構(gòu)中 其他緩 存一樣 ,磁盤 (較 低層) 上的數(shù) 據(jù)被分 割成塊 ,這 些塊作 為磁盤 和主存 (較 高層) 之間 的傳輸 單元。 VM 系統(tǒng)通 過將虛 擬內(nèi)存 分割為 稱為虛擬頁 (Virtual Page, VP) 的大小 固定的 塊來處 理這個(gè) 問題。

在任 意時(shí)刻 ,虛 擬頁面 的集合 都分為 三個(gè)不 相交的 子集:

未分配的: VM 系統(tǒng)還 未分配 (或者 創(chuàng)建) 的頁。 未分配 的塊沒 有任何 數(shù)據(jù)和 它們相關(guān)聯(lián) ,因此 也就不 占用任 何磁盤 空間。

緩存的 :當(dāng)前 已緩存 在物理 內(nèi)存中 的已分 配頁。

未緩存的: 未緩存 在物理 內(nèi)存中 的已分 配頁。

VM 簡(jiǎn)化 了鏈接 和加載 、代碼 和數(shù)據(jù) 共享, 以及應(yīng) 用程序 的內(nèi)存 分配。


簡(jiǎn)化內(nèi) 存分配

虛擬 內(nèi)存為 向用戶 進(jìn)程提 供一個(gè) 簡(jiǎn)單的 分配額 外內(nèi)存 的機(jī)制 。當(dāng)一個(gè)運(yùn)行 在用戶 進(jìn)程中 的程序 要求額 外的堆 空間時(shí) ( 如調(diào)用 malloc 的結(jié)果 ), 操作系統(tǒng)分配 一個(gè)適 當(dāng)數(shù)字 (例如 個(gè)連 續(xù)的虛 擬內(nèi)存 頁面, 并且將 它們映 射到物 理內(nèi)存中任意 位置的 k個(gè)任 意的物 理頁面 。由 于頁 表工作 的方式 ,操 作系統(tǒng) 沒有必 要分配k個(gè) 連續(xù)的 物理內(nèi) 存頁面 。頁 面可以 隨機(jī)地 分散在 物理內(nèi) 存中。

內(nèi)存映射

Linux 通過 將一個(gè) 虛擬內(nèi) 存區(qū)域 與一個(gè) 磁盤上 的對(duì)象 (object) 關(guān) 聯(lián)起來 ,以初 始化這個(gè) 虛擬內(nèi) 存區(qū)域 的內(nèi)容 ,這 個(gè)過程 稱為內(nèi) 存映射 (memory mapping)? 虛擬 內(nèi)存區(qū) 域可以映射到 兩種類 型的對(duì) 象中的 一種:

1) Linux 文件 系統(tǒng)中 的普通 文件: 一個(gè)區(qū) 域可以 映射到 一個(gè)普 通磁盤 文件的 連續(xù)部分 ,例如 一個(gè)可 執(zhí)行目 標(biāo)文件 。文 件區(qū) (section) 被分 成頁大 小的片 ,每一 片包含 一 個(gè)虛擬頁 面的初 始內(nèi)容 。因 為按需 進(jìn)行頁 面調(diào)度 ,所 以這些 虛擬頁 面沒有 實(shí)際交 換進(jìn)人 物理內(nèi)存 ,直到 CPU 第一 次引用 到頁面 ( 即發(fā)射 一個(gè)虛 擬地址 ,落在 地址空 間這個(gè) 頁面的 范圍之內(nèi) )。 如 果區(qū)域 比文件 區(qū)要大 ,那么 就用零 來填充 這個(gè)區(qū) 域的余 下部分。

2) 匿名文 件:一 個(gè)區(qū)域 也可以 映射到 一個(gè)匿 名文件 ,匿名 文件是 由內(nèi)核 創(chuàng)建的 ,包含的全 是二進(jìn) 制零。 CPU 第一次 引用這 樣一個(gè) 區(qū)域內(nèi) 的虛擬 頁面時(shí) ,內(nèi)核 就在物 理內(nèi)存中找到 一個(gè)合 適的犧 牲頁面 ,如 果該 頁面被 修改過 ,就 將這個(gè) 頁面換 出來, 用二進(jìn) 制零覆蓋 犧牲頁 面并更 新頁表 ,將 這個(gè)頁 面標(biāo)記 為是駐 留在內(nèi) 存中的 。注意 在磁盤 和內(nèi)存 之間并沒有實(shí) 際的數(shù) 據(jù)傳送 。因 為這 個(gè)原因 ,映 射到匿 名文件 的區(qū)域 中的頁 面有時(shí) 也叫做 請(qǐng)求二進(jìn)制 零的瓦 (demand-zero page)。

無論 在哪種 情況中 旦一個(gè) 虛擬頁 面被初 始化了 ,它就 在一個(gè) 由內(nèi)核 維護(hù)的 專門的交 換文件 (swap file) 之間換 來換去 。交換 文件也 叫做交 換空間 (swap space) 或者交 換區(qū)域需 要意識(shí) 到的很 重要的 一點(diǎn)是 ,在任 何時(shí)刻 ,交 換空間 都限制 著當(dāng)前 運(yùn)行著的 進(jìn)程能 夠分配 的虛擬 頁面的 總數(shù)。

一個(gè)對(duì) 象可以 被映射 到虛擬 內(nèi)存的 一 個(gè)區(qū)域 ,要么 作為共 享對(duì)象 ,要 么作為 私有對(duì)象 。如 果一個(gè) 進(jìn)程將 一個(gè)共 享對(duì)象 映射到 它的虛 擬地址 空間的 一個(gè)區(qū) 域內(nèi), 那么這 個(gè)進(jìn)程對(duì)這個(gè) 區(qū)域的 任何寫 操作, 對(duì)于那 些也把 這個(gè)共享對(duì)象 映射到 它們虛 擬內(nèi)存 的其他 進(jìn)程而言 ,也是 可見的 。而且 ,這 些變化 也會(huì)反 映在磁 盤上的 原始對(duì) 象中。

另 一方面 ,對(duì) 于一個(gè) 映射到 私有對(duì) 象的區(qū) 域做的 改變, 對(duì)于其 他進(jìn)程 來說是 不可見的 ,并 且進(jìn)程 對(duì)這個(gè) 區(qū)域所 做的任 何寫操 作都不 會(huì)反映 在磁盤 上的對(duì) 象中。 一個(gè)映 射到共享對(duì) 象的虛 擬內(nèi)存 區(qū)域叫 做共享 區(qū)域。 類似地 ,也 有私有 區(qū)域。

私有 對(duì)象使 用一種 叫做寫 時(shí)復(fù)制 (copy-on-write) 的巧 妙技術(shù) 被映射 到虛擬 內(nèi)存中 。

動(dòng)態(tài)內(nèi)存分配

動(dòng)態(tài) 內(nèi)存分 配器維 護(hù)著一 個(gè)進(jìn)程 的虛擬 內(nèi)存區(qū)域, 稱為堆 (heap) 。

分配器 將堆視 為一組 不同大 小的塊 (block) 的集合來維護(hù) 。每 個(gè)塊 就是一 個(gè)連續(xù) 的虛擬 內(nèi)存片 (chunk),要么 是已分 配的, 要么是 空閑的 。已分 配的塊 顯式地保 留為供 應(yīng)用程 序使用 ??臻e 塊可用 來分配 ???閑塊保持 空閑, 直到它 顯式地 被應(yīng)用 所分配 。一個(gè) 已分配的塊保 持已分 配狀態(tài) ,直 到它被 釋放, 這種釋 放要么是應(yīng) 用程序 顯式執(zhí) 行的, 要么是 內(nèi)存分 配器自 身隱式執(zhí) 行的。


分 配器有 兩種基 本風(fēng)格 。兩 種風(fēng)格 都要求 應(yīng)用顯式地 分配塊 。

顯式 分配器 (explicit allocator), 要 求應(yīng)用 顯式地 釋放任 何已分 配的塊 。例如 , C 標(biāo)準(zhǔn)庫 提供一 種叫做 mall0C 程 序包的 顯式分 配器。 C 程序通 過調(diào)用 malloc 函數(shù)來.分配 一個(gè)塊 ,并通 過調(diào)用 free 函數(shù)來 釋放一 個(gè)塊。 C++ 中的符與 C 中的 malloc 和 f ree 相當(dāng)。

隱式 分配器 (implicit allocator)? 另 一方面 ,要求 分配器 檢測(cè)一 個(gè)已分 配塊何 時(shí)不再被程序 所使用 ,那么 就釋放 這個(gè)塊 。隱 式分配 器也叫 做垃圾 收集器 , 而自 動(dòng)釋放 未使用 的已分 配的塊 的過程 叫做垃 級(jí)收集 (garbage collection)?例如 ,諸如 Lisp? ML 以及 Java 之類 的高級(jí) 語言就 依垃 圾收集 來釋放 已分配的塊。

使用動(dòng)態(tài)內(nèi)存的原因:

程序 使用動(dòng) 態(tài)內(nèi)存 分配的 最重要 的原因是經(jīng)常 直到程 序?qū)嶋H 運(yùn)行時(shí) ,才 知道 某些數(shù)據(jù)結(jié)構(gòu) 的大小 。

隱式空 閑鏈表:

通過標(biāo)記來標(biāo)記出當(dāng)前塊是空閑還是使用,因 為空閑 塊是通 過頭部 中的大 小字段 隱含地 連接著的 。分 配器可 以通過 遍歷堆 中所有 的塊, 從而間 接地遍 歷整個(gè) 空閑塊 的集合 。

顯式空 閑鏈表:

明確通過某種數(shù)據(jù)結(jié)構(gòu)來連接空閑塊。

垃圾收集

垃圾 收集器 (garbage collector) 是一 種動(dòng)態(tài) 內(nèi)存分 配器, 它自動(dòng) 釋放程 序不再 需要的已 分配塊 。這些 塊被稱 為垃圾 (garbage) (因 此術(shù)語 就稱之 為垃圾 收集器 )。 自動(dòng)回 收堆存儲(chǔ) 的過程 叫做垃 圾收集 (garbage collection)? 在 一個(gè)支 持垃圾 收集的 系統(tǒng)中 ,應(yīng)用 顯式分配堆塊 ,但是 從不顯 示地釋 放它們 。

收集方式分兩種:

1.引用計(jì)數(shù)

2.可達(dá)性分析,書中介紹的這種

C 程序中常見的與內(nèi)存有關(guān)的錯(cuò)誤

間接引用壞指針

讀未 初始化的內(nèi)存

允許棧緩沖區(qū)溢出

假設(shè)指 針和它 們指向 的對(duì)象 是相同小的

造成錯(cuò) 位錯(cuò)誤

引 用指針 ,而不是它 所指向 的對(duì)象

誤解指 針運(yùn)算

引用 不存在 的變量

引 用空閑 堆塊中 的數(shù)據(jù)

引起內(nèi) 存泄漏

具體解釋查看書中內(nèi)容和示例

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 虛擬存儲(chǔ)器又叫做虛擬內(nèi)存,我們現(xiàn)在的操作系統(tǒng)普遍都支持了虛擬內(nèi)存,這樣做是因?yàn)槲覀兺瑫r(shí)運(yùn)行著太多的程序了,就目前我...
    唐魚的學(xué)習(xí)探索閱讀 5,258評(píng)論 1 25
  • 引言 深入理解計(jì)算機(jī)系統(tǒng),對(duì)我來說是部大塊頭。說實(shí)話,我沒有從頭到尾完完整整的全部看完,而是選擇性的看了一些我自認(rèn)...
    小敏紙閱讀 2,350評(píng)論 1 14
  • title: 深入理解計(jì)算機(jī)系統(tǒng)date: 2019-12-10 19:29:57tags: [計(jì)算機(jī)基礎(chǔ), 操作...
    劉佳闊閱讀 1,118評(píng)論 0 0
  • 久違的晴天,家長(zhǎng)會(huì)。 家長(zhǎng)大會(huì)開好到教室時(shí),離放學(xué)已經(jīng)沒多少時(shí)間了。班主任說已經(jīng)安排了三個(gè)家長(zhǎng)分享經(jīng)驗(yàn)。 放學(xué)鈴聲...
    飄雪兒5閱讀 7,822評(píng)論 16 22
  • 創(chuàng)業(yè)是很多人的夢(mèng)想,多少人為了理想和不甘選擇了創(chuàng)業(yè)來實(shí)現(xiàn)自我價(jià)值,我就是其中一個(gè)。 創(chuàng)業(yè)后,我由女人變成了超人,什...
    亦寶寶閱讀 2,007評(píng)論 4 1

友情鏈接更多精彩內(nèi)容