5228 原創做品轉載請註明出處 《Linux內核分析》MOOC課程http://mooc.study.163.com/course/USTC-1000029000html
《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
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
跟蹤調試Linux內核的啓動過程
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S
(一)用戶態、內核態和中斷處理過程
(二)系統調用概述
(三)使用庫函數API和C代碼中嵌入彙編代碼觸發同一個系統調用
(一)給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還不能進行跟蹤)。
(三)系統調用在內核代碼中的工做機制和初始化
(一)進程的描述
進程描述符task_struct數據結構
struct task_struct{ volatile long state; //進程狀態,-1表示不可執行,0表示可執行,大於1表示中止 void *stack; //內核堆棧 atomic_t usage; unsigned int flags; //進程標識符 unsigned int ptrace; …… }
(二)進程的建立
(一)預處理、編譯、連接和目標文件的格式
可執行程序是怎麼得來的
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加載的可執行程序)
淺析動態連接的可執行程序的裝載
(一)進程切換的關鍵代碼switch_to分析
1. 進程進度與進程調度的時機分析
schedule()函數實現調度:
2. 進程上下文切換相關代碼分析
(二)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系統的執行
在這幾周的實驗中我遇到的問題一共有三個:一是clone新版本的menu時顯示鏈接超時,解決方法爲在實驗樓環境中點擊「中止實驗」而後再從新進入輸入命令;二是編譯命令make rootfs結果顯示失敗,緣由是該路徑下原有的rootfs文件還存在,解決方法爲刪除原rootfs文件再執行編譯指令;三是偶爾target remote:1234這個命令沒法執行,顯示鏈接超時,解決方法爲從新執行qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S 指令。
經過八週的視頻學習和教材學習,我對於Linux內核有了初步的理解。在視頻中老師爲咱們講解了部分核心源碼,在課後做業裏也有嘗試着本身分析進程建立過程相關的關鍵代碼、可執行程序的裝載、進程上下文切換相關代碼、Linux系統架構和執行過程等,雖舉步維艱,但也收穫頗豐。Linux內核主要包括進程管理、內存管理、設備驅動、文件系統,從分析內核到了解整個系統是如何工做的、如何控制管理資源分配、進程切換並執行、各類策略和結構讓系統運行時更有效率等,在日漸深刻的學習中我愈發認識到內核源碼數據結構和算法的精妙之處。不過我也認識到自身的許多不足,在平時的學習中有一些內容僅僅是淺嘗輒止而沒有來得及細嚼慢嚥,掌握的不夠全面。並且在本身虛擬機環境中動手實踐的效果不理想。在往後的學習中我會定時查漏補缺,爭取有朝一日可以徹底領會內核源碼的精妙之處並能吸取借鑑用於其它方面。