(3)從零開始的操作系統開發日記

所謂的日記,只不過是間隔快一週才發的冗長的資料堆砌orz


關於80386的硬件:

  1. 段頁式內存機制

    • 分段機制啓動、分頁機制未啓動:邏輯地址—>段機制處理—>線性地址=物理地址
    • 分段機制和分頁機制都啓動:邏輯地址—>段機制處理—>線性地址—>頁機制處理—>物理地址
    • 物理內存地址空間是處理器提交到總線上用於訪問計算機系統中的內存和外設的最終地址。一個計算機系統中只有一個物理地址空間。
    • 線性地址空間是80386處理器通過段(Segment)機制控制下的形成的地址空間。
  2. 段寄存器:

    • 全部都是16位,較8086多增加FS,GS作附加段
      標誌寄存器:
    • IF(Interrupt Flag):中斷允許標誌位,由CLI,STI兩條指令來控制;設置IF位使CPU可識別外部(可屏蔽)中斷請求,復位IF位則禁止中斷,IF位對不可屏蔽外部中斷和故障中斷的識別沒有任何作用;
    • IOPL(I/O Privilege Level):I/O特權級字段,它的寬度爲2位,它指定了I/O指令的特權級。如果當前的特權級別在數值上小於或等於IOPL,那麼I/O指令可執行。否則,將發生一個保護性故障中斷;【越小越高級】
    • NT(Nested Task):控制中斷返回指令IRET,它寬度爲1位。若NT=0,則用堆棧中保存的值恢復EFLAGS,CS和EIP從而實現中斷返回;若NT=1,則通過任務切換實現中斷返回。在ucore中,設置NT爲0。【在保護模式下,指示當前執行的任務嵌套於另一任務中。當任務被嵌套時,NT=1,否則NT=0。即ucore不使用嵌套式的中斷請求?】
  3. 關於雙向鏈表:
    爲了實現數據結構的通用性,採用了接口的思想,將雙向鏈表的結點加入到被管理的結構體當中,並內聯實現雙向鏈表的訪問操作,減少這些操作的函數調用時間。訪問成員則利用了很特別的技巧,下面稍微分析一下:

    struct S0{
        int member_data;
        int* list_node;
    }

上結構體在彙編中大概是這樣:

    DD member_data
    DD list_node

我們已知的是list_node的地址,則可以通過list_node-4求得member_data的地址。
如果我們的結構體有多個成員:

    struct S1{
        int member_data1;
        int member_data2;
        ....
        int member_datan;
        int* list_node;
    }

如何對敵?【都督臉】
我們不可能每創建一個用到雙向鏈表的數據結構就積累一個成員表,這多不科學,於是ucore使用了以下辦法:

    任意宿主數據結構中memberN成員的地址=(list_node的地址-list_node在宿主數據結構中的偏移量).memberN

用S1作爲例子來講,(list_node的地址-list_node在宿主數據結構中的偏移量)==this,所以可以用這種方法,在僅有list_node的情況下計算出其他成員的訪問地址。那麼問題來了,list_node的偏移量應該怎麼算?講到這裏,講義中的宏應該已經很好理解了:

#define offsetof(type, member)                                    \
((size_t)(&((type *)0)->member))

將0進行類型強制轉換,轉換成type型的結構體指針,然後訪問這個“指針”的member成員,就可以自動計算得到member的地址,又因爲宿主結構的地址的值爲0,所以這時候的&((type *)0)->member)即member在宿主數據結構【比如S1,S0】中的偏移值,而這個偏移值是不變的,所以在任意時刻可以通過O(1)時間算出其他成員的地址。
不過說實話,這代碼我也是看不懂,這種指針使用技巧真是神,原文的說法是gcc編譯器技術的技巧,真可怕。不過只要知道大概處理過程是什麼,還是能理解的。

附相關完整源碼:


//free_area是空閒塊管理結構,free_area.free_list是空閒塊鏈表頭
free_area_t free_area;
list_entry_t * le = &free_area.free_list;  //le是空閒塊鏈表頭指針
while((le=list_next(le)) != &free_area.free_list) { //從第一個節點開始遍歷
    struct Page *p = le2page(le, page_link); //獲取節點所在基於Page數據結構的變量
    ……
}

//宏:le2page
// convert list entry to page
#define le2page(le, member)              \
to_struct((le), struct Page, member)


//宏:to_struct()
/* Return the offset of 'member' relative to the beginning of a struct type */
#define offsetof(type, member)                         \
((size_t)(&((type *)0)->member))

//宏 offsetof()
/* *
 * to_struct - get the struct from a ptr
 * @ptr:    a struct pointer of member
 * @type:   the type of the struct this is embedded in
 * @member: the name of the member within the struct
 * */
#define to_struct(ptr, type, member)                         \
((type *)((char *)(ptr) - offsetof(type, member)))

日常吹水:
不知不覺已經29號了,暑假剩下1個月,時間好像已經很緊張了,然而我好想浪QWQ,上次大佬帶我去吃博多拉麪真心棒,第一次喝到可以稱謂“豬骨湯底”的湯,看母上生日有沒有空帶她去請他們吃好了【好貴,錢包君哭哭】

發佈了28 篇原創文章 · 獲贊 12 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章