天天看點

線程技術與線程實作模型

前言

做多線程應用開發,對于線程的了解是非常重要的,我們要為我們建立的每一個線程負責。這篇文章主要聊聊作業系統線程相關的主題,在了解線程定義、使用者态與核心态、模态切換、線程上下文切換的基礎之上再對常見的三種線程模型進行進一步介紹,希望對大家能夠有所幫助。

一、線程定義

什麼是線程?《POSIX Threads Programming》中有一段話對線程的定義進行描述:

A thread is defined as an independent stream of instructions that can be scheduled to run as such by the operating system.

線程可以被認為是一個可以被獨立排程的實體,這個實體共享程序的位址空間、檔案描述符、代碼和資料,且擁有自己私有的棧、寄存器上下文、和程式計數器。

二、為什麼要線程

我們在 github 上面給開源項目送出代碼的時候,按照 comment 格式都要寫 Motivation 這部分,我們今天讨論線程這個存在,也要讨論線程為什麼存在。

在很多應用中需要同時執行多個任務,這些任務大部分甚至全部都可以互相獨立的并行的執行。比如一個網絡代理,傳統的實作是用一個程序作為監聽器來監聽網絡端口,當有用戶端連接配接進來的時候,目前程序将會fork一個新的程序來處理用戶端的請求。這種體系結構不好的地方如下:

  • fork 系統調用對于作業系統來說是一個非常重的操作。
  • 每一個程序都有自己獨立的位址空間,程序間互相通信必須要通過标準的 IPC 技術來實作,比如信号量、共享記憶體,這些操作是非常昂貴的、嚴重影響系統性能。線程的出現就是為了解決這些問題,線程之間擁有共享的程序空間用于共享資料、也有自己獨立的運作空間類似一個輕量級的程序。

三、使用者空間與核心空間

在了解使用者線程與核心線程之前、我們有必要了解一下使用者空間與核心空間。現代作業系統的位址空間主要基于虛拟位址空間機制設計,和實際實體記憶體大小沒關系,比如對于32位作業系統,它的尋址空間為2的32次方也就是4G,這裡的尋址空間被稱為虛拟存儲空間。作業系統的核心是核心,獨立于普通應用程式,具有最高權限,可以通路底層硬體裝置以及受保護的空間,是以這部分包括驅動程式和作業系統。作業系統的設計者為了保證核心的安全,将使用者程序設計為隻有一定權限的程式,它不能夠操作核心以及硬體。作業系統将虛拟存儲空間劃分為兩部分,一部分是核心空間,一部分是使用者空間。針對Linux作業系統而言,最高的1G位元組供核心使用,稱為核心空間,較低的3G位元組供給各個程序使用,被稱為使用者空間。程序可以通過系統調用進入核心,Linux核心由所有程序共享。使用者空間和核心空間示意圖如下:

線程技術與線程實作模型

四、使用者态與核心态

每個程序都擁有所有的虛拟位址空間,當程序運作使用者代碼的時候是運作在使用者位址空間的,這時候 CPU 運作所需要的指令和資料都儲存在使用者空間,程序可以認為是指令+資料+CPU,是以這個時候我們把這個狀态的程序叫做使用者程序。當使用者執行系統調用而陷入核心代碼中執行的時候,目前程序運作的指令和資料都在核心空間,是以我們把這個狀态的程序叫核心程序。使用者程序和核心程序不是獨立的兩個程序的意思,而是程序運作的不同狀态。值得注意的是,使用者程序不能通路核心虛拟位址空間,核心程序可以通路全部的虛拟位址空間,是以使用者程序和核心程序進行資料交換隻能通過核心程序從使用者位址空間取資料,然後放入使用者位址空間。系統調用涉及到程序從使用者态到核心态的切換(mode switch),這個時候涉及到的切換主要是寄存器上下文的切換,和通常所說的程序上下文切換不同,mode switch的消耗相對要小很多。

五、使用者線程與核心線程

上面可以看出,使用者線程與核心線程的差別主要在于指令與資料運作于不同虛拟位址空間,使用者線程和核心線程也可以叫做使用者空間線程和核心空間線程。使用者線程由使用者代碼支援,核心線程由作業系統核心支援。

六、線程上下文切換

線程上下文切換和線程模态切換不是一個次元的東東,線程上下文切換講的是多線程之間因為排程器的排程,而從一個線程正在被排程切換到另外一個線程被排程的事情。線程上下文切換必須要儲存線程執行的寄存器狀态、棧資訊、線程正文、資料等,是以相對模态切換是比較重的操作。

七、線程模型

線程模型在不同的作業系統下的實作通常有三種,每種模型都有其優點與缺點,下面我們來看看這三種線程模型。

八、使用者空間線程模型(M : 1)

一個多線程子系統有可能全部由使用者代碼實作,這些線程的排程與切換全部發生在使用者位址空間,這種模型通常是由一個核心線程和多個使用者線程組成。典型的實作是基于POSIX線程draft 4,OSF'DCE是其中一種具體實作。一個使用者空間庫負責線程的建立、終止、排程與同步。這些線程對于作業系統核心是透明的。這種模型的好處是線程上下文切換都發生在使用者空間,避免的模态切換(mode switch),進而對于性能有積極的影響。然而不好的地方是所有的線程基于一個核心排程實體即核心線程,這意味着隻有一個處理器可以被利用,在多處理環境下這是不能夠被接受的,本質上,使用者線程隻解決了并發問題,但是沒有解決并行問題。還有一點,如果線程因為I/O操作陷入了核心态,核心态線程阻塞等待I/O資料,則所有的線程都将會被阻塞,使用者空間也可以使用非阻塞而I/O,但是還是有性能及複雜度問題。

線程技術與線程實作模型

九、核心空間線程模型(1:1)

對于使用者空間線程模型,所有的使用者線程都和特定的核心線程進行互動,而核心空間線程模型是每個使用者線程都和一個特定的核心線程進行互動,使用者線程和核心線程是1:1的關系。典型的實作是将每個使用者線程映射到一個核心線程上。每個線程由核心排程器獨立的排程,是以如果一個線程阻塞則不影響其他的線程。然而,建立、終止和同步線程都會發生在核心位址空間,這可能會帶來較大的性能問題。在建立線程的時候核心必須要進行記憶體鎖的申請,并負責排程線程,而且每個線程都要消耗有限的核心資源,當大量的線程被建立的時候,展現的尤為明顯。值得誇獎的是,在多核處理器的硬體的支援下,核心空間線程模型支援了真正的并行,下面是核心空間模型示意圖:

線程技術與線程實作模型

十、核心使用者空間線程模型(M : N)

核心使用者空間線程模型中,核心線程和使用者線程的數量比為M : N,是以也通常被叫做M : N線程模型,核心使用者空間綜合了前兩種的優點。這種模型需要核心線程排程器和使用者空間線程排程器互相操作,本質上是多個線程被綁定到了多個核心線程上,這使得大部分的線程上下文切換都發生在使用者空間,而多個核心線程又可以充分利用處理器資源,模型圖如下:

線程技術與線程實作模型