Linux操作系統分析-課程學習總結報告


Linux操作系統分析-課程學習總結報告

  • 作業要求

    根據本課程所學內容總結梳理出一個精簡的Linux系統概念模型,最大程度統攝整頓本課程及相關的知識信息,模型應該是邏輯上可以運轉的、自洽的,並舉例某一兩個具體例子(比如讀寫文件、分配內存、使用I/O驅動某個硬件等)納入模型中驗證模型。

一.計算機系統工作的基本原理

計算機系統的基本工作原理需要從計算機和操作系統OS兩個方面來看, 總結起來就是計算機有"三大法寶"和操作系統有"兩把寶劍"

  1. 計算機的"三大法寶"

    計算機有"三大法寶", 分別是存儲程序計算機, 函數調用堆棧,和中斷.

    • 存儲程序計算機

      上圖很好的展示了存儲程序計算機的工作原理

      ​ 在這里,我們可以把CPU抽象成⼀個for循環,因為它總是在執⾏next instruction(下⼀條指令),然后從內存⾥取下⼀條指令來執⾏。從這個⻆度來看,內存保存指令和數據,CPU負責解釋和執⾏這些指令,它們通過總線連接起來。這⾥揭示了計算機可以⾃動化執⾏程序的原理。
      ​ 計算機的存儲體系中最關鍵的是內存(Memory),是存儲程序計算機模型中兩個關鍵部件之⼀。每個內存單元有⼀個地址(Address),內存地址是從0開始編號的整數,CPU通過地址找到相應的內存單元,取其中的指令或者讀寫其中的數據。⼀個地址所對應的內存單元不能存很多東⻄,只能存⼀個字節,指令或int、float等多字節的數據保存在內存中要占⽤連續的多個字節的地址,這種情況下指令或數據的地址是它所占內存單元的起始字節的地址。

    • 函數調用堆棧框架

      堆棧是C語言程序運行時必須的一個記錄調用路徑和參數的空間, 在這個堆棧上保存了函數調用框架, 傳遞的參數, 保存的返回地址, 函數內部 的局部變量等等參數.

      一個函數調用動作是通過call指令來完成的.一個完成的函數調用過程主要分為以下三步:

      • call XXX
        1. 執行call之前
        2. 執行call時, cs:eip的原來的值指向call指令的下一條指令, 該值被保存到棧頂, 然后cs:eip的值指向XXX的入口地址, 即函數返回時是返回到call指令的下一條指令, 在C語言代碼中代表着函數調用代碼的 下一條代碼的起始.
      • 進入XXX
        1. pushl %ebp
        2. movl %esp, %ebp
        3. XXX的函數體
      • 退出XXX
        1. movel %ebp, %esp
        2. popl %ebp
        3. ret

      上述過程大致可以用如下的圖來描述:

    • 中斷

      ​ 中斷最初⽤於避免CPU輪詢I/O設備,就緒狀態發⽣時讓I/O設備主動通過中斷信號通知CPU,⼤⼤提⾼了CPU在輸⼊輸出上的⼯作效率,這就是硬件中斷(外部中斷)。后來隨着中斷適⽤范圍擴⼤,⽐如解決機器運⾏過程出現的異常情況以及系統調⽤的實現等,這就產⽣了軟件中斷(內部中斷),也稱為異常,軟件中斷⼜分為故障(fault)和陷阱(trap)。
      ​ 簡而言之,在沒有中斷機制之前,計算機只能⼀個程序⼀個程序地執⾏,也就是批處理,⽽⽆法多個程序並發⼯作。有了中斷機制,CPU幫我們做了⼀件事情,就是當⼀個中斷信號發⽣時,CPU把當前正在執⾏的進程X的CS:RIP寄存器和RSP寄存器等都壓棧到了⼀個叫內核堆棧的地⽅,然后把CS:RIP指向⼀個中斷處理程序的⼊⼝,做保存現場的⼯作,然后去執⾏其他進程⽐如Y,等重新回來時再恢復現場,即恢復CS:RIP寄存器和RSP寄存器等到CPU上繼續執⾏原進程X。顯然中斷機制在計算機系統中發揮着關鍵作⽤。

      中斷在定義上可以分為傳統意義上的中斷(硬件中斷)和異常, 而

      中斷又分為:

      1. 可屏蔽中斷(Maskableinterrupt)
      2. 非屏蔽中斷(Nonmaskableinterrupt)

      異常又分為:

      1. 處理器探測異常

        由CPU執行指令時探測到一個反常條件時產生,如溢出、除0錯等.根據發生異常時保存在內核堆棧中的eip的值可以進一步分為:

        • 故障(fault)

          eip=引起故障的指令的地址. 這種異常通常可以糾正,處理完異常時,該指令被重新執行. 例如缺頁異常

        • 陷阱(trap)

          eip=隨后要執行的指令的地址

        • 異常終止

          eip=???.發生嚴重的錯誤。eip值無效,只有強制終止受影響的進程

      2. 編程異常

        這類異常通常由編程者發出的特定請求產生,通常由int類指令觸發, 例如系統調用

    關於中斷和異常的處理流程, 又可以分為硬件處理流程以及軟件處理流程

    假定: 內核已經初始化, CPU在保護模式下運行, 此時發生了中斷或者異常

    • 中斷和異常的硬件處理

      1. 確定與中斷和異常相關的聯的向量i
      2. 讀取idtr寄存器指向的IDT表中的第i項
      3. 從gdtr寄存器中獲得GDT的基地址, 並在GDT中查找, 以讀取IDT表項中的段選擇符標識的段描述符
      4. 確定中斷時由授權的發生源發出的
      5. 檢查是否發生了特權級的變化, 一般指的是是否由用戶態陷入了內核態, 如果由用戶態陷入了內核態, 需要使用新的特權級相關的堆棧
      6. 若發生的是故障, 用引起異常的指令地址修改cs和eip的值, 以使得這條指令在異常處理結束后能再次被執行
      7. 在棧中保存eflags, cs, 和eip的 內容
      8. 如果異常產生一個硬件出錯嗎, 則將他保存在棧中
      9. 裝載cs和eip寄存器, 其值分別是IDT表中第i項門中描述符的段選擇符和偏移量字段

      經過上述的硬件級處理過后, 內核態的堆棧會發生變化, 但是由用戶態進入中斷和由內核態進入中斷又不太一樣, 如下圖所示:

    • 中斷和異常的軟件級處理

      1. 在內核態堆棧中保存大多數寄存器的內容, 進一步保存上下文, 也就是pt_regs數據結構中描述的相關寄存器
      2. 調用C語言函數
      3. 通過ret_from_exception()從異常處理程序退出

      經過上述處理之后, 進程堆棧進一步發生了變化:


      至此, 進程上下文的保存工作完成.

  2. 計算機有"兩大寶劍"

    計算有"兩大寶劍", 分別是中斷上下文和進程上下文

    • 進程上下文

      當一個進程在執行時,CPU的所有寄存器中的值、進程的狀態以及堆棧中的內容被稱 為該進程的上下文。當內核需要切換到另一個進程時,它需要保存當前進程的 所有狀態,即保存當前進程的上下文,以便在再次執行該進程時,能夠必得到切換時的狀態執行下去。在LINUX中,當前進程上下文均保存在進程的任務數據結 構中。在發生中斷時,內核就在被中斷進程的上下文中,在內核態下執行中斷服務例程。但同時會保留所有需要用到的資源,以便中繼服務結束時能恢復被中斷進程 的執行。

    • 中斷上下文

      “ 中斷上下文”,其實可以看作就是硬件傳遞過來的這些參數和內核需要保存的一些其他環境(主要是當前被打斷執行的進程環境).需要注意的是, 中斷上下文沒有自己的上下文環境, 它占用的是被中斷進程的上下文.

二.一個精簡的Linux系統概念模型

​ 假定現在內核已經初始化, CPU在保護模式下運行,且運行在用戶態. 此時發生了中斷或者異常, CPU首先回去讀取idtr寄存器指向的IDT表中的第i項並從gdtr寄存器中獲得GDT的基地址, 在GDT中查找, 以讀取IDT表項中的段選擇符標識的段描述符, 隨后在棧中保存eflags, cs, 和eip的 內容.接着, CPU裝載cs和eip寄存器. 然后進入中斷軟件級處理過程, 進程會切換到內核態並啟用內核態堆棧, 在內核態堆棧上保存原來的cs:eip值, 接着, CPU進一步保存上下文, 也就是pt_regs數據結構中描述的寄存器.這些都保存完了之后, 進入中斷處理例程. 處理完了之后, 檢查如果此時沒有新的優先級更高的中斷到來, 便恢復寄存器並返回到用戶態.

以用戶讀一個文件為例

​ 當用戶點擊打開文件時, 會調用庫函數中的read()函數, 而在這個函數中會觸發軟中斷指令, CPU正式陷入到內核態, 進程堆棧也會切換到相應的內核態堆棧.接着, 進入異常處理的硬件級流程, 在這個過程中, CPU在棧中保存eflags, cs, 和eip的 內容, 隨后進入異常處理的軟件級流程, 在這個過程中, CPU會進一步保存上下文. 與這個過程相對應的是: CPU讀取idtr寄存器指向的IDT表中的第128項,此時可以得到0x80中斷門, 通過這個中斷門CPU可以得到中斷處理例程. 接着, 中斷處理例程會到系統調用表中找到read()函數相對應的VFS虛擬文件系統中的系統調用sys_read()

到達VFS層次后, sys_read()會根據fd在進程打開文件表中找到相應的系統打開文件表(File數據結構), 然后執行系統打開文件表中的file operations中具體的操作.

文件打開之后, 將pt_regs數據結構中描述的寄存器以及cs:eip寄存器恢復, 此時進程又重新恢復到了用戶態, 並沿着read()函數的下一條語句繼續執行.上述具體的過程可以看看Linux文件系統的邏輯結構.如下圖,

三.心得體會&改進意見

  1. 心得體會

    當初之所以選這門課, 還是保證全面學習的態度, 希望能學一學操作系統底層的一些知識, 這樣以后無論我是從事上層編碼或者底層驅動的學習和排錯都有很大的裨益. 在這門課中, 我真正的了解到了Linux內核的運作流程, 包括Linux操作系統的"三大法寶"和兩大寶劍. 相較於408的操作系統書中的描述, 在這學期的學習過程中, 我對於操作系統內核的運行流程有了更上一層樓的理解, 尤其是在中斷異常的處理流程以及虛擬文件系統這一塊的知識, 對於以前看到的種種現象都有了一種撥雲見日的感覺.

  2. 改進意見

    首先, 謝謝老師的辛苦付出, 為我們帶來這這么精品的課程.

    在Linux這門課中, 了解Linux源碼是一件非常重要的事情. 希望在閱讀Linux源碼的同時能有更多的插畫或者是動畫演示 , 這樣更加便於理解整個內核的運作流程. 再者, 后面的課上的太快了, 部分內容只上了一部分, 很遺憾.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM