MIT 6.S081 2020 Lab6 Copy-on-Write Fork for xv6 個人全流程

文章目錄

    • 零、寫在前面
    • 一、Implement copy-on write
      • 1.1 說明
      • 1.2 實現
        • 1.2.1 延遲復制與釋放
        • 1.2.2 寫時復制


零、寫在前面

可以閱讀下 《xv6 book》 的第五章中斷和設備驅動

問題

在 xv6 中,fork() 系統調用會將父進程的整個用戶空間內存復制到子進程中。**如果父進程占用的內存較大,復制過程會非常耗時。更糟糕的是,這種復制往往是浪費的。**例如,在子進程中調用 fork() 后緊接著執行 exec(),會導致子進程丟棄復制來的內存,很可能其中大部分根本沒有被使用。另一方面,如果父子進程都使用了某個頁面,并且有一個或兩個要寫這個頁面,那就確實需要進行復制。

解決方案

**寫時復制(COW)**的 fork() 目標是推遲為子進程分配和復制物理內存頁面,直到它們真的被需要(如果有的話)。

COW 的 fork() 僅為子進程創建一個頁表,其中用戶內存的頁表項(PTE)指向父進程的物理頁面。COW 的 fork() 會將父子進程中所有用戶頁面的頁表項標記為不可寫(即清除可寫標志)。當任一進程嘗試寫這些 COW 頁面時,CPU 會觸發一個缺頁異常(page fault)。內核的缺頁異常處理程序會識別出這是 COW 的情況,為發生異常的進程分配一個新的物理頁面,復制原頁面的內容,然后修改該進程的頁表項指向新頁面,并將其標記為可寫。異常處理程序返回后,用戶進程就可以寫這個新復制的頁面了。

COW 的 fork() 也讓用戶內存的物理頁面釋放變得更復雜。因為一個物理頁面可能會被多個進程的頁表引用,只有當最后一個引用消失時,該頁面才能被釋放。

我們發現 COW 和 Lazy alloc 的思想其實是類似的。

記得切換到 cow 分支


一、Implement copy-on write

1.1 說明

你的任務是:在 xv6 內核中實現寫時復制的 fork()。當你的內核能夠成功執行 cowtestusertests 這兩個程序時,說明你完成了任務。

官網推薦的實現步驟:

  1. 修改 uvmcopy(),不要為子進程分配新頁面,而是將父進程的物理頁面映射到子進程中。清除父子兩者對應頁表項中的 PTE_W(可寫標志)。

  2. 修改 usertrap() 以識別缺頁異常。如果在 COW 頁面上發生缺頁異常,則使用 kalloc() 分配一個新頁面,將原頁面內容復制過去,并在當前進程的頁表中安裝新頁面,并設置 PTE_W

  3. 確保物理頁面只在最后一個引用消失時才釋放。你可以為每個物理頁面維護一個“引用計數”,記錄有多少個用戶頁表引用了該頁面。

    • kalloc() 分配頁面時,將引用計數設為 1。
    • fork() 讓子進程共享頁面時,將引用計數加 1。
    • 每當進程從頁表中移除頁面時,引用計數減 1。
    • 只有當引用計數為 0 時,kfree() 才能將頁面放回空閑列表。
    • 可以用一個定長的整數數組來存儲這些引用計數。你需要設計如何為頁面選擇索引以及數組大小。例如,你可以用頁面物理地址除以 4096 作為索引,數組元素個數可以設為 kalloc.ckinit() 放入空閑列表的最高物理地址除以 4096。
  4. 修改 copyout(),當遇到 COW 頁面時,采用和處理缺頁異常一樣的方案進行處理。

官網的一些提示:

  • 懶惰分配實驗應該讓你對實現 COW 所需的 xv6 代碼已有一定了解。但請不要在本實驗中基于你之前懶惰分配實驗的實現,而是從一個全新的 xv6 拷貝開始。
  • 你可以使用 RISC-V 頁表項中的 RSW(軟件保留位)來記錄每個 PTE 是否為 COW 映射。
  • usertests 測試了一些 cowtest 沒有覆蓋的情況,所以一定要確保兩個測試都能通過。
  • 有用的頁表標志宏和定義可以在 kernel/riscv.h 文件末尾找到。
  • 如果在處理 COW 缺頁異常時沒有可用內存,應該終止對應進程。

1.2 實現

官網給出的步驟其實已經讓我們有一個較為清晰的思路了,下面直接實現就行了。

實現邏輯分為兩部分:

  • 延遲復制與釋放
  • 寫時復制
1.2.1 延遲復制與釋放
  • 首先在 kalloc.c 中添加一個結構體保存每個頁面的引用計數
  • 為了保證并發安全,我們額外添加一個自旋鎖
  • 為了方便,提供一個 宏 用于計算引用計數數組索引,這也是官網給出的 頁面物理地址除以 4096 作為索引 的方法
// get cnt Array index
#define PA2IDX(p) (((uint64)(p)) / PGSIZE)struct {struct spinlock lock; // spinlock to ensure concurrency safetyint refCnt[PHYSTOP / PGSIZE]; // page ref cnt array
} pageRef;

在 kinit 中新增一行 pageRef 自旋鎖的初始化

void
kinit()
{initlock(&kmem.lock, "kmem");initlock(&pageRef.lock, "pageRef"); // initialize the lock for pageReffreerange(end, (void*)PHYSTOP);
}

在 kalloc 中 對新增頁面的引用計數初始化為1

void *
kalloc(void)
{// ...if(r) {memset((char*)r, 5, PGSIZE); // fill with junkpageRef.refCnt[PA2IDX(r)] = 1;}return (void*)r;
}

然后修改 kfree 的邏輯為只有當引用計數減為0 時才釋放

// Free the page of physical memory pointed at by v,
// which normally should have been returned by a
// call to kalloc().  (The exception is when
// initializing the allocator; see kinit above.)
void
kfree(void *pa)
{struct run *r;if(((uint64)pa % PGSIZE) != 0 || (char*)pa < end || (uint64)pa >= PHYSTOP)panic("kfree");acquire(&pageRef.lock); // acquire the lock for pageRef// not referred nowif(--pageRef.refCnt[PA2IDX(pa)] <= 0) {memset(pa, 1, PGSIZE);    // fill with junkr = (struct run*)pa;acquire(&kmem.lock);r->next = kmem.freelist;kmem.freelist = r;release(&kmem.lock);    }release(&pageRef.lock);
}
  • 我們還要添加新增引用計數的邏輯
  • 以及物理頁面復制的邏輯,以便后面調用
  • 記得在defs.h添加聲明WWWWW
    • 外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳
// if clone is needed
void *kTry_pgclone(void *pa) {acquire(&pageRef.lock);// if it only belongs to meif(pageRef.refCnt[PA2IDX(pa)] <= 1) {release(&pageRef.lock);return pa;}// apply for a new pageuint64 newpa = (uint64)kalloc();if(newpa == 0) {release(&pageRef.lock);return 0;}// copy old data to new onememmove((void*)newpa, (void*)pa, PGSIZE);// refCnt of old page decpageRef.refCnt[PA2IDX(pa)]--;release(&pageRef.lock);return (void*)newpa;
}// inc the refCnt
void kparef_inc(void *pa) {acquire(&pageRef.lock);pageRef.refCnt[PA2IDX(pa)]++;release(&pageRef.lock);
}
  • 然后就可以修改 vm.c 中的uvmcopy的復制操作 改為該物理頁面的引用次數+1
  • 以及為子進程新建頁表
int
uvmcopy(pagetable_t old, pagetable_t new, uint64 sz)
{pte_t *pte;uint64 pa, i;uint flags;for(i = 0; i < sz; i += PGSIZE){if((pte = walk(old, i, 0)) == 0)panic("uvmcopy: pte should exist");if((*pte & PTE_V) == 0)panic("uvmcopy: page not present");pa = PTE2PA(*pte);if (*pte & PTE_W) {*pte = (*pte & ~PTE_W) | PTE_COW;}flags = PTE_FLAGS(*pte);// create pte for son processif(mappages(new, i, PGSIZE, (uint64)pa, flags) != 0){goto err;}kparef_inc((void*)pa);}return 0;err:uvmunmap(new, 0, i / PGSIZE, 1);return -1;
}
1.2.2 寫時復制

即然要延遲寫操作,那么在缺頁異常的時候我們就需要知道這些頁面是否是 寫時復制的共享頁面,官網也是提醒我們,可以用 PTE 的保留位來標記。

  • 我們為COW 的缺頁錯誤編寫一個輔助函數
  • 復制一個新頁面
  • 設置為可寫,取消 COW標記
  • 取消舊頁面映射,添加新映射
  • 記得在 defs.h 中添加聲明
    • 外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳
int cow_Helper(pagetable_t pagetable, uint64 va)
{pte_t *pte;// get pte for vaif((pte = walk(pagetable, va, 0)) == 0)panic("uvmcowcopy: walk");uint64 pa = PTE2PA(*pte);uint64 newpa;// clone pageif((newpa = (uint64)kTry_pgclone((void*)pa)) != 0){// set PTE_W and unset PTE_COWuint64 flags = (PTE_FLAGS(*pte) | PTE_W) & ~PTE_COW;// unmap but do not free uvmunmap(pagetable, PGROUNDDOWN(va), 1, 0);// map to new pgif(mappages(pagetable, va, 1, newpa, flags) == -1) {panic("uvmcowcopy: mappages");}return 0;}return -1;
}
  • 然后修改copyout 的邏輯
  • 如果發現是 cow 頁面,就調用 cow_Helper() 來處理。
int
copyout(pagetable_t pagetable, uint64 dstva, char *src, uint64 len)
{uint64 n, va0, pa0;pte_t *pte;struct proc *p = myproc();while(len > 0){va0 = PGROUNDDOWN(dstva);// cowif(va0 < p->sz && (pte = walk(pagetable, va0, 0))!=0 && *pte & PTE_V&& *pte & PTE_COW)cow_Helper(pagetable,va0);// ...
}
  • 以及usertrap 邏輯的修改
  • 針對 我們寫時復制的頁面錯誤單獨處理
void
usertrap(void)
{int which_dev = 0;if((r_sstatus() & SSTATUS_SPP) != 0)panic("usertrap: not from user mode");// send interrupts and exceptions to kerneltrap(),// since we're now in the kernel.w_stvec((uint64)kernelvec);struct proc *p = myproc();// save user program counter.p->trapframe->epc = r_sepc();if(r_scause() == 8){// system callif(p->killed)exit(-1);// sepc points to the ecall instruction,// but we want to return to the next instruction.p->trapframe->epc += 4;// an interrupt will change sstatus &c registers,// so don't enable until done with those registers.intr_on();syscall();} else if((which_dev = devintr()) != 0){// ok} else if (r_scause() == 13 || r_scause() == 15){pte_t *pte;uint64 va = r_stval();// is va valid?if (va >= p->sz)exit(-1);pte = walk(p->pagetable, va, 0);// valid and cow ?if (pte == 0 || (*pte & PTE_V) == 0 || (*pte & PTE_COW) == 0)exit(-1);// copy on write for cow pgif (cow_Helper(p->pagetable, va) == -1) {exit(-1);}} else {printf("usertrap(): unexpected scause %p pid=%d\n", r_scause(), p->pid);printf("            sepc=%p stval=%p\n", r_sepc(), r_stval());p->killed = 1;}if(p->killed)exit(-1);// give up the CPU if this is a timer interrupt.if(which_dev == 2)yield();usertrapret();
}

測試一下:

cowtest:

外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳

usertest:

外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳

官網還是寫的太詳細了(bushi

p->killed = 1;
}

if(p->killed)
exit(-1);

// give up the CPU if this is a timer interrupt.
if(which_dev == 2)
yield();

usertrapret();
}

**測試一下:****cowtest:**[外鏈圖片轉存中...(img-TxytPoKI-1748706565978)]**usertest:**[外鏈圖片轉存中...(img-kelfELYU-1748706565978)]官網還是寫的太詳細了(bushi

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

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

相關文章

xhr、fetch和axios

XMLHttpRequest (XHR) XMLHttpRequest 是最早用于在瀏覽器中進行異步網絡請求的 API。它允許網頁在不刷新整個頁面的情況下與服務器交換數據。 // 創建 XHR 對象 const xhr new XMLHttpRequest();// 初始化請求 xhr.open(GET, https://api.example.com/data, true);// 設置請…

電腦驅動程序更新工具, 3DP Chip 中文綠色版,一鍵更新驅動!

介紹 3DP Chip 是一款免費的驅動程序更新工具&#xff0c;可以幫助用戶快速、方便地識別和更新計算機硬件驅動程序。 驅動程序更新工具下載 https://pan.quark.cn/s/98895d47f57c 軟件截圖 軟件特點 簡單易用&#xff1a;用戶界面簡潔明了&#xff0c;操作方便&#xff0c;…

機器學習與深度學習06-決策樹02

目錄 前文回顧5.決策樹中的熵和信息增益6.什么是基尼不純度7.決策樹與回歸問題8.隨機森林是什么 前文回顧 上一篇文章地址&#xff1a;鏈接 5.決策樹中的熵和信息增益 熵和信息增益是在決策樹中用于特征選擇的重要概念&#xff0c;它們幫助選擇最佳特征進行劃分。 熵&#…

【Kotlin】數字字符串數組集合

【Kotlin】簡介&變量&類&接口 【Kotlin】數字&字符串&數組&集合 文章目錄 Kotlin_數字&字符串&數組&集合數字字面常量顯式轉換數值類型轉換背后發生了什么 運算字符串字符串模板字符串判等修飾符數組集合通過序列提高效率惰性求值序列的操…

oscp練習PG Monster靶機復現

端口掃描 nmap -A -p- -T4 -Pn 192.168.134.180 PORT STATE SERVICE VERSION 80/tcp open http Apache httpd 2.4.41 ((Win64) OpenSSL/1.1.1c PHP/7.3.10) |_http-server-header: Apache/2.4.41 (Win64) OpenSSL/1.1.1c PHP/7.3.10 | http-methods:…

近期知識庫開發過程中遇到的一些問題

我們正在使用Rust開發一個知識庫系統&#xff0c;遇到了一些問題&#xff0c;在此記錄備忘。 錯誤&#xff1a;Unable to make method calls because underlying connection is closed 場景&#xff1a;在docker中調用headless_chrome時出錯 原因&#xff1a;為減小鏡像大小&am…

Ubuntu 22.04 系統下 Docker 安裝與配置全指南

Ubuntu 22.04 系統下 Docker 安裝與配置全指南 一、前言 Docker 作為現代開發中不可或缺的容器化工具&#xff0c;能極大提升應用部署和環境管理的效率。本文將詳細介紹在 Ubuntu 22.04 系統上安裝與配置 Docker 的完整流程&#xff0c;包括環境準備、安裝步驟、權限配置及鏡…

C#獲取磁盤容量:代碼實現與應用場景解析

C#獲取磁盤容量&#xff1a;代碼實現與應用場景解析 在軟件開發過程中&#xff0c;尤其是涉及文件存儲、數據備份等功能時&#xff0c;獲取磁盤容量信息是常見的需求。通過獲取磁盤的可用空間和總大小&#xff0c;程序可以更好地進行資源管理、預警提示等操作。在 C# 語言中&a…

2025年- H56-Lc164--200.島嶼數量(圖論,深搜)--Java版

1.題目描述 2.思路 &#xff08;1&#xff09;主函數&#xff0c;存儲圖結構 &#xff08;2&#xff09;主函數&#xff0c;visit數組表示已訪問過的元素 &#xff08;3&#xff09;輔助函數&#xff0c;用遞歸&#xff08;深搜&#xff09;&#xff0c;遍歷以已訪問過的元素&…

詳細到用手撕transformer下半部分

之前我們討論了如何實現 Transformer 的核心多頭注意力機制&#xff0c;那么這期我們來完整地實現整個 Transformer 的編碼器和解碼器。 Transformer 架構最初由 Vaswani 等人在 2017 年的論文《Attention Is All You Need》中提出&#xff0c;專為序列到序列&#xff08;seq2s…

WPF事件處理器+x名稱空間

目錄 ?編輯 一、事件處理器知識點 1. XAML中的事件綁定 2. C#中的事件處理方法 3. 方法簽名解釋 4. 命名規范 工作流程 二、導入引用名稱空間 三、x名稱空間及其常用元素 &#xff08;1&#xff09;x名稱空間的由來和作用 &#xff08;2&#xff09;x名稱空間里都有…

Axure設計案例——科技感漸變線性圖

想讓數據變化趨勢展示告別枯燥乏味&#xff0c;成為吸引觀眾目光的亮點嗎&#xff1f;快來看看這個Axure設計的科技感漸變線性圖案例&#xff01;科技感設計風格憑借炫酷的漸變色彩打破傳統線性圖的單調&#xff0c;營造出一種令人過目難忘的視覺體驗。每一條線條都仿佛是流動的…

Git全流程操作指南

Git全流程操作指南 一、Git 環境配置 1. 安裝 Git Windows&#xff1a;下載 Git for Windows macOS&#xff1a;brew install git Linux&#xff1a; sudo apt-get update && sudo apt-get install git # Debian/Ubuntu sudo yum install git …

AI與軟件工程結合的未來三年發展路徑分析

基于對數字化、制造業、工業、零售業等行業的系統調研&#xff0c;以及微軟、谷歌、阿里、華為等大廠的實踐案例&#xff0c;我們可以預見未來三年AI與軟件工程結合將呈現以下發展路徑和趨勢。 一、技術應用維度 1. AI輔助編程工具全面普及 未來三年&#xff0c;AI輔助編程工…

tiktoken學習

1.tiktoken是OpenAI編寫的進行高效分詞操作的庫文件。 2.操作過程&#xff1a; enc tiktoken.get_encoding("gpt2") train_ids enc.encode_ordinary(train_data) val_ids enc.encode_ordinary(val_data) 以這段代碼為例&#xff0c;get_encoding是創建了一個En…

DeepSeek 賦能文化遺產數字化修復:AI 重構千年文明密碼

目錄 一、引言二、文化遺產數字化修復概述2.1 文化遺產數字化修復的意義2.2 傳統數字化修復方法與局限 三、DeepSeek 技術剖析3.1 DeepSeek 技術原理與核心優勢3.2 相比其他技術的獨特之處 四、DeepSeek 在文化遺產數字化修復中的應用4.1 破損文物的智能修復4.2 文化遺產的虛擬…

leetcode題解513:找樹左下角的值(遞歸中的回溯處理)!

一、題目內容&#xff1a; 題目要求找到一個二叉樹的最底層最左邊節點的值。具體來說&#xff0c;我們需要從根節點開始遍歷二叉 樹&#xff0c;找到最深的那層中的最左邊的節點&#xff0c;并返回該節點的值。因為要先找到最底層左側的值&#xff0c;所以我們選擇遍歷順序一定…

C#面試問題41-60

41. What is the Singleton design pattern? Singleton is a class that only allows creating a single instance of itselt. 單例設計模式是一個類&#xff0c;它只允許創建自己的單個實例。 構造函數防止他在單例類以外的地方被調用。 使用情景&#xff1a;need a sing…

筆記思考法

掌握麥肯錫流筆記術&#xff0c;對大家來說有以下幾種好處: 1) 可以將自己的思考可視化&#xff0c;使之變得更加清晰 2) 避免無用功 3) 經常能夠提出有創意的想法 4) 遇到問題時能夠及時找到解決辦法 5) 不管面對什么情況都能夠找出真正有效的解決辦法 為什么僅僅通過改變使用…

Rust 學習筆記:關于閉包的練習題

Rust 學習筆記&#xff1a;關于閉包的練習題 Rust 學習筆記&#xff1a;關于閉包的練習題問題 1問題 2以下程序能否通過編譯&#xff1f;若能&#xff0c;輸出是&#xff1f;以下程序能否通過編譯&#xff1f;若能&#xff0c;輸出是&#xff1f;考慮該 API&#xff0c;空白處填…