自從學習了Linux內核之後,我開始對linux系統是怎樣運行的有了初步的了解,並開始對linux產生了濃厚的興趣。以下是我學習linux的一些總結:
1、關於計算機將c代碼轉換成匯編代碼的過程秒速
2、基於時間片輪轉多道程序內核代碼分析操作系統的工作過程
3、跟蹤分析Linux內核的啟動過程
4、使用API和C代碼中嵌入匯編實現同一個系統的調用
5、基於從system_call到iret的過程分析系統調用過程
6、分析Linux內核創建一個新進程的過程
7、Linux如何裝載和啟動一個可執行程序
8、理解進程調度時機跟蹤分析進程調度與進程切換的過程
在學習了這麼多課程之後,我了解了Linux內核啟動通過start_kernel這個函數分為兩部分,在此之前是匯編代碼完成初始化和環境配置;在此之後是按照c讓內核中的模塊初始化,初始化完畢就是啟動init進程,緊接著start_kernel就會調用rest_init函數,產生1號進程(此處需要注意:因為init_task是靜態制造出來的,pid=0)。此時init_task的任務基本已經結束了,它會淪為一個idle task,在init_idle中將會把init_task加入到CPU運行隊列中,當運行隊列中沒有別的就緒進程時,init_task將會被調用,它的核心是一個while(1)循環,在循環中它將會調用schedule函數以便在運行隊列中有新的進程加入時切換到該新進程上。
而系統調用可以用API函數直接調用或者在C語言中嵌入匯編代碼來進行調用,但兩者的本質是一樣的,都需要使用系統調用號,只不過API函數已經把系統調用號封裝起來了,與內核系統調用形成了映射關系。
當然進程的創建也是相當重要的,fork創建的新進程是和父進程一樣的副本(除了pid和ppid),都包含有效的uid和gid,進程組合會話id,環境,資源等等。通過 實驗以及代碼分析,可以得出:在do_fork中:copy_process管理子進程運行的准備,wake_up_new_task作為子進程forking完成。 linux為每個由父進程復制而來的進程創建子進程; linux為每個新創建的進程動態的分配一個task_struct結構;fork被調用一次,返回兩次。
最重要的還是了解進程調度的時機以及進程切換的過程,學習完這門課程之後,我是這麼總結的:
1、Linux中進程調度與進程切換過程:
schedule——(sched_submit_work;__schedule)——(preempt_disable;context_switch;sched_preempt_enable_no_resched)—(prepare_task_switch;switch_mm;switch_to;barrier;finish_task_switch)——__switch_to。
2、Linux系統一般執行過程
(1) 正在運行的用戶態進程X
(2) 發生中斷----save cs:eip/esp/eflags(current) to kernel stack,then load cs:eip(entry of a specific ISR)
add 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/eflag from kernel stack
(8) 繼續運行用戶態進程Y
實驗總結
在學習完此課程後,我對linux系統內核又有了更深刻的了解,我覺得我最大的收獲就是窺探了linux內核運作的機制,系統是怎麼進行進程創建的,堆棧是怎麼工作的,系統調用是怎麼實現的等等。但是學習完課程後還是有點遺憾的,就是始終沒怎麼弄懂任務切換的詳細過程,只是知道大概流程,比如為何每次將1f存入到prev->thread.ip中,即保存前一個任務的eip值,那樣豈不是每次切換任務時都是將1f存入到prev->thread.ip中,而切換回來時都得從1f開始。
莊華健
《Linux內核分析》MOOC課程http://mooc.study.163.com/course/USTC-1000029000