性能分析(4)- iowait 使用率太高案例

性能分析小案例系列,能夠經過下面連接查看哦html

https://www.cnblogs.com/poloyy/category/1814570.html緩存

 

前言

  • 前面兩個案例講的都是上下文切換致使的 CPU 使用率升高
  • 這一篇就來說講等待 I/O 致使的 CPU 使用率升高的案例

 

進程狀態

詳解進程狀態

http://www.javashuo.com/article/p-mtpekblm-kw.html服務器

 

不可中斷狀態

  • 當 iowait 升高時,進程極可能由於得不到硬件的響應,而長時間處於不可中斷狀態
  • 不可中斷也是爲了保護進程數據和硬件狀態一致,而且正常狀況下,不可中斷狀態在很短期內就會結束
  • 因此,短時的不可中斷進程,通常能夠忽略
  • 但若是系統或硬件發生了故障,進程可能會在不可中斷狀態保持好久,甚至致使系統中出現大量不可中斷進程。這時,就得注意下,系統是否是出現了 I/O 等性能問題

 

殭屍進程

多進程引用很容易碰到的問題app

 

正常狀況

  • 一個進程建立了子進程後,它應該經過系統調用 wait() 或 waitpid() 等待子進程結束,回收子進程的資源
  • 而子進程在結束時,會向它的父進程發送 SIGCHLD 信號
  • 因此,父進程還能夠註冊 SIGCHLD 信號的處理函數,異步回收資源

 

異常狀況

  • 若是父進程沒有回收資源,或是子進程執行太快,父進程還沒來得及處理子進程狀態,子進程就已經提早退出,那這時的子進程就會變成殭屍進程
  • 形象比喻:父親應該一直對兒子負責, 有始有終,若是不做爲或者跟不上,都會致使「問題少年」的出現

 

重點

  • 殭屍進程持續的時間都比較短,在父進程回收它的資源後就會消亡,或者在父進程退出後,由 init 進程回收後也會消亡
  • 一旦父進程沒有處理子進程的終止,還一直保持運行狀態,那麼子進程就會一直處於殭屍狀態
  • 大量的殭屍進程會用盡 PID 進程號,致使新進程不能建立

 

大量不可中斷狀態和殭屍狀態進程的案例

系統配置

  • Ubuntu 18.04, 2 CPU,2GB 內存
  • 前置條件:已運行案例應用

 

經過 ps 命令查看案例進程

ps aux | grep /app

結果分析

  • 多個 app 進程已啓動
  • 狀態有 Ss+、D+、R+
  • 小s:表示這個進程是一個會話的領導進程
  • +:表示前臺進程組

 

什麼是會話和進程組

  • 它們是用來管理一組相互關聯的進程
  • 進程組:好比每一個子進程都是父進程所在組的成員
  • 會話:共享同一個控制終端的一個或多個進程組

 

會話和進程組的場景類比

  • 經過 SSH 登陸服務器,就會打開一個控制終端(TTY),這個控制終端就對應 一個會話
  • 而在終端中運行的命令以及它們的子進程,就構成了一個個的進程組
  • 後臺運行的命令,構成後臺進程組
  • 前臺運行的命令,構成前臺進程組

 

經過 top 查看系統情況

結果分析

  • 平均負載,過去 1min、5min、15min 的平均負載依次減小,說明平均負載正在升高
  • 而 1min 內的平均負載已經達到系統 CPU 個數,說明系統極可能存在性能瓶頸
  • 115 zombie 說明殭屍進程比較多,並且在不停增長,有子進程在退出時沒被清理
  • 用戶 CPU 和系統 CPU 都不高,但 iowait 分別是 60.5% 和 94.6%,好像有點兒不正常,致使系統的平均負載升高
  • 有兩個處於 D 狀態的 app 進程,可能在等待 I/O

 

查看系統的殭屍進程

ps -e -o stat,ppid,pid,cmd | egrep '^[Zz]'ps -ef | grep "defunct"

一堆 app 殭屍進程異步

 

iowait 分析

一提到 iowait 升高,首先會想要查詢系統的 I/O 狀況函數

 

運行 dstat 命令,觀察 CPU 和 I/O 的使用狀況

dstat 1 10

  • 當 iowait 升高(wai)時,磁盤的請求(read)都會很大(M)
  • 這說明 iowait 的升高跟磁盤的讀請求有關,極可能就是讀磁盤致使的

 

找到讀磁盤的進程

  • 經過 top 找到 D 狀態的兩個 app 進程
  • 不可中斷狀態表明進程在跟硬件進行交互,極可能就是讀磁盤

兩個 app 進程的 PID 分別是1240七、12406工具

 

經過 pidstat 查看 app 進程的 I/O 狀況

pidstat -d -p 12407 1 5
  • -d 展現 I/O 統計數據
  • -p 指定進程號
  • 間隔 1 秒輸出 5 組數據

  • kB_rd 表示每秒的 KB 數, kB_wr 表示每秒的 KB 數,iodelay 表示 I/O 的延遲(單位是時鐘週期)
  • 它們都是 0,那就表示此時沒有任何的讀寫,說明問題不 是 12407 進程致使的,也並非12406 進程致使的

 

經過 pidstat 查看系統的 I/O 狀況

pidstat -d 1 10

  • 能看到其實的確是 app 進程在讀,只不過每過幾秒都會有新的 app 進程在讀【pid 在不斷變化】
  • 能夠確認,是 app 進程的問題

 

經過 ps 命令查看一直變化的 app 進程狀態

前面講到讀磁盤的 app 進程 PID 一直在變化,那麼就來看看已經沒在讀磁盤的進程的進程狀態是怎麼樣的性能

ps aux | grep 15973

  • 這進程已是 Z 狀態,就是殭屍進程
  • 殭屍進程都是已經退出的進程, 因此就無法兒繼續分析它的系統調用
  • 關於殭屍進程的處理方法,咱們一下子再說,如今仍是繼續分析 iowait 的問題

 

經過 perf 錄製性能事件

  • 系統 iowait 的問題還在繼續,可是 top、pidstat 這類工具已經不能給出更多的信息了
  • 此時能夠經過 perf 動態跟蹤性能事件
perf record -g

15s 後 ctrl+c 終止錄製spa

 

查看報告,分析報告

perf report

  • app 的確在經過系統調用 sys_read() 讀取數據
  • 而且從 new_sync_read 和 blkdev_direct_IO 能看出,進程正在對磁盤進行直接讀,也就是繞過了系統緩存,每一個讀請求都會從磁盤直接讀,這就能夠解釋觀察到的 iowait 升高了

 

修復源碼以後,經過 top 命令驗證

  • iowait 已經很是低了,只有 0.3%
  • 說明修改源碼已經成功修復了 iowait 高的問題
  • 不過,仔細觀察殭屍進程的數量,會發現,殭屍進程還在不斷的增加中

 

處理和分析殭屍進程

  • 殭屍進程是由於父進程沒有回收子進程的資源而出現的
  • 解決殭屍進程須要先找出父進程,而後在父進程裏解決

 

經過 pstree 找到某個 app 進程的父進程

pstree -aps 51780

51780 進程的父進程是 51688,也就是 app 應用3d

 

經過 ps 查看全部殭屍進程的父進程

ps -e -o stat,ppid,pid,cmd | egrep '^[Zz]'

全部殭屍進程的父進程都是 51688,從而確認 51688 就是殭屍進程的父進程

 

查看 app 應用程序的代碼

查看 app 應用程序的代碼,看看子進程結束的處理是否正確

  1. 有沒有調用 wait() 或 waitpid() 
  2. 或有沒有註冊 SIGCHLD 信號的處理函數

把 wait() 放到了 for 死循環的外面,也就是說, wait()  函數實際上並沒被調用到,把它挪到 for 循環的裏面就能夠了

 

改完源碼,經過 top 驗證一下

殭屍進程(Z 狀態)沒有了, iowait 也是 0,問題終於所有解決了

 

總結

  • 這個案例是由於磁盤 I/O 致使了 iowait 升高
  • 不過,iowait 高並不必定表明 I/O 有性能瓶頸
  • 當系統中只有 I/O 類型的進程在運行時,iowait 也會很高,但實際上,磁盤的讀寫遠沒有達到性能瓶頸的程度

 

分析總體思路

  1. 經過 top 查看系統資源狀況
  2. 發現平均負載逐漸升高,iowait(wa)比較高,但用戶態和內核態 CPU 使用率並不算高
  3. 查看是否有 CPU 使用率偏高的進程,發現有 D 狀態的進程,多是在等待 I/O 中
  4. 過一陣子會變成 Z 狀態進程,且 CPU 使用率上升,而後會看到 zombie 進程數逐漸增長
  5. 能夠獲得兩個結論:殭屍進程過多,應該是父進程沒有清理已經結束的子進程的資源;iowait 的上升導系統平均負載上升
  6. 由於是 iowait 較高,能夠經過 dstat 查看系統的 I/O 狀況,會發現每次 iowait 升高,讀磁盤請求都會很大
  7. 經過 pidstat -d 查看 D 狀態進程的 I/O 狀況,但發現並無有效信息
  8. 經過 pidstat -d 直接查看系統的 I/O 狀況,能夠發現不斷有新進程在進行讀磁盤操做
  9. 經過 ps 命令查看剛剛 D 狀態進程當前的進程狀態,發現已經變成殭屍進程
  10. 經過 perf record 錄製性能事件,而後經過 perf report 查看性能報告,能夠發現 app 進程都是直接讀磁盤,而不通過系統緩存
  11. 經過 pstree 找到 Z 狀態進程的父進程
  12. 經過 ps 命令確認全部殭屍進程的父進程
  13. 找到父進程源代碼,檢查 wait() / waitpid() 的是否會成功調用,或是 SIGCHLD 信號處理函數的註冊就好了
  14. 修改徹底部源碼後,從新運行應用,經過 top 驗證是否還有 iowait 太高和出現 zombie 進程的狀況
相關文章
相關標籤/搜索