Linux 調度系統全景指南 -下篇-

本文主要是講 Linux 的調度系統, 由於全部內容太多,分三部分來講,本篇是下篇(主要線程和進程),上篇請看(CPU 和中斷):Linux 調度系統全景指南 (上篇),調度可以說是操作系統的靈魂,爲了讓 CPU 資源利用最大化,Linux 設計了一套非常精細的調度系統,對大多數場景都進行了很多優化,系統擴展性強,我們可以根據業務模型和業務場景的特點,有針對性的去進行性能優化,在保證客戶網絡帶寬前提下,隔離客戶互相之間的干擾影響,提高 CPU 利用率,降低單位運算成本,提高市場競爭力。歡迎大家相互交流學習!

目錄

進程

 一般定義是操作系統對一個正在運行的程序的一種抽象, 是運行資源的管理單位(虛擬內存空間,文件句柄,全局變量,信號等運行資源),是操作系統資源分配的最小單位。在 linux 系統下,無論是進程,還是線程,到了內核裏面,我們統一都叫任務(Task),由一個統一的結構 task_struct 進行管理:

詳細結構:

大體分爲下面幾類:

進程運行空間

Linux 按照特權等級,把進程的運行空間分爲內核空間和用戶空間,分別對應着下圖中, CPU 特權等級的 Ring 0 和 Ring 3。

進程內存空間(x86_64)

各個分區的意義:

內核空間:

在 32 位系統中,Linux 會留 1G 空間給內核,用戶進程是無法訪問的,用來存放進程相關數據和內存數據,內核代碼等;在 64 位系統裏面,Linux 會採用最低 48 位來表示虛擬內存,這可通過 /proc/cpuinfo 來查看 address sizes :

address sizes :

36 bits physical, 48 bits virtual,總的虛擬地址空間爲 256TB(2^48),在這 256TB 的虛擬內存空間中, 0000000000000000 - 00007fffffffffff(128TB) 爲用戶空間,ffff800000000000 - ffffffffffffffff(128TB) 爲內核空間, 剩下的是用戶內存空間:

stack 棧區:

專門用來實現函數調用 - 棧結構的內存塊。相對空間下(可以設置大小,Linux 一般默認是 8M,可通過 ulimit –s 查看),系統自動管理,從高地址往低地址,向下生長。

內存映射區:

包括文件映射和匿名內存映射, 應用程序的所依賴的動態庫,會在程序執行時候,加載到內存這個區域,一般包括數據(data)和代碼(text); 通過 mmap 系統調用,可以把特定的文件映射到內存中,然後在相應的內存區域中操作字節來訪問文件內容,實現更高效的 IO 操作;匿名映射,在 glibc 中 malloc 分配大內存的時候會用到匿名映射。這裏所謂的 “大” 表示是超過了 MMAP_THRESHOLD 設置的字節數,它的缺省值是 128 kB,可以通過 mallopt() 去調整這個設置值。還可以用於進程間通信 IPC(共享內存)。

heap 堆區:

主要用於用戶動態內存分配,空間大,使用靈活,但需要用戶自己管理,通過 brk 系統調用控制堆的生長,向高地址生長。

BBS 段和 DATA 段:

用於存放程序全局數據和靜態數據,一般未初始化的放在 BSS 段(統一初始化爲 0,不佔程序文件的空間),初始化的放在 data 段,只讀數據放在 rodata 段(常量存儲區)。

text 段:

主要存放程序二進制代碼。

進程調度

進程狀態機

進程是一個動態的概念,是應用程序當前正在運行的一個實例, 在進程的整個生命週期中,它會處於不同的狀態,並且在不同狀態之間轉化:

R (TASK_RUNNING)-- 執行狀態

只有在該狀態的進程纔可能在 CPU 上運行。而同一時刻可能有多個進程處於可執行狀態,這些進程的 task_struct 結構(進程控制塊)被放入對 應 CPU 的可執行隊列中(一個 task 最多隻能出現在一個 CPU 的可執行隊列中)。進程調度器的任務就是從各個 CPU 的可執行隊列中分別選擇一個 task 在該 CPU 上運行。很多操作系統教科書將正在 CPU 上執行的進程定義爲 RUNNING 狀態,而將可執行但是尚未被調度執行的進程定義爲 READY 狀態,這兩種狀態在 linux 下統一爲 TASK_RUNNING 狀態。

S (TASK_INTERRUPTIBLE)-- 可中斷的睡眠狀態

處於這個狀態的進程因爲等待某個事件的發生(比如等待 socket 連接、等待信號量,等待鎖),而被掛起。這些進程的 task_struct 結構被放入對應事件的等待隊列中。當這些事件發生時(由外部中斷觸發、或由其他進程觸發),對應的等待隊列中的一個或多個進程將被喚醒。通過 ps 命令我們會看到,一般情況下,進程列表中的絕大多數進程都處於 TASK_INTERRUPTIBLE 狀態(除非機器的負載很高)。畢竟 CPU 就一兩個,進程動輒幾十上百個,如果不是絕大多數進程都在睡眠,CPU 又怎麼響應得過來。

T (TASK_STOPPED or TASK_TRACED)-- 暫停狀態或跟蹤狀態

向進程發送一個 SIGSTOP 信號,它就會因響應該信號而進入 TASK_STOPPED 狀態(除非該進程本身處於 TASK_UNINTERRUPTIBLE 狀態而不響應信號)。(SIGSTOP 與 SIGKILL 信號一樣,是非常強制的。不允許用戶進程通過 signal 系列的系統調用重新設置對應的信號處理函數。)向進程發送一個 SIGCONT 信號,可以讓其從 TASK_STOPPED 狀態恢復到 TASK_RUNNING 狀態。

Z (TASK_DEAD - EXIT_ZOMBIE)-- 退出狀態

進程成爲殭屍進程。當父進程遺漏了用 wait() 函數等待已終止的子進程時,子進程就會進入一種無父進程的狀態,此時子進程就是殭屍進程。

D (TASK_UNINTERRUPTIBLE)-- 不可中斷的睡眠狀態

TASK_INTERRUPTIBLE 狀態類似,進程處於睡眠狀態,但是此刻進程是不可中斷的。不可中斷,指的並不是 CPU 不響應外部硬件的中斷,而是指進程不響應異步信號。絕大多數情況下,進程處在睡眠狀態時,總是應該能夠響應異步信號的。否則你將驚奇的發現,kill -9 竟然殺不死一個正在睡眠的進程了!於是我們也很好理解,爲什麼 ps 命令看到的進程幾乎不會出現 TASK_UNINTERRUPTIBLE 狀態,而總是 TASK_INTERRUPTIBLE 狀態。而 TASK_UNINTERRUPTIBLE 狀態存在的意義就在於,內核的某些處理流程是不能被打斷的。如果響應異步信號,程序的執行流程中就會被插入一段用於處理異步信號的流程(這個插入的流程可能只存在於內核態,也可能延伸到用戶態),於是原有的流程就被中斷了。

進程上下文切換

上下文切換 (有時也稱做進程切換或任務切換):是指 CPU 從一個進程或線程切換到另一個進程或線程。簡潔描述一下,上下文切換可以認爲是內核(操作系統的核心)在 CPU 上對於進程(包括線程)進行以下的活動:

  1. 掛起一個進程,將這個進程在 CPU 中的狀態(上下文)存儲於內存中的某處;

  2. 在內存中檢索下一個進程的上下文並將其在 CPU 的寄存器中恢復;

  3. 跳轉到程序計數器所指向的位置(即跳轉到進程被中斷時的代碼行),以恢復該進程。

因此上下文是指某一時間點 CPU 寄存器和程序計數器的內容,廣義上還包括內存中進程的虛擬地址映射信息。上下文切換隻能發生在內核態中,上下文切換通常是計算密集型的。也就是說,它需要相當可觀的處理器時間,在每秒幾十上百次的切換中,每次切換都需要納秒量級的時間。所以,上下文切換對系統來說意味着消耗大量的 CPU 時間,事實上,可能是操作系統中時間消耗最大的操作。Linux 相比與其他操作系統(包括其他類 Unix 系統)有很多的優點,其中有一項就是,其上下文切換和模式切換的時間消耗非常少。

進程優先級

  1. 進程的優先級有動態優先級和靜態優先級決定;

  2. 它是決定進程在 CPU 的執行順序的數字;

  3. 優先級越高被 CPU 執行的概率越大;

  4. 內核採用啓發式算法決定是開啓或者關閉動態優先級,可以通過修改 nice 級別直接修改進程的靜態優先級,而獲取更多 CPU 執行時間;

  5. Linux 支持的 nice 級別從 19(最低優先級)到 - 20(最高優先級),默認只是 0。只有 root 身份的用戶才能把進程的 nice 級別調整爲負數(讓其具備較高優先級)。

時間片

調度框架

實際上進程是資源管理的單位,線程纔是調度的單位,內核統稱爲任務調度。操作系統最重要的任務就是把系統中的 task 調度到各個 CPU 上去執行, 不同的任務有不同的需求,因此我們需要對任務進行分類:一種是普通進程,另外一種是實時進程。對於實時進程,毫無疑問快速響應的需求是最重要的,而對於普通進程,我們需要兼顧前三點的需求。相信你也發現了,這些需求是互相沖突的,對於這些 time-sharing 的普通進程如何平衡設計呢?這裏需要進一步將普通進程細分爲交互式進程(interactive processs)和批處理進程(batch process)。交互式進程需要和用戶進行交流,因此對調度延遲比較敏感,而批處理進程屬於那種在後臺默默幹活的,因此它更注重 throughput 的需求。當然,無論如何,分享時間片的普通進程還是需要兼顧公平,不能有人大魚大肉,有人連湯都喝不上。爲了達到這些設計目標,調度器必須要考慮某些調度因素,比如說 “優先級”、“時間片” 等。在 Linux 內核中,優先級就是實時進程調度的主要考慮因素。而對於普通進程,如何細分時間片則是調度器的核心思考點。過大的時間片會嚴重損傷系統的響應延遲,讓用戶明顯能夠感知到延遲,卡頓,從而影響用戶體驗。較小的時間片雖然有助於減少調度延遲,但是頻繁的切換對系統的 throughput 會造成嚴重的影響。因爲這時候大部分的 CPU 時間用於進程切換,而忘記了它本來的功能其實就是推動任務的執行。由於 Linux 是一個通用操作系統,它的目標是星辰大海,既能運行在嵌入式平臺上,又能在服務器領域中獲得很好的性能表現,此外在桌面應用場景中,也不能讓用戶有較差的用戶體驗。Linux 任務調度算法核心就是解決調度優化問題:

(1)公平:對於 time-sharing 的進程,保證每個進程得到合理的 CPU 時間。

(2)高效:使 CPU 保持忙碌狀態,即總是有進程在 CPU 上運行。

(3)響應時間:使交互用戶的響應時間儘可能短。

(4)週轉時間:使批處理用戶等待輸出的時間儘可能短。

(5)吞吐量:使單位時間內處理的進程數量儘可能多。

Linux 調度器採用了模塊化設計的思想,從而把進程調度的軟件分成了兩個層次,一個是 core scheduler layer,另外一個是 specific scheduler layer:

從功能層面上看,進程調度仍然分成兩個部分,第一個部分是通過負載均衡模塊將各個 runnable task 根據負載情況平均分配到各個 CPU runqueue 上去。第二部分的功能是在各個 CPU 的 Main scheduler 和 Tick scheduler 的驅動下進行單個 CPU 上的調度。調度器處理的 task 各不相同,有 RT task,有 normal task,有 Deal line task,但是無論哪一種 task,它們都有共同的邏輯,這部分被抽象成 Core scheduler layer,同時各種特定類型的調度器定義自己的 sched_class,並以鏈表的形式加入到系統中。這樣的模塊化設計可以方便用戶根據自己的場景定義 specific scheduler,而不需要改動 Core scheduler layer 的邏輯。

2 個調度器

可以用兩種方法來激活調度:

調度策略

linux 內核目前實現了 6 種調度策略 (即調度算法),用於對不同類型的進程進行調度, 或者支持某些特殊的功能:

調度器類

調度類順序

調度實體

這種一般性要求調度器不直接操作進程,而是處理可調度實體, 因此需要一個通用的數據結構描述這個調度實體,即 seched_entity 結構, 其實際上就代表了一個調度對象,可以是一個進程,也可以是一個進程組,linux 中針對當前可調度的實時和非實時進程,定義了類型爲 seched_entity 的 3 個調度實體:

調度類算法

CFS(Completely Fair Scheduler)算法(完全公平調度器,對於普通進程)

Realtime Scheduler(實時)

實時系統是這樣的一種計算系統:當事件發生後,它必須在確定的時間範圍內做出響應。在實時系統中,產生正確的結果不僅依賴於系統正確的邏輯動作,而且依賴於邏輯動作的時序。換句話說,當系統收到某個請求,會做出相應的動作以響應該請求,想要保證正確地響應該請求,一方面邏輯結果要正確,更重要的是需要在最後期限(deadline)內作出響應。如果系統未能在最後期限內進行響應,那麼該系統就會產生錯誤或者缺陷。在多任務操作系統中(如 Linux),實時調度器(realtime scheduler)負責協調實時任務對 CPU 的訪問,以確保系統中所有的實時任務在其 deadline 內完成,爲了滿足實時任務的調度需求,Linux 提供了兩種實時調度器:POSIX realtime scheduler(後文簡稱 RT 調度)和 deadline scheduler(後文簡稱 DL 調度器)。

實時進程的優先級範圍 [0~99] 都高於普通進程[100~139],始終優先於普通進程得到運行,爲了防止普通進程飢餓,Linux kernel 有一個 RealTime Throttling 機制,就是爲了防止 CPU 消耗型的實時進程霸佔所有的 CPU 資源而造成整個系統失去控制。它的原理很簡單,就是保證無論如何普通進程都能得到一定比例(默認 5%)的 CPU 時間,可以通過兩個內核參數來控制:

Deadline Task Scheduling

Stop_Sched_Class

stop_sched_class 用於停止 CPU, 一般在 SMP 系統上使用, 用以實現負載平衡和 CPU 熱插拔. 這個類有最高的調度優先級, stop 調度器類實現了 Unix 的 stop_machine 特性, stop_machine 是一個通信信號 : 在 SMP 的情況下相當於暫時停止其他的 CPU 的運行, 它讓一個 CPU 繼續運行,而讓所有其他 CPU 空閒。在單 CPU 的情況下這個東西就相當於關中斷。一般來說,內核會在如下情況下使用 stop_machine 技術:

Idle_Sched_Class

idle 任務會被任意進程搶佔,其專屬調度器類爲 idle-task,當 CPU 沒有任務空閒時,默認的 idle 實現是 hlt 指令,hlt 指令使 CPU 處於暫停狀態,等待硬件中斷髮生的時候恢復,從而達到節能的目的。即從處理器 C0 態變到 C1 態 (見 ACPI 標準),讓 CPU 置爲 WFI(Wait for interrupt)低功耗狀態,以節省功耗,多 cpu 系統中每個 cpu 一個 idle 進程。

組調度

linux 內核實現了 control group 功能(cgroup,since linux 2.6.24),可以支持將進程分組,然後按組來劃分各種資源。比如:group-1 擁有 30% 的 CPU 和 50% 的磁盤 IO、group-2 擁有 10% 的 CPU 和 20% 的磁盤 IO、等等。cgroup 支持很多種資源的劃分,CPU 資源就是其中之一,這就引出了組調度,

linux 內核中,傳統的調度程序是基於進程來調度的, 以進程爲單位來瓜分 CPU 資源,如果我們想把進程進行分組,以進程組進行瓜分 CPU 資源,Linux 實現了組調度架構來實現這個需求:

                          Linux 組調度實現架構

組的調度策略

組調度的主要針對 rt(實時調度)和 cfs(完全公平調度)兩種類別:

實時進程的組調度

實時進程是對 CPU 有着實時性要求的進程,它的優先級是跟具體任務相關的,完全由用戶來定義的。調度器總是會選擇優先級最高的實時進程來運行,發展到組調度,組的優先級就被定義爲 “組內最高優先級的進程所擁有的優先級。

普通進程的組調度支持 (Fair Group Scheduling)

2.6.23 引入的 CFS 調度器對所有進程完全公平對待。但是依然有個問題:設想當前機器有 2 個用戶,有一個用戶跑着 9 個進程,且都是 CPU 密集型進程;另一個用戶只跑着一個 X 進程,是交互性進程。從 CFS 的角度看,它將平等對待這 10 個進程,結果導致的是跑 X 進程的用戶受到不公平對待,他只能得到約 10% 的 CPU 時間,讓他的體驗相當差。基於此,組調度的概念被引入 [6]。CFS 處理的不再是一個進程的概念,而是調度實體(sched entity),一個調度實體可以只包含一個進程,也可以包含多個進程。因此,上述例子的困境可以這麼解決:分別爲每個用戶建立一個組,組裏放該用戶所有進程,從而保證用戶間的公平性。該功能是基於控制組(control group, cgroup) 的概念,需要內核開啓 CGROUP 的支持纔可使用。

信號處理

信號機制是進程之間相互傳遞消息的一種方法,信號全稱爲軟中斷信號,也有人稱作軟中斷。從它的命名可以看出,它的實質和使用很像中斷。所以,信號可以說是進程控制的一部分:

信號分類:

(1) 與進程終止相關的信號。當進程退出,或者子進程終止時,發出這類信號。 
(2) 與進程例外事件相關的信號。如進程越界,或企圖寫一個只讀的內存區域(如程序正文區),或執行一個特權指令及其他各種硬件錯誤。 
(3) 與在系統調用期間遇到不可恢復條件相關的信號。如執行系統調用 exec 時,原有資源已經釋放,而目前系統資源又已經耗盡。 
(4) 與執行系統調用時遇到非預測錯誤條件相關的信號。如執行一個並不存在的系統調用。 
(5) 在用戶態下的進程發出的信號。如進程調用系統調用 kill 向其他進程發送信號。 
(6) 與終端交互相關的信號。如用戶關閉一個終端,或按下 break 鍵等情況。 
(7) 跟蹤進程執行的信號。 

多線程信號處理:

  1. 不要在線程的信號掩碼中阻塞不能被忽略處理的兩個信號 SIGSTOP 和 SIGKILL。

  2. 不要在線程的信號掩碼中阻塞 SIGFPE、SIGILL、SIGSEGV、SIGBUS。

  3. 確保 sigwait() 等待的信號集已經被進程中所有的線程阻塞。

  4. 在主線程或其它工作線程產生信號時,必須調用 kill() 將信號發給整個進程,而不能使用 pthread_kill() 發送某個特定的工作線程,否則信號處理線程無法接收到此信號。

  5. 因爲 sigwait()使用了串行的方式處理信號的到來,爲避免信號的處理存在滯後,或是非實時信號被丟失的情況,處理每個信號的代碼應儘量簡潔、快速,避免調用會產生阻塞的庫函數。

**         線程**

線程(英語:thread)是操作系統能夠進行運算調度的最小單位。大部分情況下,它被包含在進程之中,是進程中的實際運作單位。一條線程指的是進程中一個單一順序的控制流,一個進程中可以併發多個線程,每條線程並行執行不同的任務。在 Unix System V 及 SunOS 中也被稱爲輕量進程(lightweight processes),但輕量進程更多指內核線程(kernel thread),而把用戶線程(user thread)稱爲線程。一個進程的組成實體可以分爲兩大部分:線程集和資源集。進程中的線程是動態的對象;代表了進程指令的執行。資源,包括地址空間、打開的文件、用戶信息等等,由進程內的線程共享。

                              線程和進程的關係

根據操作系統內核是否對線程可感知,可以把線程分爲內核線程用戶線程

分類的標準主要是線程的調度者在覈內還是在覈外。前者更利於併發使用多處理器的資源,而後者則更多考慮的是上下文切換開銷。Linux 內核只提供了輕量進程的支持,限制了更高效的線程模型的實現,但 Linux 着重優化了進程的調度開銷,一定程度上也彌補了這一缺陷。目前最流行的線程機制 LinuxThreads 所採用的就是 “線程 - 進程” 一對一模型(還存在多對一,多對多模型)用戶級實現一個包括信號處理在內的線程管理機制。

        Linux 線程實現採用內核級線程” 一對一” 模型

在 linux 系統下,無論是進程,還是線程,到了內核裏面,我們統一都叫任務(Task),由一個統一的結構 task_struct 進行管理。一個進程由於其運行空間的不同,從而有內核線程和用戶進程的區分。內核線程運行在內核空間,之所以稱之爲線程是因爲它沒有虛擬地址空間,只能訪問內核的代碼和數據; 而用戶進程則運行在用戶空間, 不能直接訪問內核的數據但是可以通過中斷,系統調用等方式從用戶態陷入內核態,但是內核態只是進程的一種狀態, 與內核線程有本質區別,用戶進程運行在用戶空間上,而一些通過共享資源實現的一組進程我們稱之爲線程組。Linux 下內核其實本質上沒有線程的概念,Linux 下線程其實上是與其他進程共享某些資源的進程而已。但是我們習慣上還是稱它們爲線程或者輕量級進程。

內核線程

內核線程是直接由內核本身啓動的進程。內核線程實際上是將內核函數委託給獨立的進程,它與內核中的其他進程” 並行” 執行。內核線程經常被稱之爲內核守護進程,他們執行下列任務:

內核線程主要有兩種類型:

內核線程由內核自身生成,其特點在於:

Linux 在內核線程架構設計中,內核線程建立和銷燬都是由操作系統負責、通過系統調用完成的。在內核的支持下運行,無論是用戶進程的線程,或者是系統進程的線程,他們的創建、撤銷、切換都是依靠內核實現的。線程管理的所有工作由內核完成,應用程序沒有進行線程管理的代碼,只有一個到內核級線程的編程接口. 內核爲進程及其內部的每個線程維護上下文信息,調度也是在內核基於線程架構的基礎上完成。內核線程駐留在內核空間,它們是內核對象。

內核線程就是內核的分身,一個分身可以處理一件特定事情。Linux 內核使用內核線程來將內核分成幾個功能模塊,像 kworker,  kswapd, ksoftirqd,  migration , rcu_bh,rcu_schd, watchdog 等 (內核線程都用 [] 括起來)。這在處理異步事件如異步 IO,阻塞任務,延後任務處理時特別有用。內核線程的使用是廉價的,唯一使用的資源就是內核棧和上下文切換時保存寄存器的空間,內核線程只運行在內核態,不受用戶態上下文的拖累,在多核系統中,很多內核線程都是 per cpu 運行粒度。

用戶線程

Linux 內核只提供了輕量級進程 (LWP) 的方式支持用戶線程,限制了更高效的線程模型的實現,但 Linux 着重優化了進程的調度開銷,一定程度上也彌補了這一缺陷。目前最流行的線程機制 LinuxThreads 所採用的就是線程 - 進程”一對一”模型,調度交給核心,而在用戶級實現一個包括信號處理在內的線程管理機制。輕量級進程由 clone()系統調用創建,參數是 CLONE_VM,即與父進程是共享進程地址空間和系統資源。

LinuxThreads 是用戶空間的線程庫,所採用的是 “線程 - 進程”1 對 1 模型(即一個用戶線程對應一個輕量級進程,而一個輕量級進程對應一個特定的內核線程),將線程的調度等同於進程的調度,調度交由內核完成, 有了內核線程,每個用戶線程被映射或綁定到一個內核線程。用戶線程在其生命期內都會綁定到該內核線程。調度器管理、調度並分派這些線程。運行時庫爲每個用戶級線程請求一個內核級線程。而線程的創建、同步、銷燬由核外線程庫完成(LinuxThtreads 已綁定到 GLIBC 中發行)。  在 LinuxThreads 中,由專門的一個管理線程處理所有的線程管理工作。當進程第一次調用 pthread_create() 創建線程時就會先 創建 (clone()) 並啓動管理線程。後續進程 pthread_create()創建線程時,都是管理線程作爲 pthread_create()的調用者的子線程,通過調用 clone()來創建用戶線程,並記錄輕量級進程號和線程 id 的映射關係,因此用戶線程其實是管理線程的子線程。LinuxThreads 只支持調度範圍爲 PTHREAD_SCOPE_SYSTEM 的調度,默認的調度策略是 SCHED_OTHER。 用戶線程調度策略也可修改成 SCHED_FIFO 或 SCHED_RR 方式,這兩種方式支持優先級爲 0-99, 而 SCHED_OTHER 只支持 0。  

                            Linux 輕量級進程實現

LinuxThtreads 的設計存在一些侷限性,導致後面 Linux 實現了新的線程庫 NPTL,或稱爲 Native POSIX Thread Library,是 Linux 線程的一個新實現,它克服了 LinuxThreads 的缺點,同時也符合 POSIX 的需求。與 LinuxThreads 相比,它在性能和穩定性方面都提供了重大的改進。與 LinuxThreads 一樣,NPTL 也實現了一對一的模型。

輕量級進程具有侷限性:

優點:

(1)運行代價:LWP 只有一個最小的執行上下文和調度程序所需的統計信息。

(2)處理器競爭:因與特定內核線程關聯,因此可以在全系統範圍內競爭處理器資源。

(3)使用資源:與父進程共享進程地址空間。

(4)調度:像普通進程一樣調度。

協程

以上描述的不管是中斷,進程,線程(內核線程,用戶線程(輕量級進程實現))的調度都是由內核掌控,用戶並不能直接干預,要在用戶態實現對邏輯調度控制,需要實現類似用戶級線程,用戶級線程是完全建立在用戶空間的線程庫,用戶線程的創建、調度、同步和銷燬全部庫函數在用戶空間完成,不需要內核的幫助。因此這種線程是極其低消耗和高效的。協程本質上也是一種用戶級線程實現,在一個線程(內核執行單元)內,協程通過主動放棄時間片交由其他協程執行來協作,故名協程。協程的一些關鍵點:

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/tuCtWOh6O54LRa5g3UFJJA