作多線程應用開發,對於線程的理解是很是重要的,咱們要爲咱們建立的每個線程負責。這篇文章主要聊聊操做系統線程相關的主題,在瞭解線程定義、用戶態與內核態、模態切換、線程上下文切換的基礎之上再對常見的三種線程模型進行進一步介紹,但願對你們可以有所幫助。java
什麼是線程?《POSIX Threads Programming》中有一段話對線程的定義進行描述:git
A thread is defined as an independent stream of instructions that can be scheduled to run as such by the operating system.
線程能夠被認爲是一個能夠被獨立調度的實體,這個實體共享進程的地址空間、文件描述符、代碼和數據,且擁有本身私有的棧、寄存器上下文、和程序計數器。github
咱們在github上面給開源項目提交代碼的時候,按照comment格式都要寫Motivation這部分,咱們今天討論線程這個存在,也要討論線程爲何存在。安全
在不少應用中須要同時執行多個任務,這些任務大部分甚至所有均可以相互獨立的並行的執行。好比一個網絡代理,傳統的實現是用一個進程做爲監聽器來監聽網絡端口,當有客戶端鏈接進來的時候,當前進程將會fork一個新的進程來處理客戶端的請求。這種體系結構很差的地方以下:網絡
線程的出現就是爲了解決這些問題,線程之間擁有共享的進程空間用於共享數據、也有本身獨立的運行空間相似一個輕量級的進程。多線程
在理解用戶線程與內核線程以前、咱們有必要了解一下用戶空間與內核空間。現代操做系統的地址空間主要基於虛擬地址空間機制設計,和實際物理內存大小不要緊,好比對於32位操做系統,它的尋址空間爲2的32次方也就是4G,這裏的尋址空間被稱爲虛擬存儲空間。操做系統的核心是內核,獨立於普通應用程序,具備最高權限,能夠訪問底層硬件設備以及受保護的空間,所以這部分包括驅動程序和操做系統。操做系統的設計者爲了保證內核的安全,將用戶進程設計爲只有必定權限的程序,它不可以操做內核以及硬件。操做系統將虛擬存儲空間劃分爲兩部分,一部分是內核空間,一部分是用戶空間。針對Linux操做系統而言,最高的1G字節供內核使用,稱爲內核空間,較低的3G字節供給各個進程使用,被稱爲用戶空間。進程能夠經過系統調用進入內核,Linux內核由全部進程共享。用戶空間和內核空間示意圖以下:併發
用戶空間及內核空間jvm
每一個進程都擁有全部的虛擬地址空間,當進程運行用戶代碼的時候是運行在用戶地址空間的,這時候CPU運行所須要的指令和數據都保存在用戶空間,進程能夠認爲是指令+數據+CPU,所以這個時候咱們把這個狀態的進程叫作用戶進程。當用戶執行系統調用而陷入內核代碼中執行的時候,當前進程運行的指令和數據都在內核空間,所以咱們把這個狀態的進程叫內核進程。用戶進程和內核進程不是獨立的兩個進程的意思,而是進程運行的不一樣狀態。值得注意的是,用戶進程不能訪問內核虛擬地址空間,內核進程能夠訪問所有的虛擬地址空間,所以用戶進程和內核進程進行數據交換隻能經過內核進程從用戶地址空間取數據,而後放入用戶地址空間。性能
系統調用涉及到進程從用戶態到內核態的切換(mode switch),這個時候涉及到的切換主要是寄存器上下文的切換,和一般所說的進程上下文切換不一樣,mode switch的消耗相對要小不少。spa
上面能夠看出,用戶線程與內核線程的區別主要在於指令與數據運行於不一樣虛擬地址空間,用戶線程和內核線程也能夠叫作用戶空間線程和內核空間線程。用戶線程由用戶代碼支持,內核線程由操做系統內核支持。
線程上下文切換和線程模態切換不是一個維度的東東,線程上下文切換講的是多線程之間由於調度器的調度,而從一個線程正在被調度切換到另一個線程被調度的事情。線程上下文切換必需要保存線程執行的寄存器狀態、棧信息、線程正文、數據等,所以相對模態切換是比較重的操做。
線程模型在不一樣的操做系統下的實現一般有三種,每種模型都有其優勢與缺點,下面咱們來看看這三種線程模型。
一個多線程子系統有可能所有由用戶代碼實現,這些線程的調度與切換所有發生在用戶地址空間,這種模型一般是由一個內核線程和多個用戶線程組成。典型的實現是基於POSIX線程draft 4,OSF'DCE是其中一種具體實現。一個用戶空間庫負責線程的建立、終止、調度與同步。這些線程對於操做系統內核是透明的。
這種模型的好處是線程上下文切換都發生在用戶空間,避免的模態切換(mode switch),從而對於性能有積極的影響。然而很差的地方是全部的線程基於一個內核調度實體即內核線程,這意味着只有一個處理器能夠被利用,在多處理環境下這是不可以被接受的,本質上,用戶線程只解決了併發問題,可是沒有解決並行問題。
還有一點,若是線程由於I/O操做陷入了內核態,內核態線程阻塞等待I/O數據,則全部的線程都將會被阻塞,用戶空間也可使用非阻塞而I/O,可是仍是有性能及複雜度問題。
用戶空間線程模型
對於用戶空間線程模型,全部的用戶線程都和特定的內核線程進行交互,而內核空間線程模型是每一個用戶線程都和一個特定的內核線程進行交互,用戶線程和內核線程是1:1的關係。典型的實現是將每一個用戶線程映射到一個內核線程上。
每一個線程由內核調度器獨立的調度,因此若是一個線程阻塞則不影響其餘的線程。然而,建立、終止和同步線程都會發生在內核地址空間,這可能會帶來較大的性能問題。在建立線程的時候內核必需要進行內存鎖的申請,並負責調度線程,並且每一個線程都要消耗有限的內核資源,當大量的線程被建立的時候,體現的尤其明顯。值得誇獎的是,在多核處理器的硬件的支持下,內核空間線程模型支持了真正的並行,下面是內核空間模型示意圖:
內核空間線程模型
內核用戶空間線程模型中,內核線程和用戶線程的數量比爲M : N,所以也一般被叫作M : N線程模型,內核用戶空間綜合了前兩種的優勢。
這種模型須要內核線程調度器和用戶空間線程調度器相互操做,本質上是多個線程被綁定到了多個內核線程上,這使得大部分的線程上下文切換都發生在用戶空間,而多個內核線程又能夠充分利用處理器資源,模型圖以下:
內核用戶空間線程模型
最近在作jvm線程實現相關研究,發現java線程的問題實際上是基於C++和pthread線程庫的問題,往下深刻,也就是操做系統對於線程實現的問題,這篇文章記錄了我對操做系統線程技術的認識和理解。