進程的建立與描述

陳民禾——原創做品轉載請註明出處—— 《Linux內核分析》MOOC課程http://mooc.study.163.com/course/USTC-1000029000函數

一.複習上週內容學習

上週主要學習了Linux中的系統調用的過程,如圖所示就是系統調用的大體過程:優化

一.關於進程調度的一些基本概念spa

fork():進程是處於執行期的程序以及相關資源的總稱,進程在建立它的時候開始存活,在Linux系統中。這一般是調用fork()系統的結果,該系統調用經過複製一個現有進程來建立一個全新的進程,調用fork()的進程成爲父進程,新產生的進程稱之爲子進程,在調用結束時,在返回點這個相同位置上,父進程恢復執行,子進程開始執行,fork()系統從內核返回兩次:一次返回到父進程,另外一次回到新的子進程。其中fork()其實是由clone()系統調用實現的。線程

exec():建立新的進城以後會當即執行新的進程,接着調用exec()這組函數就能夠建立新的地址空間,並把新的地址空間載入其中。3d

進程描述符:內核把進程的列表存放在叫作任務隊列的雙向循環列表中,鏈表中的類型都是task_struct、成爲進程描述符的結構,進程描述符包含一個具體進程的全部信息,能完整的描述一個正在執行的程序:它打開的文件,進程的地址空間,掛起的信號,進程的狀態,還有其它的更多信息。指針

thread_info:每一個進程的task_struct存放在內核棧的尾端,調試

進程狀態轉換:code

- TASK_RUNNING具體是就緒仍是執行,要看系統當前的資源分配狀況;blog

- TASK_ZOMBIE也叫殭屍進程

三.進程建立

3.1 寫時拷貝

只有在須要寫入的時候,數據纔會被複制,從而使各個進程擁有各自的拷貝。也就是說,資

源的複製只有在須要寫入的時候才進行,在此以前,只是以只讀方式共享。這種技術使地址空間上的頁的拷貝被推遲到實際發生寫入的時候才進行。在頁根本不會被寫人的狀況下(舉例來講,fork()後當即調用exec(})它們就無須複製了。fork()的實際開銷就是複製父進程的頁表以反給予進程建立惟一的進程描述符。在通常狀況下,進程建立後都會立刻運行一個可執行的文件,這種優化能夠避免拷貝大量根本就不會被使用的數據〈地址空間裏經常包含數十她的數據〉。因爲Unix 強調進程快速執行的能力,因此這個優化是很重要的。

3. 2 fork()

Linux 經過clone()系統調用實現fork() 。這個調用經過一系列的參數標誌來指明父、子進程須要共享的資源〈關於這些標誌更多的信息請參考本章後面3.4 節〉。fork()、vfork()和一clone()庫函數都根據各自須要的參數楊L志去調用clone(),而後由clone()去調用do_fork().do_fork 完成了建立中的大部分工做,它的定義在kemeVfork.c 文件中。該函數調用copy_process()函數,而後讓進程開始運行。copy_process()函數完成的工做頗有意思:

l )調用dup_task_ struct()爲新進程建立一個內核枝、也read_info 結構和task_struct,這些值與當前進程的值相同。此時, 子進程和父進程的描述符是徹底相同的。

2 )檢查並確保新建立這個子進程後,當前用戶所擁有的進程數目沒有超出繪色分配的資源

的限制.

3 )子進程着手使本身與父進程區別開來。進程描述符內的許多成員都要被清0 或設爲初始值.那些不是繼承而來的進程描述符成員,主要是統計信息。task_struct 中的大多數數據都依然未被修改.

4 ) 子進程的狀態被設置爲TASK_UNJNTERRUPTIBLE,以保證它不會投入運行。

5 ) copy _process()調用copy_flags()以更新task_struct 的組ags 成員.代表進程是否擁有超級用戶權限的PF_SUPE盯RIV 標誌被清0。代表進程尚未調用exec()函數的PF_FOR.KNOEXEC標誌被設置。

6 )調用alloc _pid()爲新進程分配一個有效的PID。

7 )根據傳遞給clone()的參數標誌, copy_process()拷貝或共享打開的文件、文件系統信息、信號處理函數、進程地址空間和命名空間等。在通常狀況下,這些資源會被給定進程的全部線程共享:不然,這些資源對每一個進程是不一樣的,所以被拷貝到這裏。的最後, copy_process()傲掃尾工做並返回一個指向子進程的指針。再回到do_fork()函數,若是copy_process()函數成功返回,新建立的子進程被喚醒並讓其投入運行。內核有意選擇子進程首先執行。.由於通常子進程都會立刻調用exec()函數,這樣能夠避免寫時拷貝的額外開銷,若是父進程首先執行的話,有可能會開始向地址空間寫入。

四.實驗過程

1.更新menu內核,而後刪除test_fork.c以及test.c(以減小對以後實驗的影響)

 

2.編譯內核,能夠看到fork命令

3.啓動gdb調試,並對主要的函數設置斷點

 

4.在MenuOS中執行fork,就會發現fork函數停在了父進程中 

 

 5.繼續執行以後,停在了do_fork的位置。而後n單步執行,依次進入copy_process、dup_task_struct。按s進入該函數,能夠看到dst = src(也就是複製父進程的struct)

 

6.在copy_thread中,能夠看到把task_pg_regs(p)也就是內核堆棧特定的地址找到並初始化

7.到了15九、160行的代碼就是把壓入的代碼再放到子進程中:

*children = *current_pt_regs();
childregs->ax = 0;

8.164行,是肯定返回地址

p->thread.ip = (unsigned long) ret_from_fork;

實驗感想:

只是我第一次這麼早就完成博客,在此次實驗的過程當中我瞭解了進程間調度的基本方法,還本身實踐了gdb對內核代碼的調試,頗有意義。可執行程序代碼( Unix 稱其爲代碼段, text section)。一般進程還要包含其餘資源,像打開的文件,掛起的信號,內核內部數據,處理器狀態, 一個或多個具備內存映射的內存地址空間及一個或多個執行線程( thread of execution ),固然還包括用來存放全局變量的數據段等。實際上,進程就是正在執行的程序代碼的實時結果。

相關文章
相關標籤/搜索