LINUX內核分析第八週學習總結
標籤(空格分隔): 20135328陳都linux
陳都 原創做品轉載請註明出處 《Linux內核分析》MOOC課程 http://mooc.study.163.com/course/USTC-1000029000算法
1、進程切換的關鍵代碼switch_to的分析
1.進程調度與進程調度的時機
- 不一樣類型的進程有不一樣的調度需求。
- 第一種分類:
- I/O-bound 頻繁的進行I/O;一般會花費不少時間等待I/O操做的完成。
- CPU-bound 計算密集型;須要大量的CPU時間進行運算。
- 第二種分類:
- 批處理進程,沒必要與用戶交互,一般在後臺運行,沒必要很快響應。主要用於編譯程序,科學計算
- 實時進程,須要常常與用戶交互,因此要花不少時間等待用戶輸入操做,響應時間要快,平均延遲低於50~150ms。主要用於shell,文本編輯程序,圖形應用程序
- 交互式進程 shell,有實時需求,不該被低優先級的進程阻塞,響應時間要短要穩定。主要用於視頻、音配、機械控制。
2.不一樣的進程要採起不一樣的進程調度策略
調度策略:是一組規則,它們決定何時以怎樣的方式選擇一個新進程運行
Linux中的調度是多種策略和調度算法的組合
- Linux的調度基於分時和優先級策略:
- 進程根據優先級(系統根據特定算法計算出來)排隊;
- 這個優先級的值表示如何適當分配CPU;
- 調度程序會根據進程的運行週期動態調整優先級;
- 好比nice等系統調用,能夠手動調整優先級
調度策略本質上是一種算法,這些算法從實現的角度看僅僅是從運行隊列中選擇一個新進程,選擇的過程當中運用了不一樣的策略而已shell
常見的一些函數:架構
nice
getpriority/setpriority 設置優先級
sched_getschedduler/sched_setscheduler
sched_getparam/sched_setparam
sched_yield
sched_get_priority_min/sched_get_priority_max
sched_rr_get_interval
3.進程的調度時機
1.schedule函數實現調度
- 目的:在運行隊列中找到一個進程,把cpu分配給它
- 方法:
- 直接調用schedule()
- 鬆散調用,根據need_resched標記
2.進程調度的時機
- 中斷處理過程(包括時鐘中斷、I/O中斷、系統調用和異常)中,直接調用schedule(),或者返回用戶態時根據need_resched標記調用schedule();主動調度。
- 用戶態進程沒法實現主動調度,僅能經過陷入內核態後的某個時機點進行調度,即在中斷處理過程當中進行調度。用戶態進程只能被動調度。
- 內核線程能夠直接調用schedule()進行進程切換,也能夠在中斷處理過程當中進行調度,也就是說內核線程既能夠主動調度,也能夠被動調度;
內核線程是隻有內核態沒有用戶態的特殊進程併發
4.進程上下文切換相關代碼分析
- 爲了控制進程的執行,內核必須有能力掛起正在CPU上執行的進程,並恢復之前掛起的某個進程的執行,這叫作進程切換、任務切換、上下文切換。
- 掛起正在CPU上執行的進程,與中斷時保存現場是不一樣的,中斷先後是在同一個進程上下文中,只是由用戶態轉向內核態執行。
- 進程上下文包含了進程執行須要的全部信息
- 用戶地址空間:包括程序代碼,數據,用戶堆棧等
- 控制信息:進程描述符,內核堆棧等
- 硬件上下文(注意中斷也要保存硬件上下文只是保存的方法不一樣)
- schedule()函數選擇一個新的進程來運行,並調用context_ switch進行上下文的切換,這個宏調用switch_ to來進行關鍵上下文切換
- next = pick_ next_ task(rq, prev);//進程調度算法都封裝這個函數內部
- context_ switch(rq, prev, next);//進程上下文切換
- switch_ to利用了prev和next兩個參數:prev指向當前進程,next指向被調度的進程
schedule函數:
框架
具體內容:
函數
進程上下文切換:
學習
切換準備:
操作系統
切換寄存器狀態和堆棧:
線程
具體分析switch_to彙編:
- 42:保存當前進程的flags
- 43:把當前進程的堆棧基址壓棧
- 44:把當前的棧頂保存起來
- 45:把下一進程棧頂放入esp寄存器 完成內核堆棧切換
- 46:保存當前進程的eip,再恢復時用
- 47:下一進程棧頂是起點。next_ip通常是$1f,對於新建立的子進程是ret_from_fork
- 49: 經過寄存器傳遞參數
- 50:認爲是next進程開始了 46~49模糊地帶
2、Linux系統的通常執行過程
1. Linux系統的通常執行過程分析
最通常的狀況:正在運行的用戶態進程X切換到運行用戶態進程Y的過程
- 正在運行的用戶態進程X
- 發生中斷——
save cs:eip/esp/eflags(current) to kernel stack
壓入內核堆棧
load cs:eip(entry of a specific ISR) and ss:esp(point to kernel stack).
把當前進程的內核堆棧的信息保存,和當前中斷例程的起點加載
- SAVE_ALL //保存現場
- 中斷處理過程當中或中斷返回前調用了schedule(),其中的switch_to作了關鍵的進程上下文切換
- 標號1以後開始運行用戶態進程Y(這裏Y曾經經過以上步驟被切換出去過所以能夠從標號1繼續執行)
- restore_all //恢復現場
- iret - pop cs:eip/ss:esp/eflags from kernel stack從內核堆棧中彈出y的相關信息
- 繼續運行用戶態進程Y
2.Linux系統執行過程當中的幾個特殊狀況
- 經過中斷處理過程當中的調度時機,用戶態進程與內核線程之間互相切換和內核線程之間互相切換,與最通常的狀況很是相似,只是內核線程運行過程當中發生中斷的時候沒有進程用戶態和內核態的轉換,cs不會變化;
- 內核線程主動調用schedule(),只有進程上下文的切換,沒有發生中斷上下文的切換,也不須要從中斷中返回,與最通常的狀況相比更簡略;
- 建立子進程的系統調用在子進程中的執行起點(如ret_from_fork,上文中也已經提到過)及返回用戶態;
- 加載一個新的可執行程序後返回到用戶態的狀況,如execve,在新進程內部修改了中斷保存的信息
3.內核與舞女
- 進程的地址空間一共有4G,其中0——3G是用戶態能夠訪問,3G以上只有內核態能夠訪問
- 內核至關於出租車,能夠爲每個「招手」的進程提供內核態到用戶態的轉換
- 沒有進程須要「承載」的時候,內核進入idle0號進程進行「空轉」。當用戶進程有需求時,內核發生中斷,幫助用戶進程完成請求,而後再返回到用戶進程。就好像Taxi將用戶載了一圈以後又把用戶放下來。
- 3G以上的部分就是這樣的「出租車」,是全部進程共享的,在內核態部分切換的時候就比較容易
- 內核是各類中斷處理程序和內核線程的集合。
3、Linux系統架構和執行過程概覽
1.linux操做系統架構概覽
操做系統的基本概念及目的
操做系統:計算機系統包含的一個基本的程序集合
目的:與硬件交互,爲用戶程序提供一個良好的執行環境
典型的Linux操做系統架構
2.最簡單、最複雜的操做——執行ls命令
- 在控制檯輸入ls命令
- Shell程序分析輸入參數,肯定這是ls
- 調用系統調用fork生成一個shell自己的拷貝
- 調用exec系統調用將ls可執行文件裝入內存
- 從系統調用返回
- Shell和ls都獲得執行
3.從CPU和內存的角度來看Linux系統的執行
- 執行gets()函數;
- 執行系統調用,陷入內核;
- 等待輸入,CPU會調度其餘進程執行,同時wait一個I/O中斷;
- 敲擊ls,發I/O中斷給CPU,中斷處理程序進行現場保存、壓棧等等;
- 中斷處理程序發現X進程在等待這個I/O(此時X已經變成阻塞態),處理程序將X設置爲WAKE_UP;
- 進程管理可能會把進程X設置爲next進程,這樣gets系統調用得到數據,再返回用戶態堆棧
- 從內存角度看,全部的物理地址都會被映射到3G以上的地址空間:由於這部分對全部進程來講都是共享的
4、實踐部分
實踐要求:
- 使用gdb跟蹤分析一個schedule()函數 ,驗證您對Linux系統進程調度與進程切換過程的理解
- 特別關注並仔細分析switch_to中的彙編代碼,理解進程上下文的切換機制,以及與中斷上下文切換的關係
實踐過程:
啓動內核,並進入調試狀態
設置斷點
按c繼續,能夠發現core.c裏面調用了_schedule()
用list查看代碼
設置switch_to斷點
先找到switch_to的具體行再設置斷點
單步執行直到發現pick_nexi_task()
繼續單步執行,直到發現context_switch
進入context_switch()函數並發現了switch_to()
跟蹤完成
5、小結
- Linux調度的核心函數爲schedule,schedule函數封裝了內核調度的框架。
- Linux內核中實現進程的切換主要經過保存進程相關的信息實現,這裏須要注意進程切換中內核級進程的切換和用戶態進程切換的不一樣。
- 內核態能夠直接調用schedule函數並不須要陷入中斷這個過程。而用戶態則須要陷入內核態才能實現進程的切換
- schedule()函數實現進程調度,context_ switch完成進程上下文切換,switch_ to完成寄存器的切換。
- 在調度時機方面,內核線程能夠直接調用schedule()進行進程切換,也能夠在中斷處理過程當中進行調度,也就是說內核線程做爲一類的特殊的進程能夠主動調度,也能夠被動調度。
- 所謂進程的切換主要就是堆棧的切換,這是由宏操做switch_to()完成的