1. 程式人生 > 其它 >理解linux的CPU上下文切換

理解linux的CPU上下文切換

理解linux系統的CPU上下文切換

前言

linux是一個多工作業系統,它支援遠大於CPU數量的任務同時執行。當然,這個同時執行不是真的同時執行,而是系統在很短的時間內輪流分配CPU資源,由於CPU的速度很快,所以給人一種同時執行的錯覺。

每個任務執行前,CPU需要知道任務從哪載入、從哪開始執行,也就是需要系統設定好任務的CPU暫存器和程式計數器。這倆是CPU執行任何任務前所必須的依賴環境,因此也被叫做CPU上下文。而CPU上下文切換便是,先把前一個任務的CPU上下文儲存起來,然後載入新任務的上下文到CPU暫存器和程式計數器中,最後再跳轉到程式計數器所指向的新位置來執行新任務。

根據任務的不同,CPU的上下文切換可以分為幾個不同的場景:程序上下文切換、執行緒上下文切換,以及中斷上下文切換。

程序上下文切換

linux程序的執行空間分為核心空間和使用者空間。核心空間具有最高許可權,可以訪問所有資源;使用者空間只能訪問受限資源,不能直接訪問記憶體等硬體裝置,必須通過系統呼叫陷入核心,才能訪問特權資源。當程序在使用者空間執行時,被稱為程序的使用者態;當程序陷入到核心空間,被稱為程序的核心態。

使用者態通過系統呼叫陷入核心態的時候,會發生CPU上下文切換。系統需要先儲存使用者態的上下文,然後載入核心態程式碼的上下文,執行完核心態任務後,再進行一次CPU上下文切換,切換回使用者態的上下文。因此一次系統呼叫會發生兩次CPU上下文切換。這裡需要注意,系統呼叫過程一直是同一個程序在執行,不是一個程序切換到另一個程序。

程序是由核心管理和排程的,程序的切換隻能在核心空間。因此,程序的上下文不僅包括虛擬記憶體、棧、全域性變數等使用者空間的資源,還包括了核心堆疊、暫存器等核心空間的狀態。由於程序上下文較多,切換時候不僅包括使用者態資訊,還包括核心態資訊,所以上下文切換時耗費的時間更多。雖然一般只需要納秒級的時間,但程序上下文切換次數較多的時候,總體耗費的時間就比較可觀了。在大量程序上下文切換的情況下,CPU將大量時間耗費在暫存器、核心棧、虛擬記憶體等資源的儲存和恢復上,進而縮短了真正執行程序的時間。

Linux通過TLB(Translation Lookaside Buffer)來管理虛擬記憶體到實體記憶體的對映關係。當虛擬記憶體更新後,TLB也需要重新整理,記憶體的訪問也會隨之變慢。

只有在程序排程的時候,才需要切換程序上下文。linux為每個CPU維護了一個就序佇列,將活躍程序(在執行和等待中的程序)按照優先順序和等待CPU的時間排序,然後選擇最需要CPU的程序,也就是優先順序最高和等待時間最長的程序來執行。

觸發程序排程的常見場景:

  • 程序的CPU時間片用完,就會被系統掛起,切換到其他程序。
  • 程序所需的系統資源不足時,需要等到資源滿足後才可以執行,這時候也會被掛起。
  • 程序通過sleep()函式主動掛起。
  • 有優先順序更高的程序需要執行時,當前程序會被掛起。
  • 硬體中斷時,程序會被掛起。

執行緒上下文切換

程序是資源分配的基本單位,執行緒是排程的基本單位。程序內有多個執行緒時,這些執行緒會共享相同的虛擬記憶體和全域性變數等資源,這些資源在上下文切換時是不需要修改的。執行緒也有自己的私有資源,比如棧和暫存器等,這些在上下文切換時也需要儲存。

  • 執行緒上下文切換前後的兩個執行緒如果不屬於同一個程序,此時資源不共享,切換過程同進程上下文切換。
  • 切換前後的兩個執行緒若屬於同一個程序,共享的虛擬記憶體等資源保持不動,切換執行緒的私有資源即可。在這種情況下,切換消耗的資源更少。

中斷上下文切換

為了快速響應硬體的時間,中斷處理會打斷程序的正常排程和執行,轉而呼叫中斷處理程式。

中斷上下文切換並不涉及程序的使用者態,當中斷髮生在一個正處於使用者態的程序時,不需要儲存和恢復這個程序的使用者態資源。中斷上下文值包括核心態中斷服務程式執行所必需的狀態,包括CPU暫存器、核心堆疊、硬體中斷引數等。

檢視系統的上下文切換情況

使用vmstat(apt或yum安裝sysstat)可檢視系統的上下文切換情況

# 每2秒輸出一組報告,共1份報告
vmstat 2 1
procs  -----------memory----------  ---swap--  -----io----  -system--  ------cpu-----
r      b                            交換       空閒         緩衝       快取            si  so  bi   bo   in   cs   us  sy  id  wa  st
1      0                            0          2118592      100844     1011392         0   0   211  259  188  249  3   1   96  0   0
  • cs(context switch):每秒上下文切換的次數。此處為249
  • in(interrupt): 每秒中斷的次數。此處為188
  • r(running or runnable): 就緒佇列的長度,即正在執行和等待CPU的程序數
  • b(blocked): 處於不可中斷睡眠狀態的程序數

檢視每個程序的上下文切換情況

使用pidstat(同樣依賴於sysstat)可檢視程序的上下文切換情況。

# 每3秒輸出一組報告,-w表示輸出程序的上下文切換指標,-wt表示輸出執行緒的上下文切換指標
pidstat -w 3

13時53分56秒   UID       PID   cswch/s nvcswch/s  Command
13時53分59秒     0         7      4.95      0.00  kworker/0:1-ata_sff
13時53分59秒     0        13      2.64      0.00  rcu_sched
13時53分59秒     0        14      0.33      0.00  migration/0
13時53分59秒     0        19      0.33      0.00  migration/1
13時53分59秒     0        31      1.98      0.00  kcompactd0
13時53分59秒     0       122      1.32      0.00  kworker/1:1H-kblockd
13時53分59秒     0       247      0.99      0.33  jbd2/dm-0-8
13時53分59秒   116       501      0.66      0.00  avahi-daemon
13時53分59秒     0       505      0.99      0.00  NetworkManager
13時53分59秒   104       541      0.33      0.00  rsyslogd
13時53分59秒     0       549      0.33      0.00  wpa_supplicant
13時53分59秒     0      5102      2.31      0.33  kworker/1:1-events
13時53分59秒     0      5576      1.98      0.00  kworker/u4:0-events_freezable_power_
13時53分59秒  1000      5707      0.33      0.00  pidstat
  • cswch: 每秒自願上下文切換的次數(資源不足時,程序自願上下文切換)
  • nvcswch: 每秒非自願上下文切換的次數(時間片到期等原因,被系統強制排程而發生的上下文切換)

檢視系統中斷情況

# 持續檢視/proc/interrupts, 並高亮變化的地方
watch -d cat /proc/interrupts

           CPU0       CPU1       
  0:         29          0   IO-APIC   2-edge      timer
  1:        602          0   IO-APIC   1-edge      i8042
  8:          0          0   IO-APIC   8-edge      rtc0
  9:          0          0   IO-APIC   9-fasteoi   acpi
 12:          0        366   IO-APIC  12-edge      i8042
 14:          0          0   IO-APIC  14-edge      ata_piix
 15:          0       2563   IO-APIC  15-edge      ata_piix
 18:          0          2   IO-APIC  18-fasteoi   vboxvideo
 19:      13747      23341   IO-APIC  19-fasteoi   enp0s3
 20:       8310          0   IO-APIC  20-fasteoi   vboxguest
 21:      17084      18354   IO-APIC  21-fasteoi   ahci[0000:00:0d.0], snd_intel8x0
 22:         25          0   IO-APIC  22-fasteoi   ohci_hcd:usb1
NMI:          0          0   Non-maskable interrupts
LOC:     309756     154510   Local timer interrupts
SPU:          0          0   Spurious interrupts
PMI:          0          0   Performance monitoring interrupts
IWI:          0          0   IRQ work interrupts
RTR:          0          0   APIC ICR read retries
RES:      95556      93683   Rescheduling interrupts
CAL:       7548       8726   Function call interrupts
TLB:       7126       6564   TLB shootdowns
TRM:          0          0   Thermal event interrupts
THR:          0          0   Threshold APIC interrupts
DFR:          0          0   Deferred Error APIC interrupts
MCE:          0          0   Machine check exceptions
MCP:          9          9   Machine check polls
ERR:          0
MIS:          0
PIN:          0          0   Posted-interrupt notification event
NPI:          0          0   Nested posted-interrupt event
PIW:          0          0   Posted-interrupt wakeup event
  • RES:重排程中斷。表示喚醒空閒狀態的CPU來排程新的任務執行。

參考

  • 極客時間 - linux效能優化實戰