Linux學習期中總結

1、《Linux內核分析》總結linux

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

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

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

3.計算機的彙編指令安全

(1)movl指令:服務器

  • 寄存器尋址,寄存器模式,以%開頭的寄存器標示符。不和內存打交道,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系統的基礎是內核、C庫、工具集和系統的基本工具。

1.操做系統:整個系統中負責完成最基本功能和系統管理的部分。

2.內核(操做系統的內在覈心,通常處於系統態):
由響應中斷的中斷服務程序;管理多個進程,分享處理器時間調度程序;管理進程地址空間的內存管理程序;網絡、進程間通訊等系統服務程序組成。

3.內核空間:系統態和被保護起來的內存空間

4.系統調用:應用程序與內核通訊。應用程序經過系統調用界面陷入內核是應用程序完成工做的基本行爲方式。

6.Unix內核一般須要硬件系統提供頁機制(MMU)以管理內存,這樣能夠增強對內存空間的保護,並能夠保證每一個進程都運行於不一樣的虛地址空間上。

7.單內核與微內核比較:

(1)單內核——以單個靜態二進制文件形式存放於磁盤中,全部內核服務在一個大內核地址空間上運行。

特色:內核能夠直接調用函數,簡單並性能高。但一個功能的崩潰會致使整個內核沒法使用。

(2)微內核——內核按功能被劃分紅各個獨立的過程。每一個過程叫作一個服務器。全部服務器獨立並運行在本身的地址空間上。

特色:經過消息傳遞處理爲內核通訊,採用進程間通訊(IPC)機制。安全。一個服務器失效不會影響其餘服務器。內核各個服務之間的調用涉及進程間的通訊,比較複雜且效率低。

8.Linux內核總結:

爲單內核,但具有微內核的一些特徵:模塊化設計、搶佔式內核、支持內核線程、動態裝載內核模塊。同時避微內核設計上的性能缺陷:讓全部事情運行在內核態,直接調用函數,無需消息傳遞。支持動態加載內核模塊;支持對稱多處理(SMP);內核能夠搶佔(preemptive),容許內核運行的任務有優先執行的能力;不區分線程和進程;提供具備設備類的面向對象的設備模型、熱插拔事件,以及用戶空間的設備文件系統(sysfs)。

(二)  第二章 從內核出發

1.使用Git(管理內核源碼的分佈式控制系統)獲取最新提交到Linus版本樹的一個副本:
$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git

2.安裝內核源代碼

兩種形式:GNU zip(gzip)(運行:$ tar xvzf linux-x.y.z.tar.gz)和bzip2(運行:$ tar xvjf linux-x.y.z.tar.bz2)。

3. 配置內核(關於make與config)

配置編譯過程:

• make config:遍歷全部配置項,並讓用戶選擇

• make deconfig:基於默認的配置

• make oldconfig:先將/boot目錄下的配置文件寫進.config文件中,採用的是註釋的形式寫進新增長的功能。

• zcat /proc/config.gz > .config:配置選項CONFIG_IKCONFIG_PROC把完整的壓縮過的內核配置文件存放在/
proc/config.gz中,再次編譯時能夠方便地克隆當前的配置。

• make:默認的Makefile自動化編譯。

4.減小編譯的垃圾信息;衍生多個編譯做業

5.安裝新內核——把全部已編譯的模塊安裝到正確的主目錄/lib/modules下:% make modules_install。編譯時在內核代碼樹的根目錄下建立一個System.map文件(符號對照表),用來將內核符號與它們的起始地址對應起來。

6.同步和併發

• Linux是搶佔多任務操做系統

• 內核支持對稱多處理器系統(SMP)

• 中斷是異步到來的

• Linux內核能夠搶佔
經常使用的解決競爭的辦法是自旋鎖和信號量

(三)  第三章——進程管理

1進程就是處於執行期的程序(目標碼存放在某種存儲介質上),不只侷限於一段可執行程序代碼,還包含其餘資源,如打開的文件、掛起的信號、內核內部數據等。提供兩種虛擬機制:虛擬處理器和虛擬內存。

2.線程(執行線程)是在進程中活動的對象,擁有獨立的程序計數器、進程棧和一組進程寄存器。注意:內核調度的是線程而不是進程!

3.系統調用經過複製一個現有進程來建立一個全新的進程。調用fork()的進程爲父進程,新產生的進程稱爲子進程。fork()系統調用從內和返回兩次:一次回到父進程,一次回到新產生的子進程。

4. 共有五種進程狀態:

  • 運行TASK_RUNNING——進程是可執行的;或正在執行或等待執行。這是進程在用戶空間中執行的惟一可能的狀態。
  • 可中斷TASK_INTERRUPTIBLE——進程正在睡眠或阻塞,等待某些條件的達成。一旦這些條件達成,內核就會把進程狀態設置爲運行。出於此狀態的進程也會由於接受到信號而提早被喚醒並隨時準備投入運行。
  • 不可中斷TASK_UNINTERRUPTIBLE——一般在進程必須在等待時不受干擾或等待事件很快就會發生時出現。出於此狀態的任務對信號不做響應。
  • __TASK_TRACED——被其餘進程跟蹤的進程,例如經過ptrace對調試程序進行跟蹤。
  • 中止__TASK_STOPPED——進程中止執行:進程沒有投入或不能運行。在接受到SIGSTOP/SIGTSTP/SIGTTOU等信號或在調試期間接受到任何信號時進入此狀態。

5.進程建立:寫時拷貝——fork()——vfork()

6.進程終結:刪除進程描述符——孤兒進程形成的進退維谷

(四)第四章——進程調度

1.多任務系統分爲兩類:非搶佔式多任務和搶佔式

2.策略:調度策略一般在兩個矛盾的目標中間尋找平衡:進程響應迅速(響應時間短)和最大系統利用率(高吞吐量)。

3. 進程優先級

在某些系統中,優先級高的進程使用的時間片也比較長。調度程序老是選擇時間片未用盡並且優先級最高的進程運行。用戶和系統均可以經過設置進程的優先級來影響系統的調度。

Linux採用了兩種不一樣的優先級範圍——nice值和實時優先級值。

(1) nice值,範圍是-20到19,數值越大優先級越低,默認值爲0。Linux中,nice值則表明時間片的比例。能夠經過ps-el命令查看系統中的進程列表,結果中標記NI的一列就是進程對應的nice值。

(2) 實時優先級值,默認0到99,數值越大優先級越高。任何實時進程的優先級都高於普通的進程。

4.公平調度——CFS中,任何進程所得到的處理器時間是由它本身和其餘全部可運行進程nice值的相對差值決定。任何nice值對應的絕對時間是處理器的使用比。

5.調度的實現——CFS相關代碼四個組成部分:時間記帳、進程選擇、調度器入口和睡眠和喚醒。

6.用戶搶佔(會檢查need_ resched標誌)發生時機:

  • 從系統調用返回用戶空間時;
  • 從中斷處理程序返回用戶空間時。

7.內核搶佔發生時機:

  • 中斷處理程序正在執行且返回內核空間以前;
  • 內核代碼再一次具備可搶佔性的時候;
  • 內核中的任務顯式地調用schedule函數。

8.實時調度策略

linux 提供兩種實時調度策略SCHED_FIFO和SCHED_RR。

  • SCHED_FIFO實現了一種簡單的、先入先出的調度算法:它不使用時間片.處於可運行狀態的SCHED_FIFO 級的進程會比任何SCHED_NORMAL 級的進程都先獲得調度。
  • SCHED_RR級的進程在耗盡事先分配給它的時間後就不能再繼續執行了.即 SC阻止RR 是帶有時鬧片的SCHED_FIFO-這是一種實時輪流調度算掛。

這兩種算法實現的都是靜態優先級。Linux實時調度算法是軟實時工做方式——內核調度進程,儘可能使進程在它的限定時間到來前運行,但內核不能保證可以總能知足。

實時優先級範圍是0到MAX_RT_PRIO減1。默認狀況下,MAX_RT_PRIO爲100,nice值從-20到19直接對應的是100到139的實時優先級範圍。

(五)第五章——系統調用

重點——Linux系統調用的規則和實現方法。

1.如何定義一個系統調用:asmlinkage long sys_getpid(void)

  • 首先,注意函數聲明中的asmlinkage限定詞,這是一個編譯指令,通知編譯器僅從棧中提取該函數的參數。全部的系統調用都須要這個限定詞。
  • 其次函數返回long。爲了保證32位和64位系統的兼容,系統調用在用戶空間和內核空間有不一樣的返回值類型,在用戶空間爲int在內核空間爲long。
  • 最後,注意系統調用get_pid()中的在內核中被定義成sys_getpid()。這是Linux中全部系統調用都應該遵照的命名規則,系統調用bar()在內核中也實現爲sys_bar()函數。

2.Linux系統調用執行快,兩個緣由:

  • 有很短的上下文切換時間。
  • 系統調用處理程序和每一個系統調用自己很是簡潔。

3.通知內核的機制是靠軟中斷實現的

4.x86-32系統中,參數傳遞時ebx,ecx,edx,esi,edi按順序存放前五個參數。給用戶空間的返回值也經過寄存器傳遞。在x86系統上,它存放在eax寄存器中。

5.系統調用上下文

• 內核在執行系統調用的時候處於進程上下文。

• 在進程上下文中,內核能夠休眠而且能夠被搶佔。

• 當系統調用返回的時候,控制權仍在system_call()中,它最終會負責切換到用戶空間,並讓用戶進程繼續執行下去。

(六)第七章 連接

1.靜態連接:連接器將重定位目標文件(relocatable object files)組合成一個可執行目標文件。cpp(c previous processor,C預處理器);ccl(C編譯器);as(彙編器)。

爲了建立靜態連接,連接器完成兩個主要任務:

  • 符號解析(symbol resolution):將每一個符號引用和一個符號定義聯繫起來。
  • 重定位(relocation):編譯器和彙編器生成從0地址開始的代碼和數據節。連接器經過把每一個符號定義與一個存儲器位置聯繫起來,而後修改全部對這些符號的引用,使得它們指向這個存儲器位置,從而重定位這些節。

2.目標文件有三種形式:

  • 可重定位目標文件:包含二進制代碼和數據,其形式能夠在編譯時與其餘可重定位目標文件合併起來,建立一個可執行目標文件。
  • 可執行目標文件:包含二進制代碼和數據,其形式能夠直接拷貝到存儲器並執行。
  • 共享目標文件:一種特殊類型的可重定位目標文件,能夠在加載或運行時被動態地加載到存儲器並連接。

3.重定位由兩步組成:

  • 重定位節和符號定義。在這一步中,連接器將全部相同類型的節合併爲同一類型的新的聚合節。而後,連接器將運行時存儲器地址賦給新的聚合節,賦給輸入模塊定義的每一個節,以及賦給輸入模塊定義的每一個符號。當這一步完成時,程序中的每一個指令和全局變量都有惟一的運行時存儲器地址。
  • 重定位節中的符號引用。在這一步中,連接器修改代碼節和數據節中對每一個符號的引用,使得它們指向正確的運行時地址。爲了執行這一步,連接器依賴於稱爲重定位條目的可重定位目標模塊中的數據結構。

4.注意:靜態連接與動態連接的區別——靜態連接是把程序所須要的庫代碼和數據拷貝和嵌入到引用它們的可執行文件中;而動態連接是全部引用該庫的可執行文件文件共享這個.so(dll)文件中的代碼和數據。

5.動態連接器經過執行下面的重定位完成連接任務:

    • 重定位libc.so的文本和數據到某個存儲器段
    • 重定位libvector.so的文本和數據到另外一個存儲器段
    • 重定位p2中全部對libc.so和libvector.so定義的符號的引用
    • 最後動態連接器將控制傳遞給應用程序,此時共享庫的位置已固定,而且在程序執行的過程當中不會改變

學習感想與體會:

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

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

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

相關文章
相關標籤/搜索