第六講 進程的描述和進程的創建
@2015.04
為了管理進程,內核必須對每個進程進行清晰的描述,進程描述符提供了內核所需瞭解的進程信息。進程描述符task_struct的源碼鏈接:http://codelab.shiyanlou.com/xref/linux-3.18.6/include/linux/sched.h#1235。其結構很龐大,初次接觸我們只關注與進程創建與管理密切相關的一些字段。如:
關於task_struct的詳細描述,可參見task_struct結構描述 當然除了應該瞭解task_struct中存儲了哪些信息以外,還應該學習這些信息是如何組織的。比如所有的進程描述符是如何通過其內的一個數據成員 struct list_head tasks雙向鏈接起來的,詳見網上的一篇文章:linux 內核 task_struct 結構體字段分析
在Linux應用程序的開發中,可以通過fork、vfork和clone等API來創建一個子進程,它們在Linux內核中對應的系統調用分別為sys_fork、sys_vfork和sys_clone函數,而這些函數最終都會調用do_fork完成子進程的創建。do_fork主要是複製了父進程的task_struct,然後修改必要的信息,從而得到子進程的task_struct。主要修改了以下信息:
pid 進程鏈表 給新進程分配一個新的內核堆棧 修改子進程的IP為ret_from_fork,當子進程獲得調度時就是從這裡開始執行的 其它眾多零碎字段的初始化,如運行時間設為0等 等等吧;
剛fork出來的子進程是從ret_from_fork開始執行的,然後跳轉到syscall_exit(http://codelab.shiyanlou.com/xref/linux-3.18.6/arch/x86/kernel/entry_32.S#290),從系統調用中返回。看下面的源代碼截圖:
從用戶態代碼來看,fork()函數返回了兩次,即在父子進程中各返回一次!但是子進程的返回值是0,從返回值我們就可以判斷出返回後接下來要執行的代碼是中父進程還是子進程中。
實驗仍然中實驗樓的虛擬機裡做,這真的是學習的一個好地方,不信,你來http://www.shiyanlou.com/courses/195。
cd LinuxKernel
rm menu -rf
git clone https://github.com/mengning/menu.git
cd menu
mv test_fork.c test.c
make rootfs
一切正常的話,這時候我們簡易的內核系統就啟動起來了,這menuos種,輸入help,就可以看到新添加的命令fork,輸入fork,看看效果吧,有圖為證:
關閉QEMU窗口,中命令行中輸入:
qemu -kernel ../linux-3.18.6/arch/x86/boot/bzImage -initrd ../rootfs.img -s -S
再次啟動MenuOS,並暫停等待gdb調試。
然後水平分割命令行窗口,這新窗口中依次輸入以下命令,啟動調試:
gdb
file linux-3.18.6/vmlinux
target remote:1234
然後再設置以下斷點:
b sys_clone
b do_fork
b dup_task_struct
b copy_process
b copy_thread
b ret_from_fork
好了,繼續運行程序吧,你會發現程序調試過程中,會停在這些斷點處,然後你可以單步跟蹤一些感興趣的代碼,從而驗證我們剛剛學習到的Linux進程的創建過程!Enjoy it!