61秒,摸透Linux的健康狀態!

語言: CN / TW / HK

作業系統作為所有程式的載體,對應用的效能影響是非常重要的。然而計算機各個元件之間的速度,是非常不均衡的。拿CPU和硬碟的速度來說,比兔子和烏龜的速度差別還要大。

下面將簡單的介紹CPU、記憶體、I/O的一些基本知識,以及一些如何評估它們效能的命令。

1.CPU

首先介紹計算機中最重要的計算元件:中央處理器。一般我們可以通過top命令來觀測它的效能。

1.1 top命令

top​命令可用於觀測CPU的一些執行指標。如圖,進入top​命令之後,按1鍵即可看到每核CPU的詳細狀況。

CPU的使用有多個維度的指標,以下分別說明一下:

us使用者態所佔用的CPU百分比。

sy核心態所佔用的CPU百分比。如果這個值過高,需要配合vmstat命令,檢視是否是上下文切換是否頻繁。

ni高優先順序應用所佔用的CPU百分比。

wa等待I/O裝置所佔用的CPU百分比。如果這個值非常高,輸入輸出裝置可能存在非常明顯的瓶頸。

hi硬體中斷所佔用的CPU百分比。

si軟中斷所佔用的CPU百分比。

st這個一般發生在虛擬機器上,指的是虛擬CPU等待實際CPU時間的百分比。如果這個值過大,則你的宿主機壓力可能過大。如果你是雲主機,則你的服務商可能存在超賣。

id空閒CPU百分比。

一般的,我們比較關注空閒CPU的百分比,它可以從整體上體現CPU的利用情況。

1.2 什麼是負載

我們還要評估CPU任務執行的排隊情況,這些值就是負載(load)。top命令,顯示的CPU負載,分別是最近1分鐘、5分鐘、15分鐘的數值。

如圖,以單核作業系統為例,將CPU資源抽象成一條單向行駛的馬路。則會發生三種情況:

馬路上的車只有4輛,車輛暢通無阻,load大約是0.5。

馬路上的車有8輛,正好能首尾相接安全通過,此時load大約為1。

馬路上的車有12輛,除了在馬路上的8輛車,還有4輛等在馬路外面,需要排隊。此時load大約為1.5。

那load為1代表的是啥?針對這個問題,誤解還是比較多的。

很多同學認為,load達到1,系統就到了瓶頸,這不完全正確。load的值和cpu核數息息相關。舉例如下:

單核的負載達到1,總load的值約1。

雙核的每核負載都達到1,總load約2。

四核的每核負載都達到1,總load約為4。

所以,對於一個load到了10,卻是16核的機器,你的系統還遠沒有達到負載極限。通過uptime命令,同樣能夠看到負載情況。

1.3 vmstat

要看CPU的繁忙程度,還可以通過vmstat命令。下面是vmstat命令的一些輸出資訊。

我們比較關注的有下面幾列:

  • b 存在於等待佇列的核心執行緒數目,比如等待I/O等。數字過大則cpu太忙。
  • cs 代表上下文切換的數量。如果頻繁的進行上下文切換,就需要考慮是否是執行緒數開的過多。
  • si/so 顯示了交換分割槽的一些使用情況,交換分割槽對效能的影響比較大,需要格外關注。
$ vmstat 1
procs ---------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
34  0    0 200889792  73708 591828    0    0     0     5    6   10 96  1  3  0  0
32  0    0 200889920  73708 591860    0    0     0   592 13284 4282 98  1  1  0  0
32  0    0 200890112  73708 591860    0    0     0     0 9501 2154 99  1  0  0  0
32  0    0 200889568  73712 591856    0    0     0    48 11900 2459 99  0  0  0  0
32  0    0 200890208  73712 591860    0    0     0     0 15898 4840 98  1  1  0  0
^C

2.記憶體

2.1 觀測命令

要想了解記憶體對效能的一些影響,就需要從作業系統層面來看一下記憶體的分佈。

我們在平常寫完程式碼後,比如寫了一個C++程式,如果去檢視它的彙編,可以看到其中的記憶體地址,並不是實際的實體記憶體地址。

那麼應用程式所使用的,就是邏輯記憶體,這個學過計算機組成結構的同學都有了解。

邏輯地址可以對映到實體記憶體和虛擬記憶體上。比如你的實體記憶體是8GB,分配了16GB的SWAP分割槽,那麼應用可用的總記憶體就是24GB。

從top命令可以看到幾列資料,注意方塊括起來的三個區域,解釋如下:

VIRT這裡就是虛擬記憶體,一般比較大,不用做過多關注。

RES我們平常關注的就是這一列的數值,它代表了程序實際佔用的記憶體。平常在做監控時,也主要是監控這個數值。

SHR指的是共享記憶體,比如可以複用的一些so檔案等。

2.2 CPU快取

由於CPU核記憶體之間的速度差異是非常大的,解決方式就是加入快取記憶體。其實,這些快取記憶體,往往會有多層,如下圖。

Java有大部分知識點是圍繞多執行緒的,那是因為,如果一個執行緒的時間片跨越了多個CPU,那麼就會存在同步問題。

在Java中,最典型的和CPU快取相關的知識點,就是併發程式設計中,針對Cache line的偽共享(false sharing)問題。

偽共享是指:在這些快取記憶體中,是以快取行為單位進行儲存的。哪怕你修改了快取行中一個很小很小的資料,它都會整個的重新整理。所以,當多執行緒修改一些變數的值時,如果這些變數在同一個快取行裡,就會造成頻繁重新整理,無意中影響彼此的效能。

通過以下命令即可看到當前作業系統的快取行大小。

cat /sys/devices/system/cpu/cpu0/cache/index0/coherency_line_size

通過以下命令可以看到不同層次的快取大小。

[[email protected] ~]# cat /sys/devices/system/cpu/cpu0/cache/index1/size
32K
[[email protected] ~]# cat /sys/devices/system/cpu/cpu0/cache/index2/size
256K
[[email protected] ~]# cat /sys/devices/system/cpu/cpu0/cache/index3/size
20480K

在JDK8以上的版本,通過開啟引數-XX:-RestrictContended​,就可以使用註解@sun.misc.Contended進行補齊,來避免偽共享的問題。在併發優化中,我們再詳細講解。

2.3 HugePage

回頭看我們最長的那副圖,上面有一個叫做TLB的元件,它的速度雖然高,但容量也是有限的。這就意味著,如果實體記憶體很大,那麼對映表的條目將會非常多,會影響CPU的檢索效率。

預設記憶體是以4K的page來管理的。如圖,為了減少對映表的條目,可採取的辦法只有增加頁的尺寸。像這種將Page Size加大的技術,就是Huge Page。

HugePage有一些副作用,比如競爭加劇,Redis還有專門的研究(https://redis.io/topics/latency) ,但在一些大記憶體的機器上,開啟後會一定程度上增加效能。

2.4 預先載入

另外,一些程式的預設行為,也會對效能有所影響。比如JVM的-XX:+AlwaysPreTouch引數。預設情況下,JVM雖然配置了Xmx、Xms等引數,但它的記憶體在真正用到時,才會分配。

但如果加上這個引數,JVM就會在啟動的時候,把所有的記憶體預先分配。這樣,啟動時雖然慢了些,但執行時的效能會增加。

3.I/O

3.1 觀測命令

I/O裝置可能是計算機裡速度最差的元件了。它指的不僅僅是硬碟,還包括外圍的所有裝置。

硬碟有多慢呢?我們不去探究不同裝置的實現細節,直接看它的寫入速度(資料未經過嚴格測試,僅作參考)。

可以看到普通磁碟的隨機寫和順序寫相差是非常大的。而隨機寫完全和cpu記憶體不在一個數量級。

緩衝區依然是解決速度差異的唯一工具,在極端情況比如斷電等,就產生了太多的不確定性。這些緩衝區,都容易丟。

最能體現I/O繁忙程度的,就是top命令和vmstat​命令中的wa%。如果你的應用,寫了大量的日誌,I/O wait就可能非常的高。

對於硬碟來說,可以使用iostat命令來檢視具體的硬體使用情況。只要%util超過了80%,你的系統基本上就跑不動了。

詳細介紹如下:

  • %util最重要的判斷引數。一般地,如果該引數是100%表示裝置已經接近滿負荷運行了
  • Device表示發生在哪塊硬碟。如果你有多快,則會顯示多行
  • avgqu-sz這個值是請求佇列的飽和度,也就是平均請求佇列的長度。毫無疑問,佇列長度越短越好。
  • await響應時間應該低於5ms,如果大於10ms就比較大了。這個時間包括了佇列時間和服務時間
  • svctm表示平均每次裝置I/O​操作的服務時間。如果svctm​的值與await​很接近,表示幾乎沒有I/O​等待,磁碟效能很好,如果await​的值遠高於svctm​的值,則表示I/O佇列等待太長,系統上執行的應用程式將變慢。

3.2 零拷貝

kafka比較快的一個原因就是使用了zero copy。所謂的Zero copy,就是在操作資料時, 不需要將資料buffer從一個記憶體區域拷貝到另一個記憶體區域。因為少了一次記憶體的拷貝, CPU的效率就得到提升。

我們來看一下它們之間的區別:

要想將一個檔案的內容通過socket傳送出去,傳統的方式需要經過以下步驟:

  • 將檔案內容拷貝到核心空間。
  • 將核心空間的內容拷貝到使用者空間記憶體,比如Java應用。
  • 使用者空間將內容寫入到核心空間的快取中。
  • socket讀取核心快取中的內容,傳送出去。

零拷貝又多種模式,我們拿sendfile來說明。如上圖,在核心的支援下,零拷貝少了一個步驟,那就是核心快取向用戶空間的拷貝。即節省了記憶體,也節省了CPU的排程時間,效率很高。

4.網路

除了iotop、iostat這些命令外,sar命令可以方便的看到網路執行狀況,下面是一個簡單的示例,用於描述入網流量和出網流量。

$ sar -n DEV 1
Linux 3.13.0-49-generic (titanclusters-xxxxx)  07/14/2015     _x86_64_    (32 CPU)

12:16:48 AM     IFACE   rxpck/s   txpck/s    rxkB/s    txkB/s   rxcmp/s   txcmp/s  rxmcst/s   %ifutil
12:16:49 AM      eth0  18763.00   5032.00  20686.42    478.30      0.00      0.00      0.00      0.00
12:16:49 AM        lo     14.00     14.00      1.36      1.36      0.00      0.00      0.00      0.00
12:16:49 AM   docker0      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00

12:16:49 AM     IFACE   rxpck/s   txpck/s    rxkB/s    txkB/s   rxcmp/s   txcmp/s  rxmcst/s   %ifutil
12:16:50 AM      eth0  19763.00   5101.00  21999.10    482.56      0.00      0.00      0.00      0.00
12:16:50 AM        lo     20.00     20.00      3.25      3.25      0.00      0.00      0.00      0.00
12:16:50 AM   docker0      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
^C

當然,我們可以選擇性的只看TCP的一些狀態。

$ sar -n TCP,ETCP 1
Linux 3.13.0-49-generic (titanclusters-xxxxx)  07/14/2015    _x86_64_    (32 CPU)

12:17:19 AM  active/s passive/s    iseg/s    oseg/s
12:17:20 AM      1.00      0.00  10233.00  18846.00

12:17:19 AM  atmptf/s  estres/s retrans/s isegerr/s   orsts/s
12:17:20 AM      0.00      0.00      0.00      0.00      0.00

12:17:20 AM  active/s passive/s    iseg/s    oseg/s
12:17:21 AM      1.00      0.00   8359.00   6039.00

12:17:20 AM  atmptf/s  estres/s retrans/s isegerr/s   orsts/s
12:17:21 AM      0.00      0.00      0.00      0.00      0.00
^C

5.End

不要寄希望於這些指標,能夠立刻幫助我們定位效能問題。這些工具,只能夠幫我們大體猜測發生問題的地方,它對效能問題的定位,只是起到輔助作用。想要分析這些bottleneck,需要收集更多的資訊。

想要獲取更多的效能資料,就不得不借助更加專業的工具,比如基於eBPF的BCC工具,這些牛x的工具我們將在其他文章裡展開。讀完本文,希望你能夠快速的瞭解Linux的執行狀態,對你的系統多一些掌控。

作者簡介:小姐姐味道 (xjjdog),一個不允許程式設計師走彎路的公眾號。聚焦基礎架構和Linux。十年架構,日百億流量,與你探討高併發世界,給你不一樣的味道。