第二章
2.1、xv6程式碼結構
程式碼主要有三個部分組成:
kernel
: 我們可以ls kernel
,裡面包含了所有的核心檔案。因為XV6是一個單核心結構,這裡所有的檔案會被編譯成一個叫做kernel
的二進位制檔案,然後這個二進位制檔案會被執行在kernel mode
中。use
: 基本上是執行在user mode
的程式。這也是為什麼一個目錄稱為kernel
,另一個目錄稱為user
的原因。mkfs
: 它會建立一個空的檔案映象,我們會將這個映象存在磁碟上,這樣我們就可以直接使用一個空的檔案系統。
2.2、xv6如何切換不同模式(user/kernel..)
2.3、xv6啟動流程
從entry.S
開始啟動,在末尾跳轉到start.c
,
start.c
主要完成以下功能:
-
在暫存器
mstatus
中把之前的特權模式設定為監督者模式 -
透過把
main
的地址寫入暫存器mepc
來設定返回地址為main
, -
透過在頁表暫存器
satp
中寫入0來禁止監督者模式下的虛擬地址轉換 -
把所有中斷和異常委託給監督者模式
-
對時鐘晶片進行程式設計以產生時鐘中斷
-
在完成這些工作後,
start
透過呼叫mret
"返回"到監督者模式。之後程式會跳轉到main(kernel/main.c:11)
main.c
中完成以下設定:
consoleinit();
printfinit();
printf("\n");
printf("xv6 kernel is booting\n");
printf("\n");
kinit(); // physical page allocator
kvminit(); // create kernel page table
kvminithart(); // turn on paging
procinit(); // process table
trapinit(); // trap vectors
trapinithart(); // install kernel trap vector
plicinit(); // set up interrupt controller
plicinithart(); // ask PLIC for device interrupts
binit(); // buffer cache
iinit(); // inode table
fileinit(); // file table
virtio_disk_init(); // emulated hard disk
userinit(); // first user process
__sync_synchronize();
之後跳轉到userinit()
透過userinit()
中的initcode.s
跳轉到user/init.c
,(initcode
是一段翻譯好的機器碼,彙編格式見user/initcode.S
)
init.c
中呼叫執行sh.c
,啟動完成,
2.4 呼叫系統呼叫
的流程
在使用者態中,所有的系統介面都定義在user.h
中,透過usys.pl
將標頭檔案中的系統介面與真正的系統介面實現連線起來,核心態的系統呼叫介面定義在syscall.h
中,具體實現分佈在核心各個檔案中,透過陣列下標索引在syscall.c
中引用和呼叫
第三章
3.1 頁表工作流程總結
-
三級頁表
-
同一個物理頁可對映到多個虛擬地址
-
將根頁表實體地址放到satp暫存器中供cpu翻譯地址使用
-
核心和每個程序都有自己的頁表
3.2 地址空間
每一頁4KB,需要12位進行索引
虛擬地址
虛擬地址64位,高25位不使用,低12位時頁內索引,中間27位索引三級頁表,每一級頁表使用9位,所以每級頁表最多\(2^9 = 512\)個頁表項,又因為頁大小為4KB = \(2^{12}\)B,所以每一個頁表項大小為\(2^3B=8B=64bit\),
實體地址
核心頁表
程序頁表
程序頁表和虛擬地址空間之間的關係
每個程序的頁表是何時被建立的
虛擬地址空間何時被建立
那些事是CPU指令完成的,那些事是作業系統完成的?