《操作系統真象還原》-閱讀筆記(上)

第一章

配置bochs,進入bochs simulator後一直是黑屏,原來默認是調試模式,需要輸入C(continue)來讓調試繼續。

第二章

主講MBR及進入MBR前的步驟
1.實模式只能訪問1MB的內存空間。
2.BIOS在ROM中。
3.開機上電後CS:IP指向內存0xfff0,這裏有個跳轉語句,轉到fe05b纔是真正的BIOS程序,之後檢測內存,顯卡燈,建立數據結構,中斷向量表和填寫中斷例程。

第三章

主講實模式
段的大小統一64KB,段基址代表內存的起始,偏移地址代表段內偏移量。
section:節,只是爲了讓程序員在邏輯上將程序劃分爲幾個部分,僞指令
vstart:告訴編譯器以新的數字作爲後面數據的地址的起始值

第四章

進入保護模式
段描述符:專門用來描述一個內存段,大小爲8字節
這裏寫圖片描述
全局描述符(GDT):
1.相當於描述符的數組,可以用選擇子中提供的下標在全局描述符表中索引描述符。
2.位於內存中,需要GDTR寄存器指向他CPU才知道在哪。通過lgdt加載GDTR,指令格式是:lgdt48位內存數據,48位前16位是以字節爲單位的界限值,後32位是GDT的起始地址。2^16=65536,65536/8=8192,所以最多存儲8192個段或門。
3.在保護模式下由於段基址已經存入段描述符,段寄存器沒必要再存段基址了,所以段寄存器存入選擇子,通過選擇子在GDT中找到段描述符從而得到內存段起始地址和段界限值。選擇子低2位存儲了RPL(可以表示0、1、2、3四種特權級),第2位是TI位用來選擇是GDT還是LDT索引描述符。而選擇子的索引部分有13位,所以最多索引8192個段,和GDT存儲的數量相同


這裏寫圖片描述

注意:GDT中第0個段描述符是不可用的,原因是如果選擇子忘記初始化則值爲0,爲避免誤操作如果訪問第0個描述符處理器將發出異常。
局部描述符(LDT)
現代操作系統中很少使用LDT
CPU廠商建議每個任務的私有內存段都應該放到自己的段描述符表中,該表就是LDT,即每個任務都有自己的LDT,隨着任務切換,也要切換相應任務的LDT。下面是Linux0.11內核中描述符的關係
這裏寫圖片描述
從相應的內核代碼中,我們看到在task_struct的聲明中,有如下內容:

struct task_struct {
    ...
    struct desc_struct ldt[3];
    struct tss_struct tss;
};

這說明ldt是和每個task有關。每當需要創建新的process時,就需要在內存中把一塊相應的區域劃分給這個process的LDT。
(部分參考自http://blog.csdn.net/aotony_1988/article/details/50935897

第五章

先回憶下上章內容並解釋一些不清楚的地方。
一個進程的地址空間,從用戶的角度看,是由若干的段(segment)組成的,這些段可以分爲兩種:私有段(private)、共享段(shared)。cpu也是按照用戶的邏輯進行內存管理的(分段),Intel Pentium規定了每種段最多有8K個,每個segment最大4G。一個cpu對應有一個GDT(global descriptor table),該表詳細描述了shared segment,這個表爲所有進程共享的;一個process對應有一個LDT(local),該表詳細描述了該process的private segment,這個表是進程私有的。
我們的程序是處在一個個的Segment中的,無論是指令還是數據。我們在程序中打印出一個變量的地址其實是段內的偏移地址(32位),程序的16bit段號由操作系統分配管理,我們是看不見的,但是的確存在。
注意注意注意:以上的解釋中,爲簡單,沒有說明分頁

總之,SegmentSelector(16bit) + 段內Offset(32bit) = 一個32bit的地址值
其實這個就是虛擬地址。將這個地址轉換爲物理地址就需要用他到頁目錄表中找到頁表的地址,再到頁表中找到真實物理地址。
(部分參考自http://blog.csdn.net/yleek/article/details/8204393

  1. 如果存儲器採用基本分頁機制,那麼操作系統會爲每個進程或任務建立一個頁表(這個頁表可能是一級的也可能是多級的)。整個操作系統中有多個進程在運行,那麼系統就會有多個頁表。頁表在內存中的存儲位置由寄存器CR3給出。
  2. 如果存儲器採用基本分段機制,那麼操作系統會爲每個進程或任務建立一個段表(一般不用多級),用於記錄數據段、代碼段等各類段在內存中的具體位置。
  3. 如果採用段頁式結合的機制,那麼一般一個進程或任務,操作系統會給其建立一個段表,而段表中的每個段又會對應一個頁表,也就是說,段頁式機制的每個進程有一個段表,有多個頁表。
    對於典型的Linux系統而言,操作系統會維護一個全局描述符表(相當於系統的段表),全局描述符表中用於記錄系統任務和用戶任務的描述符,其中用戶任務的描述符又指向用戶任務的局部描述符表(相當於用戶任務的段表)。因此要說linux中的分段機制用的是一張大表,我個人認爲也是有道理的。

這本書裏採用的是第一種方案,一個進程一個頁表,這個頁表是二級的。將頁目錄表和頁表緊挨在一起,如下圖所示
這裏寫圖片描述

這裏將兩個表都存在內核空間的低端內存處,這樣通過cr3的物理地址可以馬上找到頁表(因爲內核空間是真實線性的,即虛擬地址和物理地址一樣)。每一個進程的頁表都放在一起,其實還有其他的放置方法可見此貼http://bbs.csdn.net/topics/390956135?page=1
快表
虛擬地址到物理地址轉換有一個緩存器,快表,存儲少量的轉換關係,能加快轉換速度。轉換時一般先查快表查不到再查頁表項。
啓動分頁流程
1:準備好目錄表和頁表
2:將頁表地址寫入控制寄存器cr3 (又稱爲頁目錄基址寄存器)
3:寄存器cr0的PG位置1
tips
在64位的Linux下,gcc 編譯 32 位程序需要添加參數 -m32 ,ld需要添加參數是 -m elf_i386。

第六章

本章講了一些函數調用 ,彙編和C語言混合編程等知識
1.cdecl調用,由調用者清理棧空間,即將棧頂往上加。
2.LINUX系統調用入口只有一個 0X80 中斷描述符表中的一項 具體調用子功能由eax指定。
3.調用函數,當輸入的參數小於等於 5 個時, Linux 用寄存器傳遞參數。當參數個數大於 5 個時,把參數按照順序放入連續的內存區域,並將該區域的首地址放到 ebx 寄存器。
tips
fatal error: sys/cdefs.h: No such file or directory|
Try these:
sudo apt-get purge libc6-dev
sudo apt-get install libc6-dev

In case of -m32:
sudo apt-get install libc6-dev-i386

源碼中LOADER_STRAT_SELECTOR一直在boot.inc裏定義位0x2 所以Loader.bin應放到硬盤第二個扇區

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章