Linux內核學習總結

Linux內核學習總結html

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

前言:總結分爲三部分,第一部分對課程進行知識點總結,第二部分是學習心得體會,第三部分是附錄,對所有做業的博客連接,便於查詢與學習。程序員

1、《Linux內核分析》總結算法

(一)計算機是如何工做的安全

1.存儲程序計算機工做模型框架

2. X86CPU的寄存器:通用寄存器、段寄存器、標誌寄存器等。函數

3.計算機的彙編指令學習

(1)movl指令:spa

  • 寄存器尋址,寄存器模式,以%開頭的寄存器標示符。不和內存打交道,eax賦值給edx;
  • 當即尋址,把當即數直接放在寄存器,當即數是以$開頭的數值;
  • 直接尋址,直接訪問一個指定的內存地址的數據;
  • 間接尋址:將寄存器的值做爲一個內存地址來訪問內存;
  • 變址尋址:在間接尋址之時改變寄存器的數值。

注意:AT&T彙編格式與Intel彙編格式略有不一樣,Linux內核使用的是AT&T彙編格式。操作系統

(2)其餘指令

堆棧是向下增加的,有一個基址ebp指向堆棧棧底

  • pushl 壓棧,esp減4,把eax放入esp內存位置
  • popl 出棧,從堆棧棧頂取32位放到寄存器eax裏面,有兩個動做:首先間接尋址,把棧頂數值放到eax裏面,再把棧頂加4。
  • call 函數調用,把當前的eip壓棧,給eip賦新值;

注意:*是指這些指令是僞指令,程序員不能直接修改這些,即eip寄存器不能被直接修改,只能經過特殊指令間接修改。

4.將C代碼編譯成彙編代碼

(1)函數調用堆棧是由邏輯上多個堆棧疊加起來的

(2)函數的返回值默認使用eax寄存器存儲返回給上一級函數

(3)使用命令編譯成彙編代碼:gcc –S –o main.s main.c -m32

(二)操做系統是如何工做的

1. 堆棧——堆棧式C語言程序運行時必須的一個記錄調用路徑和參數的空間。包括:函數調用框架;傳遞參數;保存返回地址(如eax);提供局部變量空間

2. 堆棧寄存器:esp 堆棧指針和ebp 基址指針(在C語言中表示當前函數調用基址)

3. 堆棧操做:push棧頂指針減小4個字節(32位)和pop 棧頂指針增長4個字節

4. 參數傳遞與局部變量

(1)創建框架(至關於 call 指令)

push %ebp

movl %esp,%ebp

(2)拆除框架(至關於 ret 指令)

movl %ebp,%esp

pop  %ebp

函數返回時必定會拆除框架,創建和拆除是一一對應的。

(3)傳遞參數

在創建子函數的框架以前,局部變量的值保存在調用者堆棧框架中,因此在子函數框架創建以前能夠採用變址尋址的方式將變量值入棧。

!函數的返回值經過eax寄存器傳遞

(三)構造一個簡單的Linux系統MenuOS

1. 計算機三個法寶:存儲程序計算機、函數調用堆棧、中斷

2. 操做系統兩把寶劍:中斷上下文的切換(保存現場和恢復現場)以及進程上下文的切換

3. 總結:rest_init爲0號進程,一直存在。0號進程建立了1號進程kernel_init,還建立了其餘的服務線程。即道生一(start_kernel....cpu_idle),一輩子二(kernel_init和kthreadd),二生三(即前面0、1和2三個進程),三生萬物(1號進程是全部用戶態進程的祖先,2號進程是全部內核線程的祖先)。
Linux在無進程概念的狀況下將一直從初始化部分的代碼執行到start_kernel,而後再到其最後一個函數調用rest_init。

從rest_init開始,Linux開始產生進程,由於init_task是靜態製造出來的,pid=0,它試圖將從最先的彙編代碼一直到start_kernel的執行都歸入到init_task進程上下文中。在rest_init函數中,內核將經過下面的代碼產生第一個真正的進程(pid=1)。而後init_task變爲一個idle task,init_idle函數的第一個參數current就是&init_task,在init_idle中將會把init_task加入到cpu的運行隊列中,這樣當運行隊列中沒有別的就緒進程時,init_task(也就是idle task)將會被調用,它的核心是一個while(1)循環,在循環中它將會調用schedule函數以便在運行隊列中有新進程加入時切換到該新進程上。

(四)扒開系統調用的三層皮

1.用戶態和內核態

  • 用戶態:在相應的低執行狀態下,代碼的掌控範圍受到限制,只能在對應級別容許的範圍內活動
  • 內核態:在高執行級別下,代碼能夠執行特權指令,訪問任意的物理地址。

Intel x86 CPU有四種不一樣的執行級別0—3,Linux只是用了期中的0級和3級分別表示內核態和用戶態。

2.理解中斷處理的完整過程:中斷信號(int指令)完成:保存cs:eip的值、當前堆棧段棧頂和當前標誌,同時加載了當前中斷信號或是系統調用的相關聯的中斷服務入口到cs:eip裏面,把當前對戰段和esp也加載到CPU裏面。

SAVE ALL完成後若沒有發生調度,則接着執行RESTORE_ALL;若發生進程調度,則當前的狀態會暫時的保存在系統裏面,當下一次發生進程調度切換到當前進程時再接着執行完畢。

3. 系統調用的三個層次

系統調用的三個層次依次是:xyz函數(API)、system_ call(中斷向量)和 sys_ xyz(中斷服務程序)。

4. 總結:

在Linux系統中是經過激活0x80中斷來觸發系統調用的,須要調用的系統調用號實現賦值給eax存儲器,若是有傳入參數可賦值給ebx寄存器,若是多於1個則按順序賦值給ebx、ecx、edx、esi、edi、ebp,若是超過6個則經過指針變量指向另外一片堆棧區,若是無參數傳入則賦值爲0。
雖然Intel X86 CPU有4種執行級別0~3,可是在Linux系統中僅使用了0和3級,分別表示內核態和用戶態。一些涉及底層、硬件、核心的操做必須在內核態下才容許執行,爲操做系統程序和驅動程序專享,普通程序僅能執行在用戶態下。若是普通程序須要涉及內核態的操做,就須要經過系統調用來實現。這樣作的好處是屏蔽平臺相關操做下降了軟件開發難度,加強了系統安全性,使程序具備更好的移植性(Linux系統及其餘Unix系統遵循統一標準,系統調用基本同樣)。

(五)進程額管理和進程的建立

操做系統內核三大功能:進程管理(核心)、內存管理和文件系統。

1.Linux經過複製父進程來建立一個新進程,經過調用do_fork來實現。

2.Linux爲每一個新建立的進程動態地分配一個task_struct結構。

3.爲了把內核中的全部進程組織起來,Linux提供了幾種組織方式,其中哈希表和雙向循環鏈表方式是針對系統中的全部進程(包括內核線程),而運行隊列和等待隊列是把處於同一狀態的進程組織起來。

4.fork()函數被調用一次,但返回兩次。

(六)可執行程序的裝載

1.可執行程序過程:先預處理.cpp,在編譯成彙編代碼.s到目標代碼.o,再連接成可執行文件,加載到內存中執行。

2.可執行文件加載到內存中開始執行的第一行代碼,0X8048X00爲實際的入口。

3. 動態連接分爲可執行程序裝載時動態連接和運行時動態連接。

4. do_ execve調用do_ execve_ common,do_ execve_ common主要依靠exec_ binprm,其中重要的函數:search_binary_handler(bprm)。

(七)進程的切換和系統的通常執行過程

1. 進程調度算法——每一個進程對CPU、I/O等資源需求不同。

  • 第一種分類:I/O密集型(I/O-bound)和CPU密集型(CPU-bound)
  • 第二種分類:批處理進程;實時進程;交互式進程

2. 進程調度(schedule()函數實現)的時機:

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

注意:用戶態進程只能被動調度,內核線程是隻有內核態沒有用戶態的特殊進程。

3. 操做系統(任何計算機系統都包含一個基本的程序集合)有兩個目的:

  • 與硬件交互,管理全部的硬件資源;
  • 爲用戶程序(應用程序)提供一個良好的執行環境。

4. 本週主要理解Linux中進程調度與進程切換過程。進程調度是按必定的策略動態地把處理機分配給處於就緒隊列中的某一個進程,以使之執行。而進程切換是從正在運行的進程中收回處理器,而後再使待運行進程來佔用處理器。實質上就是把進程存放在處理器的寄存器中的中間數據找個地方存起來,從而把處理器的寄存器騰出來讓其餘進程使用。

2、學習感想與體會:

      做爲一名大三學生,從這學期還沒開學開始學習雲課堂的《Linux內核分析》,跟着孟老師一步一步瞭解Linux內核,到如今半個學期已通過去,又對課本《Linux內核設計與實現》的一些章節,搭配着視頻進行學習鞏固。時間感受過得很快。但同時也學到了很多知識。

      首先,上個學期開始接觸Linux,此次又經過這門課的學習,加深了對操做系統理論的理解,知道了Linux系統是如何工做的,如何經過代碼閱讀、調試去跟蹤驗證Linux系統的運行機制。其次,Linux做爲一個極其成功的操做系統,其內核紛繁複雜、博大精深,我我的學習起來也是至關困難。雖然完成了網課、看了課本,孟老師您也講得很幽默,但我仍是感受本身剛剛開始學習,並且須要在深刻挖掘的東西還有不少不少。

      經過半個學期的學習,我認爲重要的不是學習到了多少內核代碼(其實也很重要);但更重要重要的是學習方法,即從何處着手學習Linux內核,例如:如何調試內核、如何看懂內核中的彙編代碼,如何分析系統調用等。這也是我學習以後最大的收穫。總之,雖然網課結束了但學習尚未結束,繼續加油~

附錄——博客做業列表:

第一週學習總結——計算機是如何工做的:http://www.cnblogs.com/lhc-java/p/5217595.html

第二週學習總結——操做系統是如何工做的:http://www.cnblogs.com/lhc-java/p/5246779.html

第三週學習總結——構造一個簡單的Linux系統MenuOS:http://www.cnblogs.com/lhc-java/p/5271085.html

第四周學習總結——扒開系統調用的三層皮(上):http://www.cnblogs.com/lhc-java/p/5282826.html

第五週學習總結——扒開系統調用的三層皮(下):http://www.cnblogs.com/lhc-java/p/5326027.html

第六週學習總結——進程額管理和進程的建立:http://www.cnblogs.com/lhc-java/p/5340414.html

第七週學習總結——可執行程序的裝載:http://www.cnblogs.com/lhc-java/p/5361824.html

第八週學習總結——進程的切換和系統的通常執行過程:http://www.cnblogs.com/lhc-java/p/5389954.html

相關文章
相關標籤/搜索