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