NPTL分析之線程的建立

來源:互聯網
上載者:User

標籤:

NPTL(NativePosix Thread Library)

NPTL包括pthread線程庫以及配套的同步方法,我們這裡暫時只講pthread線程庫的實現。

1. NPTL的起源

在NPTL之前,linux的線程庫是LinuxThreads,該庫部分實現了Posixthreads的規範。其主要特點是線程的調度在使用者態完成,且由一個管理線程來調度。相應的,其缺點也來自於這種架構,導致管理線程帶來了很大的任務切換的開銷。另外一個問題是LinuxThreads對訊號的處理不滿足Posixthreads的規範。

NPTL解決了LinuxThreads的問題,因此,成為了Linux上預設的線程庫。

2.NPTL的實現

提到NPTL與LinuxThreads的區別,很多人會說到1:1和M:N,即使用者態線程與核心態進程的比例問題。NPTL的1:1在我看來是0.5:0.5,因為使用者態看到的那個線程與核心態的進程本來就是同一個進程。是我對NPTL線程的理解:


   中,紅色線以上是使用者態,下面是核心態。左邊虛線框內是單線程的,右邊是多線程的(2個線程)。

   在Linux的眼裡,不會區別進程和線程,在它眼裡只有task_struct。task_struct是用來描述進程(線程)的結構體,其中會記錄一切關於進程的資訊。核心做任務調度的時候,僅僅是選擇一個task_struct而已。這個task_struct是屬於進程還是線程的,核心並不關心。正因為如此,多線程下的無差別調度才能保證(所以,如果你想拖慢別人程式的速度,你可以建立大量的線程)。既然核心中線程和進程都是task_struct,那在使用者態中線程的特性是如何?的?下面將就幾個例子來說明線程的特性是如何?的。

2.1 為什麼線程間可以很容易地訪問全域變數,但是進程間卻不行?

   我們在寫多線程代碼時,線程間共用某些資料非常簡單:弄一個全域變數即可。但是寫多進程的程式時,則必須另外建立一個共用記憶體才能共用資料。為什麼會有這樣的差異?

   簡單地說,是因為線程的實現時線程間共用了虛擬位址空間。一般來說,每個進程都有獨立的虛擬位址空間,其中包括獨立的頁表。那麼,在一個線程組中,每個線程看到的虛擬位址對應的物理地址都是一樣的。而在兩個進程中,同樣的虛擬位址可能就對應不同的物理地址。這就導致線程間的資料共用和進程間有很大的差別,繼而導致線程間的同步方式和進程間有較大差異。

2.2 線程在核心中的資料模型

   首先,看下面這個圖:


不管是通過fork產生的進程還是通過pthread_create產生的線程,其在核心中都對應著一個task_struct。linux_structure圖中有兩個task_struct,其中右邊的task_struct是通過pthread_create產生的,因此左邊的task_struct是threadgroup的leader。task_struct有很多欄位,其中mm就是用來描述虛擬位址空間的。我們可以看到兩個task_struct的mm指向了同一個mm_struct對象,也就是前面提到的線程間共用同一個虛擬位址空間。

   不僅僅是虛擬位址空間,線程間還共用訊號相關的資料。比如,線程間共用相同的訊號處理函數,相同的pending訊號(線程也有獨立的pending訊號,這些訊號是通過pthread_kill發送的,而不是通過kill發送的)。可以看到,signal、sighand和pending欄位都指向相同的對象。既然共用訊號相關的資料,那麼就可以解釋一個問題:多線程程式中,訊號是由哪個線程處理的?答案很簡單:由最先從核心態返回使用者態的線程處理的。訊號處理會作為一個專門的技術問題在以後的技術分享中講解。

   使用者態中,多線程會作為一個進程看待,在核心中,會被抽象為“線程組”。線程組中的task_struct有不同的pid欄位,但是會有相同的tgid(threadgroup id)欄位,這個tgid作為使用者態進程的pid給上層使用。所以,在一個進程下的每個線程中擷取到的pid是相同的,使用者態的pid與核心態的pid不是同一個東西,想擷取線程在核心態的pid可以通過系統調用(gettid)來擷取。線程組中的task_struct會通過一個鏈表(thread_group)連結起來,後面建立的線程的task_struct中的group_leader欄位會指向leader。通過共用mm_struct、fs、signal相關的資料,再通過thread_group相關的設定,線程的概念基本就實現了。

2.3 線程的建立流程,從使用者態到核心

   2.3.1 pthread_create

pthread_create是建立線程的入口,主要參數是一個函數指標,其指向的函數為線程建立成功後要執行的函數。由於線程組中的各個線程是可以並行運行在不同的CPU上的,所以各個線程必須得有自己獨立的使用者態棧和核心棧。Linux使用者態棧的大小一般是8M,通過mmap在MemoryMapping Area中分配一塊記憶體(不考慮使用者態棧緩衝)。在使用者態也有一個資料結構用來描述線程(structpthread),該資料結構就放在使用者態棧的最下面的位置,其中會存放線程建立成功後要執行的函數地址。

分配完structpthread和使用者態棧之後(其實還有很多事情,不過都是些瑣事),差不多就可以帶著這些資訊進入核心申請task_struct了。

   2.3.2 syscall入口

      像建立新的進程這種資源管理工作基本上都要通過核心完成,從使用者態進入核心態有好幾種方式,其中系統調用是主動陷入核心的一種方式。從使用者態進入核心態與從核心態返回使用者態,兩種路徑都是有標準的,由於涉及到模式的轉換,所以與處理器架構有很大的關係。一般來說,從使用者態進入核心態時,都會把進程在使用者態時的狀態儲存在核心棧中,這樣完成了核心態的任務之後返回時可以恢複使用者態的狀態。各種寄存器資訊基本上就儲存在核心棧的頂部的structpt_regs裡面,x86_64架構下pt_regs的欄位如下:


   這個結構體在核心很多地方會見到,在trace系統中有重要的作用,後面會以專題的形式講一下pt_regs的故事。

   glibc在使用者態對clone封裝了一層,名為ARCH_CLONE。其中會把線程建立成功後要執行的函數地址以及參數壓入使用者態棧,然後再調用系統調用clone,這樣系統調用clone返回後再從棧中取出線程函數以及對應的參數,繼而開始執行線程函數。

   2.3.3 clone

      系統調用(syscall)clone用於建立當前進程的一個副本,fork就是調用的clone。我們這裡是要建立線程,那麼對clone的用法當然與fork有所區別了。區別主要在於clone的參數clone_flags的設定:

      建立線程調用的clone:


      建立進程調用的clone:


可以看到建立線程時使用的clone_flags中多了CLONE_VM、CLONE_FS、CLONE_FILES和CLONE_SIGNAL(CLONE_SIGHAND| CLONE_THREAD),這些是實現Posixthread規範的關鍵。這些參數體現在linux_structure圖中就是其中的mm、fs、files、signal、sighand和pending欄位指向相同的對象。正因為這些資料的共用,線程間的資料共用和同步比在進程間簡單得多。

fork建立的進程是當前進程的兒子,pthread_create建立的進程則相當於當前進程的兄弟。CLONE_THREAD這個標記使得建立的進程加入當前的線程組。

clone的過程主要包括task_struct的分配以及相應的資源的拷貝,比較有條理,可以自行查看相關的代碼,重點關注核心棧的建立以及核心棧與task_struct的關聯關係的建立。

2.4 總結

   線程在Linux核心中只是一個邏輯上的概念,並沒有某個資料結構來區分進程或者線程,“線程”是一個輕量級進程,它與其它進程共用了虛擬位址空間,這一特性直接導致多線程和多進程的天壤之別。本文僅僅講了NPTL中線程庫部分(其中的線程建立部分),線程間的同步(futex)以後再講。


著作權聲明:本文為博主原創文章,未經博主允許不得轉載。

NPTL分析之線程的建立

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.