[英]How does the kernel separate threads from processes
假設我有一個像Firefox這樣的瀏覽器進程,它有pid = 123. Firefox有5個打開的標簽,每個標簽都在一個單獨的線程中運行,所以它總共有5個線程。
所以我想深入了解內核如何將進程分離到要在struct task_struct
或thread_info中執行的線程。
與struct task_struct
類似,是任務列表的任務描述符。 struct task_struct
在哪里包含對這五個線程的引用或鏈接。
像Firefox這樣的進程的struct thread_struct
是否包含對所有5個線程的引用
要么
每個線程都被視為Linux內核中的進程。
與Windows不同,Linux在內核中沒有“線程”的實現。 內核為我們提供了有時稱為“輕量級進程”的東西,它們是“進程”和“線程”概念的概括,可以用來實現。
當您閱讀內核代碼並且一方面看到諸如thread_struct
之類的thread_struct
,另一方面看到pid
(進程ID)時,可能會thread_struct
感到困惑。 實際上,兩者都是同一個。 不要被術語混淆。
每個輕量級進程都有一個完全不同的thread_info
和task_struct
(帶有嵌入式thread_struct
)。 您似乎認為一個輕量級進程的task_struct
應該指向同一(用戶空間)“進程”中其他(用戶空間)“線程”的task_struct
。 不是這種情況。 在內核中,每個“線程”都是一個單獨的進程,調度程序分別處理每個“線程”。
Linux有一個名為clone
的系統調用,用於創建新的輕量級進程。 調用clone
,必須提供各種標志,指示新進程與現有進程之間將共享的內容。 他們可以共享他們的地址空間,或者他們每個人都可以擁有不同的地址空間。 他們可以共享他們的打開文件,或者他們每個人都有自己的打開文件列表。 他們可以共享信號處理程序,也可以各自擁有自己的信號處理程序。 它們可以位於相同的“線程組”中,也可以位於不同的線程組中。 等等...
雖然“線程”和“進程”在Linux中是相同的,但是您可以通過使用clone
創建不共享其地址空間,打開文件,信號處理程序等的進程來實現我們通常認為的“進程”。
您還可以通過使用clone
創建共享其地址空間,打開文件,信號處理程序等的進程來實現我們通常認為的“線程”。
如果你看一下task_struct
的定義,你會發現它有指向其他結構的指針,例如mm_struct
(地址空間), files_struct
(打開文件), sighand_struct
(信號處理程序)等等。 clone
新的“進程”時,將復制所有這些結構。 當您clone
一個新的“線程”時,這些結構將在新舊task_struct
之間共享 - 它們將指向相同的mm_struct
,相同的files_struct
,依此類推。 無論哪種方式,您只是提供不同的標記來clone
,告訴它要復制什么,以及分享什么。
我剛剛提到了上面的“線程組”,所以你可能會對此感到好奇。 簡而言之,“進程”中的每個“線程”都有自己的PID,但它們都共享相同的TGID(線程組ID)。 TGID都等於第一個程序線程的PID。 用戶空間“PID”,如ps
或/proc
,實際上是內核中的“TGID”。 當然, clone
有一個標志來確定一個新的輕量級進程是否會有一個新的TGID(因此將它放在一個新的“線程組”中)。
UNIX進程也有“父”和“子”。 Linux task_struct
有一些指針用於實現父子關系。 而且,正如您可能已經猜到的那樣, clone
有一個標志來確定新輕量級進程的父級是什么。 它可以是其稱為進程clone
,或者是所謂的進程的父 clone
。 你能弄清楚在創建“進程”時使用了哪個,在創建“線程”時使用了哪個?
查看手冊頁進行clone
; 這將是非常有教育意義的。 也可以嘗試strace
上采用並行線程看到一個節目clone
使用。
(其中很多都是從記憶中寫出來的;其他人可以根據需要隨意修改)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.