回覆列表
-
1 # IT劉小虎
-
2 # 陶陶然的心語坊
首先關於核心心程序建立,涉及到底層的東西,個人認為沒必要太深入,瞭解他是怎麼實現的就可了,如果確實要深入理解,那就得去看原始碼了。
1.Linux 程序建立:Linux繼承了UNIX的程序建立方式,用的是fork API函式,什麼是fork呢,就是先clone然後在分支,父子程序各幹各的。
2.Windows
程序建立:Windows沒有fork,但是有CreateProcess這個API函式,用來建立一個新的程序和它的主執行緒,這個新程序執行指定的可執行檔案。
其實Linux建立程序,就是建立程序執行所需的記憶體空間,填充描述程序的 task_struct 結構體,以及載入程序的程式而已。
Linux 核心並無專門建立執行緒的機制我們之前提到,Linux並不特殊對待執行緒,在Linux看來,執行緒不過就是一種特殊的程序而已。那麼,Linux是如何建立執行緒的呢?
執行緒機制是大多數現代程式語言都會提供的機制,該機制允許在同一程序的共享記憶體地址空間執行一組“特殊的程序(即執行緒)”。這些執行緒不僅共享同一段記憶體空間,還可以共享已經開啟的檔案,統計量等其他資源。執行緒機制支援程式併發執行,在多處理器核心的系統上,該併發機制能夠實現多條執行緒同時執行。
Linux 管理執行緒的方式不同於其他一些經典作業系統,Linux 並沒有執行緒的概念,它把執行緒當作程序的一個子集來管理。因此,Linux 核心並未為執行緒提供額外排程演算法,也沒有提供額外的資料結構用於描述和儲存執行緒。
就像程序一樣,Linux 使用 task_struct 結構體描述和記錄執行緒,每個執行緒都有唯一屬於自己的 task_struct 結構。從這個角度來看,執行緒就是一個普通的程序,只不過執行緒可能和其他程序共享一些資源而已。
以 Windows 為代表的一些作業系統提供了專門用於建立執行緒的機制,在這些系統中,執行緒常常被稱作“輕量級程序”,因為相對於程序而言,執行緒耗費的資源較少,能夠較為迅速的建立和投入執行。
但是對於 Linux 而言,執行緒不過是程序之間共享資源的一種手段罷了。那麼是不是 Linux 中的執行緒比 Windows 中的執行緒更加“重量級”呢?也不是,因為 Linux 中的程序本身就很輕量級,Linux 建立程序所需時間,並不比 Windows 建立執行緒所需時間多多少。
從C語言程式碼層面來看,假設某個程序包含 4 個執行緒,以 Windows 為代表的一些作業系統一般會有一個包含指向 4 個不同執行緒的指標的程序描述符,負責描述地址空間、開啟的檔案等共享資源,而執行緒本身再去描述自己獨佔的資源。
與之對應的,Linux 的做法就高雅許多,它僅需為這 4 個執行緒建立 4 個 task_struct 結構體,然後在 task_struct 中指定它們共享的資源就可以了。
建立執行緒看了我最近幾篇文章的讀者應該已經明白,Linux 核心中的執行緒其實就是程序,因此執行緒的建立與程序的建立過程是類似的,從C語言原始碼層面看,基本上也是透過 fork() 函式和 exec() 函式族實現的。只不過在呼叫 clone() 函式時需要傳遞一個引數用於描述共享資源,例如:
上面這行C語言程式碼和呼叫 fork() 函式的結果差不多,只不過輸入的幾個引數標誌位說明了子程序與父程序共享一些資源:地址空間、檔案系統、開啟的檔案、訊號處理程式。
對比一下,fork() 基本上就相當於 clone(SIGCHLD, 0),這也是 fork() 函式建立的子程序之後不再與父程序共享資源的原因。
關於 clone() 函式的引數標誌位,可以在Linux中輸入 man 命令檢視。
Linux 核心執行緒就像使用者空間的C語言程式開發一樣,Linux 核心也經常需要在後臺處理資料,這時就需要藉助核心執行緒了。Linux 的核心執行緒一般不會獨立的地址空間,它們只在核心空間執行,不會切換到使用者空間。不過排程是和普通程序一樣的,可以被排程和搶佔。
Linux 建立核心執行緒由 kthread_create() 函式實現,它的C語言原始碼如下,請看:
可見,kthread_create() 函式的C語言程式碼並不長,而且也可以看出,Linux 核心執行緒是透過 kthread_create_info 結構體描述的,它的定義C語言程式碼如下,可見,核心執行緒的描述和儲存也是包含 task_struct 結構體的:
kthread_create() 函式建立名為 namefmt 的執行緒,不過執行緒被建立後是處於不可執行狀態的,我們可以透過 wake_up_process() 函式喚醒它。當然,也可以透過 kthread_run() 方法實現這一過程,相關的C語言程式碼如下,請看:
其實就是將 kthread_create() 函式和 wake_up_process() 函式組合到一起而已。Linux 的核心執行緒被啟動後,會一直執行到呼叫 do_exit() 退出。我們也可以呼叫 kthread_stop() 函式提前結束它,相關的C語言程式碼如下,請看:
kthread_stop() 函式接收的引數為 kthread_create() 函式建立的結構體的 task_struct 成員。從C語言程式碼可以看出,kthread_stop() 其實也是會呼叫 wake_up_process() 函式喚醒執行緒的,它在喚醒執行緒後,會等待執行緒函式退出,並不會呼叫 threadfn() 函式。
這裡需要注意,如果建立的執行緒函式 threadfn() 呼叫了 do_exit() 函式,最好就不要再呼叫 kthread_stop() 函數了。
kthread_stop() 函式等待執行緒退出是透過 wait_for_completion() 函式實現的,相關的C語言程式碼如下,請看:
稍稍跟蹤一下C語言程式碼,發現其實這一等待過程是由 do_wait_for_common()函式實現的,它的C語言程式碼如下,請看:
還是比較清晰的,這裡就不再贅述了。至此,我們就瞭解了Linux核心是如何建立執行緒並投入執行,以及如何結束核心執行緒的了。
小結本節主要討論了 Linux 核心中的執行緒的建立,應該能夠看出,其實核心還是圍繞對 task_struct 結構的管理,這與管理程序並無過多區別。因此,說Linux中的執行緒只是一種特殊的程序,一點也不為過。