一、進程與線程的概念
進程是多程序設計中操作系統的基本概念,用于描述程序執行的實體。在linux等多用戶多進程的操作系統中,通常將這個執行實體稱為進程,有時也被稱為線程或任務。
在Linux操作系統中,為何進程和線程有時候會互相稱呼呢?下面將通過對進程創建和銷毀的流程進行闡述,可以更加自然地理解這一點。
1. 創建進程的入口函數
首次遇到進程創建是在Linux啟動過程中,rest_init函數調用kernel_thread函數創建了兩個內核進程:kernel_init和kthreadd。
1.1 kernel_thread的原型
定義在kernel/fork.c文件內,是調用_do_fork實現的,源碼如下:
我們知道kthreadd進程負責創建所有內核線程,那么它是如何創建的呢?循著鏈表kthread_create_list,可以找到鏈表是__kthread_create_on_node函數內插入的,進而我們引出了kthread_create、kthread_run等函數。
1.2 kthread_create的原型
定義在include/Linux/kthread.h文件內,是個宏定義。
kthread_create_on_node函數定義在kernel/kthread.c文件內,內部是調用__kthread_create_on_node實現。
當看到EXPORT_SYMBOL標識時,可以知道kthread_create_on_node是對全部內核代碼公開的,內核和驅動都可以調用,調用時只需extern該函數聲明或包含頭文件即可,實際操作中,更多的是使用kthread_create宏。
我們繼續看kthread_create_on_node的主要實現函數是__kthread_create_on_node,line 299顯示task的數據結構體是struct task_sttruct, 便是進程描述符。
1.3 kthread_run的原型
kthread_run是定義在include/linux/kthread.h頭文件的宏,可以看出內部也是調用kthread_create函數實現的。
1.4 對比三個內核創建進程函數
- kernel_thread直接調用_do_fork創建進程,但不對外開放。
- kthread_create創建了進程由kthread進程具體完成創建,間接調用_do_fork實現,但它對所有內核開放。
- kthread_run調用kthread_create創建了進程,并立即喚醒去執行。
2、用戶進程該如何創建
在Linux應用編程的時候我們常用三個函數,fork、vfork和pthead__create,區別與內核進程的創建,用戶態不能直接調用內核態的進程創建函數,必須經由系統調用system call機制(system call不是本文重點,后面單獨一篇詳述)。
2.1 fork函數
fork函數調用_do_fork函數創建進程。
2.2 vfork函數
vfork函數調用_do_fork函數創建進程。不同于fork函數,args內多了flags的賦值。
2.3 clone函數
clone函數也是調用_do_fork函數創建進程。不同于fork、vfork函數,args內多了更多參數的賦值。
2.4 小節
創建內核進程的接口有kernel_thread、kthread_create和kthread_run。
創建用戶進程的接口有fork、vfork和pthread__create。
這六個接口最終都是調用_do_fork實現的。
3、創建進程的具體實現之_do_fork
用戶態和內核態創建進程最終都是直接或間接調用_do_fork實現的。可見_do_fork函數的重要性,內部實現是調用copy_process來創建進程描述符以及子進程執行所需要的所有其他數據結構。
4、進程描述符之Struct task_struct
調用copy_process來創建進程描述符,描述符的數據結構是struct task_struct,定義在include/linux/sched.h文件內。
我們可以簡單一撇結構體內的成員變量(有大量刪減),單獨講每個成員變量沒有意義,后續我們在實際內核功能中理解它們。
5、進程銷毀
當一個進程運行結束或者因為異常而終止退出時,該如何操作呢?在用戶態常用exit函數來終止,在內核態直接調用do_exit()。
最終都會調用內核函數do_exit(), 該函數可以理解為進程創建的逆過程,即把進程創建的資源一一釋放,并調整與其父子進程的關系。具體實現過程不再分析,直接看源碼。
6、總結與下一篇計劃
本篇主要講解內核態和用戶態創建和銷毀進程的接口函數,并側重介紹了創建過程函數_do_fork。
本篇中講到用戶態調用內核態的函數需要用到系統調用,下一篇著重講解系統調用的過程。