【MIT 6.S081】2020, 實驗記錄(6),Lab: Copy-on-Write Fork

目錄

    • Task: Implement copy-on write
      • step 1:對內存塊進行引用計數
      • step 2:`uvmcopy` 實現 fork 時將 parent 的物理頁映射到 child 中
      • step 3:在 `usertrap` 中增加對 page fault 的處理
      • 執行測試

官方說明:Lab: Copy-on-Write Fork for xv6

這個實驗是利用 page fault 實現操作系統的 Copy-On-Write 功能(COW)。

COW 的意思是說,當 fork 時,子進程不會完全拷貝父進程的整個內存空間,而是與父進程共享同一份物理內存,并在 page table 中記錄映射關系,當任意一個進程需要寫某塊內存時,會發生 page fault,page fault handler 會真正分配出一塊新的內存并重新建立映射,從而節省了大量的內存拷貝。

通過這個 lab,對 COW 有了更深的了解。

Task: Implement copy-on write

這個 task 的要求是實現 COW,并通過 cowtestusertests

step 1:對內存塊進行引用計數

由于多個進程可能會共享同一份物理內存,因此像 kfree() 這類函數不能隨意釋放一個物理內存,而是需要借助引用計數來決定能否真正釋放物理內存,所以我們需要實現對每個物理內存塊的引用計數。

kalloc.c 文件中,我們模仿 kmem 結構體寫一個 kref,用來對每個內存塊進行引用計數:

struct {struct spinlock lock;int counts[PHYSTOP / PGSIZE];
} kref;

kinit() 對 kref 進行初始化:

kinit

更改引用計數的函數:

// 增加對某個物理地址的引用計數
void
kref_inc(uint64 pa)
{acquire(&kref.lock);kref.counts[pa / PGSIZE]++;release(&kref.lock);
}// 減少對某個物理地址的引用計數
void
kref_dec(uint64 pa)
{acquire(&kref.lock);kref.counts[pa / PGSIZE]--;release(&kref.lock);
}// 獲取某個物理地址的引用計數
int
kref_count(uint64 pa)
{int count;acquire(&kref.lock);count = kref.counts[pa / PGSIZE];release(&kref.lock);return count;
}

然后修改 kalloc(),當新分配一個物理內存塊時,其引用計數應當為 1:

kalloc
然后是修改 kfree() 函數,當通過 kfree 想釋放一個內存塊時,需要檢查這個內存塊的引用計數,如果存在多個引用,只需要對引用計數 -1 即可,只有當沒有更多進程引用這個內存塊時,才能真正釋放它:

kfree

step 2:uvmcopy 實現 fork 時將 parent 的物理頁映射到 child 中

調用 fork 時,會使用 uvmcopy 來克隆一份 parent 的物理內存空間給 child,而我們為了實現 COW,在這里只需要共享同一個物理內存。

具體的做法就是,遍歷 parent 的整個內存空間,將其物理地址封裝成 PTE 放入 child 的 page table 中,同時需要將 parent 和 child 的 PTE 都改為只讀的,這樣之后任一進程在 write 時能夠發生 page fault 并進而完成實際的內存分配。

除了 PTE 需要設置為只讀的,還需要在 PTE 標記一下這是一個 COW page,從而與普通的只讀 page 進行區分,為此,需要使用 PTE 中的預留位作為 COW 標志位:

PTE_COW
修改 uvmcopy

uvmcopy
這一塊代碼對原有拷貝整個內存空間的代碼進行改造,實現遍歷整個內存空間,并獲取每塊內存的 PTE 和物理地址,對 PTE 的 flags 做了修改后,再將其添加到 child 的 page table 中。另外注意,需要增加對物理內存的引用計數,因為這里每個內存增加了一個 child 進行對其的引用。

step 3:在 usertrap 中增加對 page fault 的處理

當一個被標為 COW 的只讀 page 發生 page fault 時,我們需要對其進行 COW 的處理,為其分配一份新的物理內存,并修改 page table 中的映射,這樣當 page fault 被處理并恢復正常流程后,就可以對內存進行 write 了。

首先我們需要一個函數來判斷一個 page 是否為 COW page:

// 判斷是否為 cow page
// 是的話返回 1,否則返回 0
int is_cowpage(pagetable_t pagetable, uint64 va){if (va >= MAXVA)return 0;pte_t *pte = walk(pagetable, va, 0);if(pte == 0)return 0;if ((*pte & PTE_V) == 0)return 0;return (*pte & PTE_COW) != 0;
}

另外我們將處理 COW 導致的 page fault 的邏輯封裝為函數 handle_cow()

void* handle_cow(pagetable_t pagetable, uint64 va){va = PGROUNDDOWN(va);pte_t *pte = walk(pagetable, va, 0);if(pte == 0)return 0;uint64 pa = PTE2PA(*pte);if (pa == 0)return 0;// 根據 ref count 判斷是否需要分配新的物理頁if (kref_count(pa) == 1) {// 如果只存在一個引用,則直接修改 PTE 的 flags 并返回即可*pte |= PTE_W;  // 增加 PTE 的寫權限*pte &= ~PTE_COW;  // 清除 PTE 的 COW 標志return (void*)pa;}// 如果存在多個引用,則需要分配新的物理頁并拷貝舊頁面內容char *mem = kalloc();  // 分配物理內存if (mem == 0) {return 0;}memmove(mem, (void*)pa, PGSIZE);  // 拷貝舊頁面內容// 修改 va 的 mapping*pte = PA2PTE(mem) | ((PTE_FLAGS(*pte) | PTE_W) & (~PTE_COW));// 將舊物理頁的 ref count 減一kfree((void*)pa);return mem;
}

這里的易錯點:

  • 我們需要通過物理頁的 ref count 來決定處理 COW page fault 時,是否需要分配一個新的物理頁,因為當被標為 COW page 時,是兩個及以上的進程來共享同一個物理 page,當隨著有些進程因為 COW page fault 而創建了新的內存塊而減少 ref count,直到當 ref count 為 1 時,發生 COW page fault 的進程就不再需要分配新的物理頁,而是直接將之前的物理頁恢復可寫的 flag 即可。
  • 修改 PTE 的 flags 時,注意不要出錯

有了這兩個函數,我們就可以在 usertrap() 中增加對 page fault 的處理邏輯:

usertrap

在發生 page fault 時,會有三種可能:

  1. 訪問了非法內存頁
  2. 內存頁合法,但是權限不合適(比如在不可執行的 page 上執行指令等)
  3. 在 COW 的只讀 page 上執行 write 操作。這也是我們唯一能處理并恢復的 page fault

完成以上修改后,這個實驗就完成了。

執行測試

make qemu 后分別執行 cowtestusertests

cowtest
測試通過~

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/714506.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/714506.shtml
英文地址,請注明出處:http://en.pswp.cn/news/714506.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

IP地址工具,判斷IP是否在指定范圍內(支持ipv6)

常用方法,判斷一個ip是否在指定的ip范圍內,范圍可能包括起始ip范圍或者掩碼形式,無其它依賴, package com.yk.ip;import java.math.BigInteger; import java.net.InetAddress; import java.net.UnknownHostException; import jav…

操作系統-文件原理

目錄 一、磁盤 1.1 磁盤結構 1. 盤片: 2. 盤面: 3. 磁頭: 4. 磁道: 5. 扇區: 6. 磁道密度和扇區密度: 1.2 磁盤訪問 1. 尋道(Seeking): 2. 延遲旋轉&#xff…

C++進階-- map和set

關聯式容器 在前面,我們所學的vector、list、deque,這些都是序列容器,也就是底層為線性序列的數據結構。 而關聯式容器是C標準庫中的一種類別,用于存儲鍵值對(key-value pair),關聯式容器中的元…

vxe-table編輯單元格動態插槽slot的使用

業務場景:表格中只有特定某一行的的單元格可以編輯,列很多,為每個列寫個插槽要寫很多重復代碼,所以這里使用動態插槽,簡化代碼量。顯示編輯圖標,點擊編輯圖標隱藏。失去焦點保存調后臺接口。 解決辦法&…

Linux多線程服務端編程:使用muduo C++網絡庫 學習筆記 附錄C 關于Boost的看法

這是作者為電子工業出版社出版的《Boost程序庫完全開發指南》寫的推薦序,此處節選了作者對在C工程項目中使用Boost的看法。 最近一年(這篇文章寫于2010年8月)作者電話面試了數十位C應聘者。慣用的暖場問題是“工作中使用過STL的哪些組件&…

十行代碼開發一個AI應用

Semantic Kernal 簡介 Semantic Kernel (SK) is a lightweight SDK that lets you easily mix conventional programming languages with the latest in Large Language Model (LLM) AI "prompts" with templating, chaining, and planning capabilities out-of-the-…

關于vue中關于eslint報錯的問題

1 代碼保存的時候會自動將單引號報錯為雙引號 導致eslint報錯的問題, 解決思路: 在項目根目錄下新建一個.prettierrc.json文件 { “tabWidth”: 2,“useTabs”: false,“singleQuote”: true,“semi”: false} 2 關于報錯代碼的時候 出現尾隨逗號報錯…

Zabbix 系統告警“More than 75% used in the configuration cache”處理辦法

Zabbix系統報錯提示 Zabbix 系統告警“More than 75% used in the configuration cache”,看了一下意思是可用的配置緩存超過75%。 修改緩存大小 vim /etc/zabbix/zabbix_server.confesc : /CacheSize 找到配置 將64M改大一點,保存退出。 重啟zabbix…

WPF常用mvvm開源框架介紹 vue的mvvm設計模式鼻祖

WPF(Windows Presentation Foundation)是一個用于構建桌面應用程序的.NET框架,它支持MVVM(Model-View-ViewModel)架構模式來分離UI邏輯和業務邏輯。以下是一些常用的WPF MVVM開源框架: Prism Prism是由微軟…

代碼隨想錄算法訓練營 Day32 | LeetCode122.買賣股票的最佳時機II、LeetCode55. 跳躍游戲、LeetCode45.跳躍游戲II

LeetCode122.買賣股票的最佳時機II 那么這里面根據貪心思想: 1、在最低點時買入,如果該點比上一點價格更低,只有兩種情況:上一點為買入點,則此時更新買入點;上一點不是買入點,則此時將股票賣出…

物聯網的核心技術有哪些?

物聯網的核心技術有哪些? 說起物聯網的相關技術,涉及到很多,比如自動識別技術、傳感器技術、網絡通信技術、云計算等,但說到核心技術,我認為有以下6個。這6個核心技術分別是感知和識別技術、物聯網設備硬件、通信技術、數據處理技…

【MySQL】數據庫中常用的函數

目錄 聚合函數COUNT()函數的多種用法COUNT(*)COUNT(主鍵)COUNT(1)COUNT(常量)COUNT(非主鍵)COUNT(distinct(字段)) COUNT()函數小結 字符函數length(str)函數:獲取參數值的字節個數concat(str1,str2,...)函數:字符串拼接upper(str)、lower(str)函數:大小…

Linux高負載排查最佳實踐

在Linux系統中,經常會因為負載過高導致各種性能問題。那么如何進行排查,其實是有跡可循,而且模式固定。 本次就來分享一下,CPU占用過高、磁盤IO占用過高的排查方法。 還是那句話,以最佳實踐入手,真傳一句話…

1 開源鴻蒙OpenHarmony niobe407 STM32F407IGT6芯片輕型系統全量源碼4.1版本下載流程

開源鴻蒙OpenHarmony niobe407 STM32F407IGT6芯片輕型系統全量源碼4.1版本下載流程 作者將狼才鯨日期2024-02-27 一、前景提要 如果通過DevEco Marketplace網站獲取下載源碼的話,不全,有些板子下不到;OpenHarmony開發板列表,官方…

數據庫-第二/三章 關系數據庫和標準語言SQL【期末復習|考研復習】

前言 總結整理不易,希望大家點贊收藏。 給大家整理了一下計數據庫系統概論中的重點概念,以供大家期末復習和考研復習的時候使用。 參考資料是王珊老師和薩師煊老師的數據庫系統概論(第五版)。 文章目錄 前言第二、三章 關系數據庫和標準語言SQL2.1 關系2…

JVM原理-基礎篇

Java虛擬機(JVM, Java Virtual Machine)是運行Java應用程序的核心組件,它是一個抽象化的計算機系統模型,為Java字節碼提供運行環境。JVM的主要功能包括:類加載機制、內存管理、垃圾回收、指令解釋與執行、異常處理與安…

React react.fragment和<>的使用及區別

React一個常用的模式是組件返回多個元素。Fragment可以為你的子元素分組而不需要在DOM上為它們添加額外的節點。 Fragment 使用 render() {return (<React.Fragment> <ChildA /> <ChildB /> <ChildC /> </React.Fragment> );}短語法使用 這里…

虛擬機內存不夠用了?全流程操作Look一下?

虛擬機信息&#xff1a;操作系統&#xff1a;CentOS Linux 7 (Core)&#xff0c;用的是VMware Workstation 16 Pro 版本16.2.3 build-19376536&#xff1b;我的主機 Windows 10 Education, 64-bit (Build 22000.1817) 10.0.22000 前言&#xff1a;虛擬機用久了就會出現內存不足…

代碼隨想錄算法訓練營Day37|738.單調遞增的數字、968.監控二叉樹

738.單調遞增的數字 題目鏈接&#xff1a;738.單調遞增的數字 文檔鏈接&#xff1a;738.單調遞增的數字 視頻鏈接&#xff1a;貪心算法&#xff0c;思路不難想&#xff0c;但代碼不好寫&#xff01;LeetCode:738.單調自增的數字 C實現 class Solution { public:int monotoneIn…

Rocky Linux 安裝部署 Zabbix 6.4

一、Zabbix的簡介 Zabbix是一種開源的企業級監控解決方案&#xff0c;用于實時監測服務器、網絡設備和應用程序的性能和可用性。它提供了強大的數據收集、處理和可視化功能&#xff0c;同時支持事件觸發、報警通知和自動化任務等功能。Zabbix易于安裝和配置&#xff0c;支持跨平…