下面這個是 top
命令的介面,相信大家應該都不陌生。
top - 19:01:38 up 91 days, 23:06, 1 user, load average: 0.00, 0.01, 0.05
Tasks: 151 total, 1 running, 149 sleeping, 1 stopped, 0 zombie
%Cpu(s): 0.0 us, 0.1 sy, 0.0 ni, 99.8 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 8010420 total, 5803596 free, 341300 used, 1865524 buff/cache
KiB Swap: 0 total, 0 free, 0 used. 6954384 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
13436 root 20 0 1382776 28040 5728 S 0.3 0.4 251:21.06 n9e-collector
1 root 20 0 43184 3384 2212 S 0.0 0.0 5:15.64 systemd
2 root 20 0 0 0 0 S 0.0 0.0 0:00.28 kthreadd
3 root 20 0 0 0 0 S 0.0 0.0 0:00.58 ksoftirqd/0
5 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 kworker/0:0H
7 root rt 0 0 0 0 S 0.0 0.0 0:35.48 migration/0
%Cpu(s):
這一行表示的是 CPU 不同時間的佔比,其中大家比較熟悉的應該是 system time
與 user time
:
user time
佔比應該最高,這是程序執行應用程式碼的的時間佔比(CPU 密集)system time
佔用率高,則意味著存在頻繁的系統呼叫(IO 密集)或者一些潛在的效能問題不熟悉的朋友可以參考下面這張圖(來源於極客時間的課程):
接下來我們將探究隱藏在這些時間背後的操作原理。
作業系統的核心功能就是管理硬體資源,因此不可避免會使用到一些直接操作硬體的CPU指令,這類指令我們稱之為特權指令。特權指令如果使用不當,將會導致整個系統的崩潰,因此作業系統提供了一組特殊的資源存取程式碼 —— 核心kernel
來負責執行這些指令。
作業系統將虛擬地址空間劃分為兩部分:
kernel memotry
:存放核心程式碼和資料(程序間共用)user memotry
:存放使用者程式的程式碼和資料(相互隔離)通過區分核心空間和使用者空間的設計,隔離了作業系統程式碼與應用程式程式碼。即便是單個應用程式出現錯誤也不會影響到作業系統的穩定性,這樣其它的程式還可以正常的執行。
應用程式通過核心提供的介面,存取 CPU、記憶體、I/O 等硬體資源,我們將該過程稱為系統呼叫system call
。系統呼叫是作業系統的最小功能單位。
每個程序處於活動狀態時,可能處於以下兩種狀態之一:
每次執行系統呼叫時,都需要經歷以下變化:
而之前的 user time
與 system time
分別就是對應 CPU 在使用者態與核心態的執行時間。
當發生以下狀況時,執行緒會被掛起,並由系統排程其他執行緒執行:
sleep
主動掛起同個程序下的執行緒共用程序的使用者態空間,因此當同個程序的執行緒發生切換時,都需要經歷以下變化:
不同執行緒的使用者態空間資源是相互隔離的,當不同程序的執行緒發生切換時,都需要經歷以下變化:
每次儲存和恢復上下文的過程,都是在系統態進行的,並且需要幾十納秒到數微秒的 CPU 時間。當切換次數較多時會耗費大量的 system time
,進而大大縮短了真正執行程序的 user time
。
當用戶執行緒過多時,會引起大量的上下文切換,導致不必要的效能開銷。
Linux 中的執行緒是從父程序 fork
出的輕量程序,它們共用父程序的記憶體空間。
Linux 的排程策略是搶佔式的,每個執行緒都有優先順序prirority
的概念,並按照優先順序高低分為兩種:
每個 CPU 都有自己的執行佇列 runqueue
,需要執行的執行緒會被加入到這個佇列中。
每個佇列可以進一步細分為 3 個佇列以及 5 種排程策略:
dl_rq
SCHED_DEADLINE
選擇 deadline
距離當前時間點最近的任務執行rt_rq
—— 可以互相搶佔的實時任務
SCHED_FIFO
一旦搶佔到 CPU 資源,就會一直執行直到退出,除非被高優先順序搶佔SCHED_RR
當 CPU 時間片用完,核心會把它放到佇列末尾,可以被高優先順序搶佔cfs_rq
—— 公平佔用 CPU 時間的普通任務
SCHED_NORMAL
普通程序SCHED_BATCH
後臺程序Linux 核心在選擇下一個任務執行時,會按照該順序來進行選擇,也就是先從 dl_rq
裡選擇任務,然後從 rt_rq
裡選擇任務,最後從 cfs_rq
裡選擇任務。所以實時任務總是會比普通任務先得到執行。
實時程序的優先順序總是高於普通程序,因此當系統中有實時程序執行時,普通程序幾乎是無法分到時間片的。
為了保證 cfs_rq
佇列的公平性,Linux 採用完全公平排程演演算法 CFS Completely Fair Scheduler
進行排程,保證每個普通程序都儘可能被排程到。
CFS 引入了 vruntime
作為衡量是否公平的依據:
vruntime
與任務佔用的 CPU 時間成正比vruntime
與任務優先順序成反比(優先順序越高vruntime
增長越慢)如果一個任務的 vruntime
較小,說明它以前佔用 CPU 的時間較短,受到了不公平對待,因此該程序會被優先排程,從而到達所謂的公平性。
為了實現可控的排程,Linux 為普通程序引入了 nice
值的概念。其的取值其範圍是 -20 ~ +19
,調整該值會改變程序的優先順序:prirority += nice
。
與此同時 vruntime
計算也會受到影響:
當用戶程序設定了一個大於 0 的 nice 值時,其使用者態的執行時間將被統計為nice time
而不是 user time
。簡單來說,nice time
表示 CPU 花了多少時間用於執行低優先順序的任務。
當 nice time
佔比比較高時,通常是某些定時任務排程器導致的:它們會為後臺任務程序設定一個較大的 nice
值,避免這些程序與其他執行緒爭搶 CPU 資源。
中斷就是一種插隊機制,可以讓作業系統優先處理一些緊急的任務。當硬體裝置(例如,網路卡)需要向 CPU 發出訊號時(例如,資料已到達),就會產生硬體中斷。
CPU 接收到中斷時,會切換到核心態執行特定的中斷服務,並且期間不允許其他中斷搶佔(關中斷)。
當中斷服務需要執行較長時間時,可能會導致且其他的中斷得不到及時的響應。
為了提高中斷處理效率,作業系統在之前的基礎上把中斷處理分成兩部分:
top half
:在遮蔽中斷的上下文中執行,用於完成關鍵性的處理動作bottom half
:不在中斷服務上下文中執行,主要處理不那麼急迫但耗時的任務核心在處理完中斷上半部後,可以延期執行下半部,該機制被稱為軟中斷softirq
。
軟中斷處理的過程是不會關中斷的,因此當有硬中斷到來的時候,可以及時響應。
構成軟中斷機制的核心元素包括:
irq_stat
softirq_vec
daemon
呼叫open_softirq()
將軟中斷服務程式註冊到軟中斷向量表softirq_vec
(可選)
呼叫raise_softirq()
觸發軟中斷事務
irq_stat
daemon
執行緒daemon
執行緒被喚醒後會執行do_softirq()
處理軟中斷
irq_stat
是否存發生軟中斷事件softirq_vec
中對應的軟中斷服務程式irq_stat
,如果發現新的軟中斷,就會喚醒ksoftrqd
執行緒來處理我們知道 CPU 執行的優先順序為:硬中斷 > 軟中斷 > 普通程序。
這意味著:
為了保證公平性,核心為每個 CPU 都設定一個ksoftrqd
執行緒。如果所有的軟中斷在短時間內無法被處理完,核心就會喚醒ksoftrqd
處理剩餘的軟中斷。以下面這張圖為例:
raise_softirq()
觸發軟中斷,喚醒daemon
daemon
被喚醒開始處理軟中斷daemon
發現仍有未處理的軟中斷,喚醒ksoftrqd
ksoftrqd
獲得 CPU 時間片後,繼續處理未完成的軟中斷由於 ksoftrqd
其實是一個 nice
值為 0 的普通執行緒,會進入 cfs_rq
參與排程,可以和普通程序公平地使用 CPU。
但如果 ksoftrirqd
長時間得不到 CPU,就會致使軟中斷的延遲變得很大,因此 ksoftirqd
的實時性是很難得到保障。
典型問題是 ping 延遲:如果 ping 包無法在軟中斷裡得到處理,就會被 ksoftirqd
處理,導致 ping 延遲變得很大。
硬中斷的優先順序很高,但是需要的 CPU 時間極少。當出現大量硬中斷時,可能會引起較多的 CPU 使用者態與核心態的切換,但是interrupt time
不會顯著上升。
此外,由於部分核心程式碼是不可重入的(例如,修改暫存器),其執行過程不能打斷。因此這些程式碼的執行過程中,會遮蔽掉硬中斷。
關中斷的操作在核心裡隨處可見,這反過來會給硬中斷帶來一些影響。比如,程序關中斷時間太長會導致網路報文無法及時處理,進而引起業務效能抖動。
而軟中斷的執行時間如果太長,就會給使用者執行緒帶來延遲,如果 softirq time
很大則很可能意味著使用者執行緒會受到影響。
網路 IO 頻繁的應用機器的 softirq time
通常會比較高,可能存在網路連線數過多,或者網路流量過大的情況,
ksoftirqd
的優先順序與使用者執行緒是一致的,因此,如果軟中斷處理常式是在 ksoftirqd
裡執行的,那它可能會有一些延遲。
在 GNU top命令中,steal time
定義為 「虛擬機器器管理程序 hypervisor
從 VM 竊取的時間」。 該概念是Xen,KVM,VMware 等社群或者廠商推廣到Linux社群的。
當系統管理程序和 VM 嘗試佔用同一物理 CPU 核 pCPU
時,會導致 VM 的虛擬 CPU vCPU
可用的處理器時間減少,從而造成 VM 效能下降。
中虛擬化環境中,可能發生時間竊取的一些情況:
vCPU
的執行在同個 pCPU
上(公有云的 CPU 超賣)vCPU
與執行緒繫結在了某個特定的 pCPU
上,導致虛擬主機 vhost
程序處理 I/O 時從這些 vCPU
上竊取時間pCPU
https://www.cnblogs.com/Anker/p/3269106.html
https://zhuanlan.zhihu.com/p/69554144
https://blog.csdn.net/helloanthea/article/details/28877221
https://blog.csdn.net/lenomirei/article/details/79274073
https://www.jianshu.com/p/673c9e4817a8
https://opensource.com/article/20/1/cpu-steal-time
https://www.cnblogs.com/menkeyi/p/6732020.html
https://blog.csdn.net/zxh2075/article/details/78262568
https://kb.cnblogs.com/page/207897/
https://www.cnblogs.com/charlesblc/p/6255806.html
http://www.wowotech.net/linux_kenrel/soft-irq.html