《Linux內核分析》期中總結

《Linux內核分析》期末總結

 5228 原創做品轉載請註明出處 《Linux內核分析》MOOC課程http://mooc.study.163.com/course/USTC-1000029000html

1、博客連接彙總

《Linux內核分析》第一週學習筆記:http://www.cnblogs.com/20135228guoyao/p/5215176.htmllinux

《Linux內核分析》第二週學習筆記:http://www.cnblogs.com/20135228guoyao/p/5243214.htmlgit

《Linux內核分析》第三週學習筆記:http://www.cnblogs.com/20135228guoyao/p/5270286.htmlgithub

《Linux內核設計與實現》第1、二章學習筆記:http://www.cnblogs.com/20135228guoyao/p/5274011.html算法

《Linux內核分析》第四周學習筆記:http://www.cnblogs.com/20135228guoyao/p/5284262.html安全

《Linux內核設計與實現》第五章學習筆記:http://www.cnblogs.com/20135228guoyao/p/5297421.html網絡

《Linux內核分析》第五週學習筆記:http://www.cnblogs.com/20135228guoyao/p/5312936.html數據結構

《Linux內核設計與實現》第十八章學習筆記:http://www.cnblogs.com/20135228guoyao/p/5330236.html架構

《Linux內核分析》第六週學習筆記:http://www.cnblogs.com/20135228guoyao/p/5334985.html框架

《Linux內核設計與實現》第三章學習筆記:http://www.cnblogs.com/20135228guoyao/p/5340778.html

《深刻理解計算機系統》第七章學習筆記:http://www.cnblogs.com/20135228guoyao/p/5343928.html

《Linux內核分析》第七週學習筆記:http://www.cnblogs.com/20135228guoyao/p/5360264.html

《Linux內核分析》第八週學習筆記:http://www.cnblogs.com/20135228guoyao/p/5383322.html

《Linux內核設計與實現》第四章學習筆記:http://www.cnblogs.com/20135228guoyao/p/5384449.html

2、知識點總結

1. 計算機是如何工做的?

  • 存儲程序計算機工做模型:馮諾依曼體系結構
  • X86彙編基礎:CPU的寄存器(通用寄存器、段寄存器、標誌寄存器)、常見彙編指令、堆棧
  • 彙編一個簡單的C程序分析其彙編指令執行過程

2. 操做系統是如何工做的?

  • 函數調用堆棧
  • 藉助Linux內核部分源代碼模擬存儲程序計算機工做模型及時鐘中斷
  • 在mykernel基礎上構造一個簡單的操做系統內核
  • 三個法寶:
    • 存儲程序計算機:全部計算機基礎性的邏輯框架
    • 堆棧:高級語言的起點,函數調用須要堆棧機制
    • 中斷機制:多道系統的基礎,是計算機效率提高的關鍵

3. 構造一個簡單的Linux系統MenuOS

  • Linux內核源代碼簡介

•arch:支持不一樣的CPU的源代碼,其中的關鍵目錄包括:Documentation、drivers、firewall、fs、include等
•documentation:文檔目錄
•fs:文件系統
•init:內核啓動相關的代碼main.c、Makefile等基本都在該目錄中。(main.c中的start_ kernel函數是Linux內核啓動的起點,即初始化內核的起點)
•kernel:Linux內核核心代碼在kernel目錄中。
•lib:公用的庫文件
•mm:內存管理的代碼
•scripts:與腳本相關的代碼
•security:與安全相關的代碼
•sound目錄:與聲音相關的代碼
•tools目錄:與工具相關的代碼
•net:與網絡相關的代碼
•readme:介紹了什麼是Linux,Linux可以在哪些硬件上運行,如何安裝內核源代碼等
•……

 

  • 構造一個簡單的Linux系統

cd LinuxKernel/ 
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img

 

    • qemu命令是模擬內核啓動虛擬機,啓動Linux內核須要三個參數(kernel、initrd、root所在的分區和目錄),執行的第一個文件是init。
    • -kernel指明內核文件名
    • -initrd指明根文件系統,啓動其中的init文件。(menuOS源代碼編譯->init->rootfs.img)其中rootfs.img 爲根文件系統,目前只支持help、version、quit功能。
    • 啓動過程爲:啓動內核->啓動init->啓動進程
  • 跟蹤調試Linux內核的啓動過程

qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S

 

4. 扒開系統調用的三層皮(上)

(一)用戶態、內核態和中斷處理過程

  • 內核態:通常現代CPU有幾種指令執行級別。在高執行級別下,代碼能夠執行特權指令,訪問任意的物理地址,這種CPU執行級別對應着內核態
  • 用戶態:在相應的低級別執行狀態下,代碼的掌控範圍有限,只能在對應級別容許的範圍內活動
  • 中斷處理是從用戶態進入內核態的主要方式,中斷/int指令會在堆棧上保存一些寄存器的值:如用戶態棧頂地址、當前的狀態字、當時cs:eip的值(當前中斷程序的入口)

(二)系統調用概述

  • 系統調用是操做系統爲用戶態進程與硬件設備進行交互提供的一組接口
  • 系統調用概述和系統調用的三層皮:xyz(API)、system_ call(中斷向量)、sys_xyz(中斷向量對應的中斷服務程序)

(三)使用庫函數API和C代碼中嵌入彙編代碼觸發同一個系統調用

  • 使用庫函數API獲取系統當前時間
  • C代碼中嵌入彙編代碼的方法
  • 使用C代碼中嵌入彙編代碼觸發系統調用獲取系統當前時間

5. 扒開應用系統的三層皮(下)

(一)給MenuOS增長time和time-asm命令

rm menu -rf //強制刪除當前menu

git clone http://github.com/mengning/menu.git   //從新克隆新版本的menu

cd menu

ls

make rootfs //rootfs是事先寫好的一個腳本,自動編譯自動生成根文件系統,同時自動啓動MenuOS

vi test.c  //進入test.c文件

MenuConfig("getpid","Show Pid",Getpid);

MenuConfig("getpid_asm","Show Pid(asm)",GetpidAsm);  //在main函數中增長MenuConfig()

int Getpid(int argc,char *argv[]);

int GetpidAsm(int argc,char *argv[]); //增長對應的Getpid和GetpidAsm兩個函數

make rootfs //編譯

 

(二)使用gdb跟蹤系統調用內核函數sys_time

qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S 

gdb

(gdb)file linux-3.18.6/vmlinux

(gdb)target remote:1234 //鏈接到須要調試的MenuOS

(gdb)b start_kernel //設置斷點

(gdb)c //執行,可見程序在start_kernel處停下

list //可查看start_kernel的代碼

(gdb)b sys_time //sys_time是13號系統調用對應的內核處理函數,在該函數處設置斷點

(gdb)c

//若是這裏一直按n單步執行,會進入schedule函數。sys_time返回後進入彙編代碼處理,gdb沒法繼續進行追蹤
執行int 0x80後執行system call對應的代碼(system call不是函數,是一段特殊的彙編代碼,gdb還不能進行跟蹤)。

 

(三)系統調用在內核代碼中的工做機制和初始化

  • 系統調用在內核代碼中的工做機制和初始化
  • 簡化後便於理解的system_call僞代碼
  • 簡單瀏覽system_call和iret之間的主要代碼

6. 進程的描述和進程的建立

(一)進程的描述

進程描述符task_struct數據結構

 struct task_struct{ 
    volatile long state; //進程狀態,-1表示不可執行,0表示可執行,大於1表示中止 
    void *stack; //內核堆棧 
    atomic_t usage;  
    unsigned int flags; //進程標識符 
    unsigned int ptrace;  
    …… 
} 

(二)進程的建立

  • 進程的建立概覽及fork一個進程的用戶態代碼
  • 理解進程建立過程複雜代碼的方法
  • 瀏覽進程建立過程相關的關鍵代碼
  • 建立的新進程是從哪裏開始執行的
  • 使用gdb跟蹤建立新進程的過程
  • 道生一(start_ kernel...cpu_ idle),一輩子二(kernel_ init和kthreadd),二生三(即前面的0、一、2三個進程),三生萬物(1號進程是全部用戶態進程的祖先,2號進程是全部內核線程的祖先)
  • start_ kernel建立了cpu_ idle,也就是0號進程。而0號進程又建立了兩個線程,一個是kernel_ init,也就是1號進程,這個進程最終啓動了用戶態;另外一個是kthreadd。0號進程是固定的代碼,1號進程是經過複製0號進程PCB以後在此基礎上作修改獲得的
  • iret與int 0x80指令對應,一個是彈出寄存器值,一個是壓入寄存器的值
  • 若是將系統調用類比於fork();那麼就至關於系統調用建立了一個子進程,而後子進程返回以後將在內核態運行,而返回到父進程後仍然在用戶態運行

7. 可執行程序的裝載

(一)預處理、編譯、連接和目標文件的格式

  • 可執行程序是怎麼得來的

1. C源代碼(.c)通過編譯器預處理被編譯成彙編代碼(.asm)

2. 彙編代碼由彙編器被編譯成目標代碼(.o)

3. 將目標代碼連接成可執行文件(a.out)

4. 可執行文件由操做系統加載到內存中執行

 

  • 目標文件的格式ELF

    目標文件的三種形式:
    1. 可重定位文件.o,用來和其餘object文件一塊兒建立可執行文件和共享文件
    2. 可執行文件,指出應該從哪裏開始執行
    3. 共享文件,主要是.so文件,用來被連接編輯器和動態連接器連接

 

  • 靜態連接的ELF可執行文件和進程的地址空間

可執行文件加載到內存時:

1. 加載效果:將代碼段數據加載到內存中,再把數據加載到內存,默認從0x8048000地址開始加載
2. 啓動一個剛加載過可執行文件的進程時,可執行文件加載到內存以後執行的第一條代碼地址
3. 通常靜態連接會將全部代碼放在一個代碼段,而動態連接的進程會有多個代碼段

 

(二)可執行程序、共享庫和動態加載

  • 裝載可執行程序以前的工做

1. 通常執行一個程序的Shell環境,實驗中直接使用execve系統調用
2. Shell自己不限制命令行參數的個數,命令行參數的個數受限於命令自身
3. Shell會調用execve將命令行參數和環境參數傳遞給可執行程序的main函數

 

  • 裝載時動態連接和運行時動態連接應用舉例

1. 準備.so文件(在Linux下動態連接文件格式,在Windows中是.dll)
2. 編譯成.so文件

 

(三)可執行程序的裝載

  • 可執行程序的裝載相關關鍵問題分析

1. execve與fork是比較特殊的系統調用:

• execve用它加載的可執行文件把當前的進程覆蓋掉,返回以後就不是原來的程序而是新的可執行程序起點;
• fork函數的返回點ret_ from_fork是用戶態起點

2. sys_ execve內核處理過程:

• do_ execve -> do_ execve_ common -> exec_ binprm -> search_ binary_handler,最後根據文件頭部信息尋找對應的文件格式處理模塊

 

  • sys_execve的內部處理過程

    exec通常和fork調用,常規用法是fork出一個子進程,而後在子進程中執行exec,替換爲新的代碼。

  • 使用gdb跟蹤sys_execve內核函數的處理過程

1. 開始先更新內核,再用test_exec.c將test.c覆蓋掉
2. test.c文件中增長了exec系統調用,Makefile文件中增長了gcc -o hello hello.c -m32 -static
3. 啓動內核並驗證execv函數
4. 啓動gdb調試
5. 先停在sys_execve處,再設置其它斷點
6. 進入函數單步執行

 

  • 可執行程序的裝載與莊生夢蝶的故事

      莊周(調用execve的可執行程序)入睡(調用execve陷入內核),醒來(系統調用execve返回用戶態)發現本身是蝴蝶(被execve加載的可執行程序)

  • 淺析動態連接的可執行程序的裝載

8. 進程的切換和系統的通常執行過程

(一)進程切換的關鍵代碼switch_to分析

1. 進程進度與進程調度的時機分析

schedule()函數實現調度:

  • 中斷處理過程(包括時鐘中斷、I/O中斷、系統調用和異常)中,直接調用schedule(),或者返回用戶態時根據need_resched標記調用schedule()
  • 內核線程能夠直接調用schedule()進行進程切換,也能夠在中斷處理過程當中進行調度,也就是說內核線程做爲一類的特殊的進程能夠主動調度,也能夠被動調度
  • 用戶態進程沒法實現主動調度,僅能經過陷入內核態後的某個時機點進行調度,即在中斷處理過程當中進行調度

2. 進程上下文切換相關代碼分析

  • 爲了控制進程的執行,內核必須有能力掛起正在CPU上執行的進程,並恢復之前掛起的某個進程的執行,這叫作進程切換、任務切換、上下文切換
  • 掛起正在CPU上執行的進程,與中斷時保存現場是不一樣的,中斷先後是在同一個進程上下文中,只是由用戶態轉向內核態執行
  • 進程上下文包含了進程執行須要的全部信息
  • schedule()函數選擇一個新的進程來運行,並調用context_ switch進行上下文的切換,這個宏調用switch_ to來進行關鍵上下文切換

(二)Linux系統的通常執行過程

1. Linux系統的通常執行過程分析

最通常的狀況:正在運行的用戶態進程X切換到運行用戶態進程Y的過程:

1. 正在運行的用戶態進程X
2. 發生中斷——save cs:eip/esp/eflags(current) to kernel stack,then load cs:eip(entry of a specific ISR) and ss:esp(point to kernel stack).
3. SAVE_ALL //保存現場
4. 中斷處理過程當中或中斷返回前調用了schedule(),其中的switch_to作了關鍵的進程上下文切換
5. 標號1以後開始運行用戶態進程Y(這裏Y曾經經過以上步驟被切換出去過所以能夠從標號1繼續執行)
6. restore_all //恢復現場
7. iret - pop cs:eip/ss:esp/eflags from kernel stack
8.繼續運行用戶態進程Y

 

2. Linux系統執行過程當中的幾個特殊狀況

1. 經過中斷處理過程當中的調度時機,用戶態進程與內核線程之間互相切換和內核線程之間互相切換,與最通常的狀況很是相似,只是內核線程運行過程當中發生中斷沒有進程用戶態和內核態的轉換
2. 內核線程主動調用schedule(),只有進程上下文的切換,沒有發生中斷上下文的切換,與最通常的狀況略簡略
3. 建立子進程的系統調用在子進程中的執行起點及返回用戶態,如fork
4. 加載一個新的可執行程序後返回到用戶態的狀況,如execve

 

3. 內核與舞女

1. 進程的地址空間一共有4G,其中0——3G是用戶態能夠訪問,3G以上只有內核態能夠訪問
2. 內核至關於出租車,能夠爲每個「招手」的進程提供內核態到用戶態的轉換。
3. 沒有進程須要「承載」的時候,內核進入idle0號進程進行「空轉」。當用戶進程有需求時,內核發生中斷,幫助用戶進程完成請求,而後再返回到用戶進程。就好像Taxi將用戶載了一圈以後又把用戶放下來。
4. 3G以上的部分就是這樣的「出租車」,是全部進程共享的,在內核態部分切換的時候就比較容易
5. 內核是各類中斷處理程序和內核線程的集合

 

(三)Linux系統架構和執行過程概覽

1. Linux操做系統架構概覽

2. 最簡單也是最複雜的操做——執行ls操做

3. 從CPU和內存的角度看Linux系統的執行

3、實驗總結

  在這幾周的實驗中我遇到的問題一共有三個:一是clone新版本的menu時顯示鏈接超時,解決方法爲在實驗樓環境中點擊「中止實驗」而後再從新進入輸入命令;二是編譯命令make rootfs結果顯示失敗,緣由是該路徑下原有的rootfs文件還存在,解決方法爲刪除原rootfs文件再執行編譯指令;三是偶爾target remote:1234這個命令沒法執行,顯示鏈接超時,解決方法爲從新執行qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S 指令。

4、學習總結

  經過八週的視頻學習和教材學習,我對於Linux內核有了初步的理解。在視頻中老師爲咱們講解了部分核心源碼,在課後做業裏也有嘗試着本身分析進程建立過程相關的關鍵代碼、可執行程序的裝載、進程上下文切換相關代碼、Linux系統架構和執行過程等,雖舉步維艱,但也收穫頗豐。Linux內核主要包括進程管理、內存管理、設備驅動、文件系統,從分析內核到了解整個系統是如何工做的、如何控制管理資源分配、進程切換並執行、各類策略和結構讓系統運行時更有效率等,在日漸深刻的學習中我愈發認識到內核源碼數據結構和算法的精妙之處。不過我也認識到自身的許多不足,在平時的學習中有一些內容僅僅是淺嘗輒止而沒有來得及細嚼慢嚥,掌握的不夠全面。並且在本身虛擬機環境中動手實踐的效果不理想。在往後的學習中我會定時查漏補缺,爭取有朝一日可以徹底領會內核源碼的精妙之處並能吸取借鑑用於其它方面。

相關文章
相關標籤/搜索