61秒,摸透Linux的健康狀態(tài)!
操作系統(tǒng)作為所有程序的載體,對應(yīng)用的性能影響是非常重要的。然而計(jì)算機(jī)各個組件之間的速度,是非常不均衡的。拿CPU和硬盤的速度來說,比兔子和烏龜?shù)乃俣炔顒e還要大。
下面將簡單的介紹CPU、內(nèi)存、I/O的一些基本知識,以及一些如何評估它們性能的命令。
1.CPU
首先介紹計(jì)算機(jī)中最重要的計(jì)算組件:中央處理器。一般我們可以通過top命令來觀測它的性能。
1.1 top命令
top
命令可用于觀測CPU的一些運(yùn)行指標(biāo)。如圖,進(jìn)入top
命令之后,按1
鍵即可看到每核CPU的詳細(xì)狀況。

CPU的使用有多個維度的指標(biāo),以下分別說明一下:
us?用戶態(tài)所占用的CPU百分比。
sy?內(nèi)核態(tài)所占用的CPU百分比。如果這個值過高,需要配合vmstat命令,查看是否是上下文切換是否頻繁。
ni?高優(yōu)先級應(yīng)用所占用的CPU百分比。
wa?等待I/O設(shè)備所占用的CPU百分比。如果這個值非常高,輸入輸出設(shè)備可能存在非常明顯的瓶頸。
hi?硬件中斷所占用的CPU百分比。
si?軟中斷所占用的CPU百分比。
st?這個一般發(fā)生在虛擬機(jī)上,指的是虛擬CPU等待實(shí)際CPU時間的百分比。如果這個值過大,則你的宿主機(jī)壓力可能過大。如果你是云主機(jī),則你的服務(wù)商可能存在超賣。
id??空閑CPU百分比。
一般的,我們比較關(guān)注空閑CPU的百分比,它可以從整體上體現(xiàn)CPU的利用情況。
1.2 什么是負(fù)載
我們還要評估CPU任務(wù)執(zhí)行的排隊(duì)情況,這些值就是負(fù)載
(load)。top命令,顯示的CPU負(fù)載,分別是最近1分鐘、5分鐘、15分鐘的數(shù)值。

如圖,以單核操作系統(tǒng)為例,將CPU資源抽象成一條單向行駛的馬路。則會發(fā)生三種情況:
馬路上的車只有
4
輛,車輛暢通無阻,load大約是0.5。馬路上的車有8輛,正好能首尾相接安全通過,此時load大約為1。
馬路上的車有12輛,除了在馬路上的8輛車,還有4輛等在馬路外面,需要排隊(duì)。此時load大約為1.5。
那load為1代表的是啥?針對這個問題,誤解還是比較多的。
很多同學(xué)認(rèn)為,load達(dá)到1,系統(tǒng)就到了瓶頸,這不完全正確。load的值和cpu核數(shù)息息相關(guān)。舉例如下:
單核的負(fù)載達(dá)到1,總load的值約1。
雙核的每核負(fù)載都達(dá)到1,總load約2。
四核的每核負(fù)載都達(dá)到1,總load約為4。
所以,對于一個load到了10,卻是16核的機(jī)器,你的系統(tǒng)還遠(yuǎn)沒有達(dá)到負(fù)載極限。通過uptime命令,同樣能夠看到負(fù)載情況。
1.3 vmstat
要看CPU的繁忙程度,還可以通過vmstat命令。下面是vmstat命令的一些輸出信息。

我們比較關(guān)注的有下面幾列:
b
?存在于等待隊(duì)列的內(nèi)核線程數(shù)目,比如等待I/O等。數(shù)字過大則cpu太忙。cs
?代表上下文切換的數(shù)量。如果頻繁的進(jìn)行上下文切換,就需要考慮是否是線程數(shù)開的過多。si
/so
?顯示了交換分區(qū)的一些使用情況,交換分區(qū)對性能的影響比較大,需要格外關(guān)注。
2.內(nèi)存
2.1 觀測命令

要想了解內(nèi)存對性能的一些影響,就需要從操作系統(tǒng)層面來看一下內(nèi)存的分布。
我們在平常寫完代碼后,比如寫了一個C++程序,如果去查看它的匯編,可以看到其中的內(nèi)存地址,并不是實(shí)際的物理內(nèi)存地址。
那么應(yīng)用程序所使用的,就是邏輯內(nèi)存,這個學(xué)過計(jì)算機(jī)組成結(jié)構(gòu)的同學(xué)都有了解。
邏輯地址可以映射到物理內(nèi)存和虛擬內(nèi)存上。比如你的物理內(nèi)存是8GB,分配了16GB的SWAP分區(qū),那么應(yīng)用可用的總內(nèi)存就是24GB。
從top命令可以看到幾列數(shù)據(jù),注意方塊括起來的三個區(qū)域,解釋如下:

VIRT?這里就是虛擬內(nèi)存,一般比較大,不用做過多關(guān)注。
RES??我們平常關(guān)注的就是這一列的數(shù)值,它代表了進(jìn)程實(shí)際占用的內(nèi)存。平常在做監(jiān)控時,也主要是監(jiān)控這個數(shù)值。
SHR?指的是共享內(nèi)存,比如可以復(fù)用的一些so文件等。
2.2 CPU緩存
由于CPU核內(nèi)存之間的速度差異是非常大的,解決方式就是加入高速緩存。其實(shí),這些高速緩存,往往會有多層,如下圖。

Java有大部分知識點(diǎn)是圍繞多線程的,那是因?yàn)?,如果一個線程的時間片跨越了多個CPU,那么就會存在同步問題。
在Java中,最典型的和CPU緩存相關(guān)的知識點(diǎn),就是并發(fā)編程中,針對Cache line
的偽共享(false sharing)問題。
偽共享是指:在這些高速緩存中,是以緩存行
為單位進(jìn)行存儲的。哪怕你修改了緩存行中一個很小很小的數(shù)據(jù),它都會整個的刷新。所以,當(dāng)多線程修改一些變量的值時,如果這些變量在同一個緩存行里,就會造成頻繁刷新,無意中影響彼此的性能。
通過以下命令即可看到當(dāng)前操作系統(tǒng)的緩存行大小。
通過以下命令可以看到不同層次的緩存大小。
在JDK8以上的版本,通過開啟參數(shù)-XX:-RestrictContended
,就可以使用注解@sun.misc.Contended
進(jìn)行補(bǔ)齊,來避免偽共享的問題。在并發(fā)優(yōu)化中,我們再詳細(xì)講解。
2.3 HugePage
回頭看我們最長的那副圖,上面有一個叫做TLB
的組件,它的速度雖然高,但容量也是有限的。這就意味著,如果物理內(nèi)存很大,那么映射表的條目將會非常多,會影響CPU的檢索效率。
默認(rèn)內(nèi)存是以4K
的page來管理的。如圖,為了減少映射表的條目,可采取的辦法只有增加頁的尺寸。像這種將Page Size加大的技術(shù),就是Huge Page。

HugePage有一些副作用,比如競爭加劇,Redis還有專門的研究(https://redis.io/topics/latency) ,但在一些大內(nèi)存的機(jī)器上,開啟后會一定程度上增加性能。
2.4 預(yù)先加載
另外,一些程序的默認(rèn)行為,也會對性能有所影響。比如JVM的-XX:+AlwaysPreTouch
參數(shù)。默認(rèn)情況下,JVM雖然配置了Xmx、Xms等參數(shù),但它的內(nèi)存在真正用到時,才會分配。
但如果加上這個參數(shù),JVM就會在啟動的時候,把所有的內(nèi)存預(yù)先分配。這樣,啟動時雖然慢了些,但運(yùn)行時的性能會增加。
3.I/O
3.1 觀測命令
I/O設(shè)備可能是計(jì)算機(jī)里速度最差的組件了。它指的不僅僅是硬盤,還包括外圍的所有設(shè)備。
硬盤有多慢呢?我們不去探究不同設(shè)備的實(shí)現(xiàn)細(xì)節(jié),直接看它的寫入速度(數(shù)據(jù)未經(jīng)過嚴(yán)格測試,僅作參考)。

可以看到普通磁盤的隨機(jī)寫和順序?qū)懴嗖钍欠浅4蟮?。?code class="js_darkmode__78">隨機(jī)寫完全和cpu內(nèi)存不在一個數(shù)量級。
緩沖區(qū)依然是解決速度差異的唯一工具,在極端情況比如斷電等,就產(chǎn)生了太多的不確定性。這些緩沖區(qū),都容易丟。
最能體現(xiàn)I/O繁忙程度的,就是top命令和vmstat
命令中的wa%
。如果你的應(yīng)用,寫了大量的日志,I/O wait就可能非常的高。

對于硬盤來說,可以使用iostat命令來查看具體的硬件使用情況。只要%util超過了80%,你的系統(tǒng)基本上就跑不動了。

詳細(xì)介紹如下:
%util?最重要的判斷參數(shù)。一般地,如果該參數(shù)是100%表示設(shè)備已經(jīng)接近滿負(fù)荷運(yùn)行了
Device?表示發(fā)生在哪塊硬盤。如果你有多快,則會顯示多行
avgqu-sz??這個值是請求隊(duì)列的飽和度,也就是平均請求隊(duì)列的長度。毫無疑問,隊(duì)列長度越短越好。
await?響應(yīng)時間應(yīng)該低于5ms,如果大于10ms就比較大了。這個時間包括了隊(duì)列時間和服務(wù)時間
svctm?? 表示平均每次設(shè)備
I/O
操作的服務(wù)時間。如果svctm
的值與await
很接近,表示幾乎沒有I/O
等待,磁盤性能很好,如果await
的值遠(yuǎn)高于svctm
的值,則表示I/O
隊(duì)列等待太長,系統(tǒng)上運(yùn)行的應(yīng)用程序?qū)⒆兟?/p>
3.2 零拷貝
kafka比較快的一個原因就是使用了zero copy。所謂的Zero copy,就是在操作數(shù)據(jù)時, 不需要將數(shù)據(jù)buffer從一個內(nèi)存區(qū)域拷貝到另一個內(nèi)存區(qū)域。因?yàn)樯倭艘淮蝺?nèi)存的拷貝, CPU的效率就得到提升。
我們來看一下它們之間的區(qū)別:

要想將一個文件的內(nèi)容通過socket發(fā)送出去,傳統(tǒng)的方式需要經(jīng)過以下步驟:
將文件內(nèi)容拷貝到內(nèi)核空間。
將內(nèi)核空間的內(nèi)容拷貝到用戶空間內(nèi)存,比如Java應(yīng)用。
用戶空間將內(nèi)容寫入到內(nèi)核空間的緩存中。
socket讀取內(nèi)核緩存中的內(nèi)容,發(fā)送出去。

零拷貝又多種模式,我們拿sendfile來說明。如上圖,在內(nèi)核的支持下,零拷貝少了一個步驟,那就是內(nèi)核緩存向用戶空間的拷貝。即節(jié)省了內(nèi)存,也節(jié)省了CPU的調(diào)度時間,效率很高。
4.網(wǎng)絡(luò)
除了iotop、iostat這些命令外,sar命令可以方便的看到網(wǎng)絡(luò)運(yùn)行狀況,下面是一個簡單的示例,用于描述入網(wǎng)流量和出網(wǎng)流量。
當(dāng)然,我們可以選擇性的只看TCP的一些狀態(tài)。
5.End
不要寄希望于這些指標(biāo),能夠立刻幫助我們定位性能問題。這些工具,只能夠幫我們大體猜測
發(fā)生問題的地方,它對性能問題的定位,只是起到輔助作用。想要分析這些bottleneck,需要收集更多的信息。
想要獲取更多的性能數(shù)據(jù),就不得不借助更加專業(yè)的工具,比如基于eBPF的BCC工具,這些牛x的工具我們將在其他文章里展開。讀完本文,希望你能夠快速的了解Linux的運(yùn)行狀態(tài),對你的系統(tǒng)多一些掌控。