首頁 > 運維 > linux運維 > Linux效能最佳化知識點總結大全 · 實作+收藏版

Linux效能最佳化知識點總結大全 · 實作+收藏版

發布: 2023-08-03 15:01:50
轉載
764 人瀏覽過

Linux效能最佳化知識點總結大全 · 實作+收藏版

###########

Part1Linux效能最佳化

1效能最佳化

效能指標

高並發與反應快對應著效能最佳化的兩個核心指標:吞吐延遲

#
Linux效能最佳化知識點總結大全 · 實作+收藏版
圖片來自: www.ctq6.cn
  • #應用負載角度:直接影響了產品終端的使用者體驗
  • 系統資源角度:資源使用率、飽和度等

效能問題的本質就是系統資源已經到達瓶頸,但請求的處理還不夠快,無法支撐更多的請求。效能分析其實就是找出應用或系統的瓶頸,設法避免或緩解它們。

  • 選擇指標評估應用程式和系統效能
  • 為應用程式和系統設定效能目標
  • 進行效能基準測試
  • 效能分析定位瓶頸
  • #效能監控與警告

對於不同的效能問題要選取不同的效能分析工具。以下是常用的Linux Performance Tools以及對應分析的效能問題類型。

Linux效能最佳化知識點總結大全 · 實作+收藏版
圖片來自: www.ctq6.cn

#到底該怎麼理解"平均負載"

平均負載:單位時間內,系統處於可運作狀態和不可中斷狀態的平均進程數,也就是平均活躍進程數。它和我們傳統意義上所理解的CPU使用率並沒有直接關係。

其中不可中斷程序是正處於內核態關鍵流程中的程序(如常見的等待裝置的I/O響應)。 不可中斷狀態其實是系統對進程和硬體設備的一種保護機制。

平均負載多少時合理

實際生產環境中將系統的平均負載監控起來,根據歷史資料判斷負載的變化趨勢。當負載有明顯升高趨勢時,及時進行分析和調查。當然也可以當設定閾值(如當平均負載高於CPU數量的70%時)

現實工作中我們會經常混淆平均負載和CPU使用率的概念,其實兩者並不完全對等:

  • CPU密集型進程,大量CPU使用會導致平均負載升高,此時兩者一致
  • I/O密集型進程,等待I/O也會導致平均負載升高,此時CPU使用率並不一定高
  • #大量等待CPU的進程調度會導致平均負載升高,此時CPU使用率也會比較高

平均負載高時可能是CPU密集型進程導致,也可能是I/O繁忙導致。具體分析時可結合mpstat/pidstat工具輔助分析負載來源

#

2CPU

CPU上下文切換(上)

CPU上下文切換,就是把前一個任務的CPU上下文(CPU暫存器和PC)儲存起來,然後載入新任務的上下文到這些暫存器和程式計數器,最後再跳到程式計數器所指的位置,執行新任務。其中,保存下來的上下文會儲存在系統核心中,待任務重新調度執行時再加載,確保原來的任務狀態不受影響。 關注Linux中文社群

#依照任務類型,CPU上下文切換分為:

  • 進程上下文切換
  • #線程上下文切換
  • 中斷上下文切換

########################################## #######進程上下文切換#########Linux程序依照等級權限將行程的運行空間分成核心空間和使用者空間。從使用者態轉變為核心態時需要透過系統呼叫來完成。 ######一次系統呼叫過程其實進行了兩次CPU上下文切換:###
  • CPU暫存器中使用者狀態的指令位置先保存起來,CPU暫存器更新為核心狀態指令的位置,跳到核心態運行核心任務;
  • #系統呼叫結束後,CPU暫存器恢復原來保存的用戶態數據,再切換到用戶空間繼續運作。

系統呼叫過程中並不會涉及虛擬記憶體等進程用戶態資源,也不會切換進程。和傳統意義上的進程上下文切換不同。因此系統呼叫通常稱為特權模式切換

行程是由核心管理和調度的,行程上下文切換只能發生在內核態。因此相較於系統呼叫來說,在保存目前進程的核心狀態和CPU暫存器之前,需要先把該進程的虛擬內存,棧保存下來。再載入新進程的核心態後,還要刷新進程的虛擬記憶體和使用者堆疊。

進程只有在調度到CPU上運行時才需要切換上下文,有以下幾個場景:CPU時間片輪流分配,系統資源不足導致進程掛起,進程透過sleep函數主動掛起,高優先級進程搶佔時間片,硬體中斷時CPU上的程序被掛起轉而執行核心中的中斷服務。

執行緒上下文切換

執行緒上下文切換分為兩種:

  • 前後線程同屬於一個進程,切換時虛擬記憶體資源不變,只需要切換線程的私有數據,寄存器等;
  • 前後線程屬於不同進程,與進程上下文切換相同。

同行程的執行緒切換消耗資源較少,這也是多執行緒的優勢。

中斷上下文切換

中斷上下文切換並不涉及到行程的使用者狀態,因此中斷上下文只包含核心態中斷服務程式執行所必須的狀態(CPU暫存器,核心堆疊,硬體中斷參數等)。

中斷處理優先權比行程高,所以中斷上下文切換和行程上下文切換不會同時發生

CPU上下文切換(下)

透過vmstat可以檢視系統整體的上下文切換情況

vmstat 5         #每隔5s输出一组数据
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 1  0      0 103388 145412 511056    0    0    18    60    1    1  2  1 96  0  0
 0  0      0 103388 145412 511076    0    0     0     2  450 1176  1  1 99  0  0
 0  0      0 103388 145412 511076    0    0     0     8  429 1135  1  1 98  0  0
 0  0      0 103388 145412 511076    0    0     0     0  431 1132  1  1 98  0  0
 0  0      0 103388 145412 511076    0    0     0    10  467 1195  1  1 98  0  0
 1  0      0 103388 145412 511076    0    0     0     2  426 1139  1  0 99  0  0
 4  0      0  95184 145412 511108    0    0     0    74  500 1228  4  1 94  0  0
 0  0      0 103512 145416 511076    0    0     0   455  723 1573 12  3 83  2  0
登入後複製
  • cs (context switch) 每秒上下文切換次數
  • in (interrupt) 每秒中斷次數
  • ##r (runnning or runnable)就緒佇列的長度,正在運行和等待CPU的進程數
  • b (Blocked) 處於不可中斷睡眠狀態的進程數
要查看每個進程的詳細情況,需要使用pidstat來查看每個進程上下文切換情況

pidstat -w 5
14时51分16秒   UID       PID   cswch/s nvcswch/s  Command
14时51分21秒     0         1      0.80      0.00  systemd
14时51分21秒     0         6      1.40      0.00  ksoftirqd/0
14时51分21秒     0         9     32.67      0.00  rcu_sched
14时51分21秒     0        11      0.40      0.00  watchdog/0
14时51分21秒     0        32      0.20      0.00  khugepaged
14时51分21秒     0       271      0.20      0.00  jbd2/vda1-8
14时51分21秒     0      1332      0.20      0.00  argusagent
14时51分21秒     0      5265     10.02      0.00  AliSecGuard
14时51分21秒     0      7439      7.82      0.00  kworker/0:2
14时51分21秒     0      7906      0.20      0.00  pidstat
14时51分21秒     0      8346      0.20      0.00  sshd
14时51分21秒     0     20654      9.82      0.00  AliYunDun
14时51分21秒     0     25766      0.20      0.00  kworker/u2:1
14时51分21秒     0     28603      1.00      0.00  python3
登入後複製
  • cswch 每秒自愿上下文切换次数 (进程无法获取所需资源导致的上下文切换)
  • nvcswch 每秒非自愿上下文切换次数 (时间片轮流等系统强制调度)
vmstat 1 1    #首先获取空闲系统的上下文切换次数
sysbench --threads=10 --max-time=300 threads run #模拟多线程切换问题

vmstat 1 1    #新终端观察上下文切换情况
此时发现cs数据明显升高,同时观察其他指标:
r列: 远超系统CPU个数,说明存在大量CPU竞争
us和sy列:sy列占比80%,说明CPU主要被内核占用
in列: 中断次数明显上升,说明中断处理也是潜在问题
登入後複製

说明运行/等待CPU的进程过多,导致大量的上下文切换,上下文切换导致系统的CPU占用率高

pidstat -w -u 1  #查看到底哪个进程导致的问题
登入後複製

从结果中看出是sysbench导致CPU使用率过高,但是pidstat输出的上下文次数加起来也并不多。分析sysbench模拟的是线程的切换,因此需要在pidstat后加-t参数查看线程指标。

另外对于中断次数过多,我们可以通过/proc/interrupts文件读取

watch -d cat /proc/interrupts
登入後複製

发现次数变化速度最快的是重调度中断(RES),该中断用来唤醒空闲状态的CPU来调度新的任务运行。分析还是因为过多任务的调度问题,和上下文切换分析一致。

某個應用程式的CPU使用率達到100%,怎麼辦?

Linux作為多任務作業系統,將CPU時間分割為很短的時間片,透過調度器輪流分配給各個任務使用。為了維護CPU時間,Linux透過事先定義的節拍率,觸發時間中斷,並使用全域變了jiffies記錄開機以來的節拍數。時間中斷發生一次該值 1.

CPU使用率,除了空閒時間以外的其他時間佔總CPU時間的百分比。可以透過/proc/stat中的資料來計算出CPU使用率。因為/proc/stat時開機以來的節拍數累加值,計算出來的是開機以來的平均CPU使用率,一般意義不大。可以間隔取一段時間的兩次值作差來計算該段時間的平均CPU使用率。 效能分析工具給的都是間隔一段時間的平均CPU使用率,要注意間隔時間的設定。

CPU使用率可以透過top 或 ps來查看。分析進程的CPU問題可以透過perf,它以效能事件採樣為基礎,不僅可以分析系統的各種事件和核心效能,還可以用來分析指定應用程式的效能問題。

perf top / perf record / perf report (-g 開啟呼叫關係的取樣)

sudo docker run --name nginx -p 10000:80 -itd feisky/nginx
sudo docker run --name phpfpm -itd --network container:nginx feisky/php-fpm

ab -c 10 -n 100 http://XXX.XXX.XXX.XXX:10000/ #测试Nginx服务性能
登入後複製

发现此时每秒可承受请求给长少,此时将测试的请求数从100增加到10000。在另外一个终端运行top查看每个CPU的使用率。发现系统中几个php-fpm进程导致CPU使用率骤升。

接着用perf来分析具体是php-fpm中哪个函数导致该问题。

perf top -g -p XXXX #对某一个php-fpm进程进行分析
登入後複製

发现其中sqrt和add_function占用CPU过多, 此时查看源码找到原来是sqrt中在发布前没有删除测试代码段,存在一个百万次的循环导致。将该无用代码删除后发现nginx负载能力明显提升

系统的CPU使用率很高,为什么找不到高CPU的应用?

sudo docker run --name nginx -p 10000:80 -itd feisky/nginx:sp
sudo docker run --name phpfpm -itd --network container:nginx feisky/php-fpm:sp
ab -c 100 -n 1000 http://XXX.XXX.XXX.XXX:10000/ #并发100个请求测试
登入後複製

实验结果中每秒请求数依旧不高,我们将并发请求数降为5后,nginx负载能力依旧很低。

此时用top和pidstat发现系统CPU使用率过高,但是并没有发现CPU使用率高的进程。

出现这种情况一般时我们分析时遗漏的什么信息,重新运行top命令并观察一会。发现就绪队列中处于Running状态的进行过多,超过了我们的并发请求次数5. 再仔细查看进程运行数据,发现nginx和php-fpm都处于sleep状态,真正处于运行的却是几个stress进程。

下一步就利用pidstat分析这几个stress进程,发现没有任何输出。用ps aux交叉验证发现依旧不存在该进程。说明不是工具的问题。再top查看发现stress进程的进程号变化了,此时有可能时以下两种原因导致:

  • 进程不停的崩溃重启(如段错误/配置错误等),此时进程退出后可能又被监控系统重启;
  • 短时进程导致,即其他应用内部通过exec调用的外面命令,这些命令一般只运行很短时间就结束,很难用top这种间隔较长的工具来发现

可以通过pstree来查找 stress的父进程,找出调用关系。

pstree | grep stress
登入後複製

发现是php-fpm调用的该子进程,此时去查看源码可以看出每个请求都会调用一个stress命令来模拟I/O压力。之前top显示的结果是CPU使用率升高,是否真的是由该stress命令导致的,还需要继续分析。代码中给每个请求加了verbose=1的参数后可以查看stress命令的输出,在中断测试该命令结果显示stress命令运行时存在因权限问题导致的文件创建失败的bug。

此時依舊只是猜測,下一步繼續透過perf工具來分析。效能報告顯示確實時stress佔用了大量的CPU,透過修復權限問題來最佳化解決即可.

系統中出現大量不可中斷程序和殭屍行程怎麼辦?

進程狀態

  • R Running/Runnable,表示該行程在CPU的就緒佇列中,正在執行或等待執行;
  • D Disk Sleep,不可中斷狀態睡眠,一般表示進程正在跟硬體交互,並且交互過程中不允許被其他進程中斷;
  • Z Zombie,殭屍進程,表示進程實際上已經結束,但是父進程還沒有回收它的資源;
  • S Interruptible Sleep,可中斷睡眠狀態,表示進程因為等待某個事件而被系統掛起,當等待事件發生則會被喚醒並進入R狀態;
  • #I Idle,空閒狀態,用在不可中斷睡眠的核心執行緒上。此狀態不會導致平均負載升高;
  • T Stop/Traced,表示進程處於暫停或追蹤狀態(SIGSTOP/SIGCONT, GDB偵錯);
  • X Dead,進程已經消亡,不會在top/ps中看到。

对于不可中断状态,一般都是在很短时间内结束,可忽略。但是如果系统或硬件发生故障,进程可能会保持不可中断状态很久,甚至系统中出现大量不可中断状态,此时需注意是否出现了I/O性能问题。

僵尸进程一般多进程应用容易遇到,父进程来不及处理子进程状态时子进程就提前退出,此时子进程就变成了僵尸进程。大量的僵尸进程会用尽PID进程号,导致新进程无法建立。

磁盘O_DIRECT问题

sudo docker run --privileged --name=app -itd feisky/app:iowait
ps aux | grep '/app'
登入後複製

可以看到此时有多个app进程运行,状态分别时Ss+和D+。其中后面s表示进程是一个会话的领导进程,+号表示前台进程组。

其中进程组表示一组相互关联的进程,子进程是父进程所在组的组员。会话指共享同一个控制终端的一个或多个进程组。

用top查看系统资源发现:1)平均负载在逐渐增加,且1分钟内平均负载达到了CPU个数,说明系统可能已经有了性能瓶颈;2)僵尸进程比较多且在不停增加;3)us和sys CPU使用率都不高,iowait却比较高;4)每个进程CPU使用率也不高,但有两个进程处于D状态,可能在等待IO。

分析目前数据可知:iowait过高导致系统平均负载升高,僵尸进程不断增长说明有程序没能正确清理子进程资源。

用dstat来分析,因为它可以同时查看CPU和I/O两种资源的使用情况,便于对比分析。

dstat 1 10    #间隔1秒输出10组数据
登入後複製

可以看到当wai(iowait)升高时磁盘请求read都会很大,说明iowait的升高和磁盘的读请求有关。接下来分析到底时哪个进程在读磁盘。

之前top查看的处于D状态的进程号,用pidstat -d -p XXX 展示进程的I/O统计数据。发现处于D状态的进程都没有任何读写操作。在用pidstat -d 查看所有进程的I/O统计数据,看到app进程在进行磁盘读操作,每秒读取32MB的数据。进程访问磁盘必须使用系统调用处于内核态,接下来重点就是找到app进程的系统调用。

sudo strace -p XXX #对app进程调用进行跟踪
登入後複製

报错没有权限,因为已经时root权限了。所以遇到这种情况,首先要检查进程状态是否正常。ps命令查找该进程已经处于Z状态,即僵尸进程。

这种情况下top pidstat之类的工具无法给出更多的信息,此时像第5篇一样,用perf record -d和perf report进行分析,查看app进程调用栈。

看到app确实在通过系统调用sys_read()读取数据,并且从new_sync_read和blkdev_direct_IO看出进程时进行直接读操作,请求直接从磁盘读,没有通过缓存导致iowait升高。

通过层层分析后,root cause是app内部进行了磁盘的直接I/O。然后定位到具体代码位置进行优化即可。

僵尸进程

上述优化后iowait显著下降,但是僵尸进程数量仍旧在增加。首先要定位僵尸进程的父进程,通过pstree -aps XXX,打印出该僵尸进程的调用树,发现父进程就是app进程。

查看app代码,看看子进程结束的处理是否正确(是否调用wait()/waitpid(),有没有注册SIGCHILD信号的处理函数等)。

碰到iowait升高时,先用dstat pidstat等工具确认是否存在磁盘I/O问题,再找是哪些进程导致I/O,不能用strace直接分析进程调用时可以通过perf工具分析。

对于僵尸问题,用pstree找到父进程,然后看源码检查子进程结束的处理逻辑即可。

CPU效能指標

  • #CPU使用率

    • 用戶CPU使用率, 包括用戶態(user)和低優先級用戶態(nice). 此指標過高說明應用程式比較繁忙.
    • ##系統CPU使用率, CPU在內核態運作的時間百分比(不含中斷). 此指標高說明核心比較繁忙.
    • 等待I/O的CPU使用率, iowait, 此指標高說明系統與硬體設備I/O互動時間比較長.
    • 軟/硬中斷CPU使用率, 此指標高說明系統中發生大量中斷.
    • steal CPU / guest CPU, 表示虛擬機器佔用的CPU百分比.
  • #平均負載

    理想情況下平均負載等於邏輯CPU個數,表示每個CPU都被充分利用. 若大於則說明系統負載較重.

  • 進程上下文切換

    ##############################################################################

    包括無法取得資源的自願切換和系統強制調度時的非自願切換. 上下文切換本身是保證Linux正常運行的一項核心功能. 過多的切換則會將原本運行進程的CPU時間消耗在寄存器,核心佔及虛擬記憶體等資料保存與復原上

  • CPU快取命中率

    CPU快取的複用情形,命中率越高效能越好. 其中L1/L2常用在單核心,L3則用在多核心中

效能工具

  • #平均負載案例
    • 先用uptime查看系統平均負載
    • 判斷負載在升高後再用mpstat和pidstat分別查看每個CPU和每個進程CPU使用情況.找出導致平均負載較高的進程.另外,搜尋公眾號Linux就該這樣學後台回复“git書籍”,獲取一份驚喜禮包。
  • 上下文切換案例
    • #先用vmstat查看系統上下文切換與中斷次數
    • #再用pidstat觀察行程的自願與非自願上下文切換情況
    • 最後透過pidstat觀察執行緒的上下文切換情況
  • 進程CPU使用率高案例
    • 先用top檢視系統與進程的CPU使用情況,定位到進程
    • 再用perf top觀察進程呼叫鏈,定位到具體函數
  • 系統CPU使用率高案例
    • 先用top查看系統與進程的CPU使用情況,top/pidstat都無法找到CPU使用率高的進程
    • 重新審視top輸出
    • 從CPU使用率不高,但是處於Running狀態的進程入手
    • perf record/report發現短時進程導致(execsnoop工具)
    • ###### #######不可中斷和殭屍進程案例###############先用top觀察iowait升高,發現大量不可中斷和殭屍進程####### #####strace無法追蹤進程系統呼叫######
    • perf分析呼叫鏈發現根源來自磁碟直接I/O
  • #軟體中斷案例
    • top觀察系統軟中斷CPU使用率高
    • 查看/proc/softirqs找到變化速率較快的幾種軟體中斷
    • sar命令發現是網絡小包問題
    • tcpdump找出網絡幀的類型和來源, 確定SYN FLOOD攻擊導致
##根據不同的效能指標來找合適的工具:

Linux效能最佳化知識點總結大全 · 實作+收藏版圖片來自: www.ctq6.cn
在生產環境中往往開發者沒有權限安裝新的工具包,只能最大化利用好系統中已經安裝好的工具. 因此要了解一些主流工具能夠提供哪些指標分析.

Linux效能最佳化知識點總結大全 · 實作+收藏版
圖片來自: www.ctq6.cn

先運行幾個支援指標較多的工具, 如top/vmstat/pidstat,根據它們的輸出可以得出是哪種類型的效能問題. 定位到進程後再用strace/perf分析呼叫情況進一步分析. 如果是軟中斷導致用/proc/softirqs

Linux效能最佳化知識點總結大全 · 實作+收藏版
圖片來自: www .ctq6.cn

CPU最佳化

  • 應用程式最佳化

    • 編譯器最佳化: 編譯階段開啟最佳化選項, 如gcc -O2
    • 演算法最佳化
    • 非同步處理: 避免程式因為等待某個資源而一直阻塞,提升程式的並發處理能力. (將輪詢替換為事件通知)
    • 多執行緒代替多進程: 減少上下文切換成本
    • 善用快取: 加快程式處理速度
  • ##系統最佳化

    • CPU綁定: 將進程綁定要1個/多個CPU上,提高CPU快取命中率,減少CPU調度帶來的上下文切換
    • CPU獨佔: CPU親和性機制來分配進程
    • #優先權調整:使用nice適當降低非核心應用的優先權
    • 為進程設定資源顯示: cgroups設定使用上限,防止由某個應用自身問題耗盡系統資源
    • NUMA最佳化: CPU盡可能存取本地內存
    • 中斷負載平衡: irpbalance,將中斷處理過程自動負載平衡到各個CPU上
  • TPS、QPS 、系統吞吐量的差異與理解

    • QPS(TPS)

    • 並發數字

    • #回應時間

      ##QPS(TPS)=並發數/平均對應時間

    • 使用者請求伺服器

    • 伺服器內部處理

    • 伺服器傳回給客戶

      QPS類似TPS,但是對於一個頁面的存取形成一個TPS,但是一次頁面請求可能包含多次對伺服器的請求,可能計入多次QPS

    • QPS (Queries Per Second)每秒查詢率,一台伺服器每秒能夠回應的查詢次數.

    • TPS (Transactions Per Second)每秒事務數,軟體測試的結果.

    • 系統吞吐量, 包含幾個重要參數:

3記憶體

Linux記憶體是怎麼運作的

記憶體映射

大多數電腦用的主記憶體都是動態隨機存取記憶體(DRAM ),只有核心才可以直接存取實體記憶體。 Linux核心為每個行程提供了一個獨立的虛擬位址空間,而這個位址空間是連續的。這樣進程就可以很方便的存取記憶體(虛擬記憶體)。

虛擬位址空間的內部分為核心空間與使用者空間兩部分,不同字長的處理器位址空間的範圍不同。 32位元系統核心空間佔用1G,用戶空間佔3G。 64位元系統核心空間和用戶空間都是128T,分別佔記憶體空間的最高和最低處,中間部分為未定義。

並不是所有的虛擬記憶體都會分配實體內存,只有實際使用的才會。分配後的實體記憶體透過記憶體映射管理。為了完成記憶體映射,核心為每個進程都維護了一個頁表,記錄虛擬位址和實體位址的映射關係。頁表實際儲存在CPU的記憶體管理單元MMU中,處理器可以直接透過硬體找出要存取的記憶體。

當進程存取的虛擬位址在頁表中查不到時,系統會產生一個缺頁異常,進入內核空間分配物理內存,更新進程頁表,再返回用戶空間恢復進程的運行。

MMU以頁為單位管理內存,頁大小4KB。為了解決頁表項過多問題Linux提供了多層頁表HugePage的機制。

虛擬記憶體空間分佈

使用者空間記憶體從低到高是五種不同的記憶體段:

  • 只讀段 程式碼與常數等
  • #資料段 全域變數等
  • 堆疊 動態分配的內存,從低位址開始向上增長
  • 檔案映射 動態庫、共享內存等等,從高位址開始向下增長
  • #堆疊 包括局部變數和函數呼叫的上下文等,堆疊的大小是固定的。一般8MB

記憶體分配與回收

#分配

malloc對應到系統呼叫上有兩種實作方式:

  • brk() 針對小塊記憶體(<128K),透過移動堆頂位置來分配。內存釋放後不立即歸還內存,而是被緩存起來。
  • **mmap()**針對大塊記憶體(>128K),直接用記憶體映射來分配,即在檔案映射段找一塊空閒記憶體分配。

前者的快取可以減少缺頁異常的發生,提高記憶體存取效率。但是由於記憶體沒有歸還系統,在記憶體工作繁忙時,頻繁的記憶體分配/釋放會造成記憶體碎片。

後者在釋放時直接歸還系統,所以每次mmap都會發生缺頁異常。在記憶體工作繁忙時,頻繁記憶體分配會導致大量缺頁異常,使核心管理負擔增加。

上述兩種呼叫並沒有真正分配內存,這些內存只有在首次訪問時,才通過缺頁異常進入內核中,由內核來分配

回收

#記憶體緊張時,系統會透過以下方式回收記憶體:

  • 回收缓存:LRU算法回收最近最少使用的内存页面;

  • 回收不常访问内存:把不常用的内存通过交换分区写入磁盘

  • 杀死进程:OOM内核保护机制 (进程消耗内存越大oom_score越大,占用CPU越多oom_score越小,可以通过/proc手动调整oom_adj)

    echo -16 > /proc/$(pidof XXX)/oom_adj
    登入後複製

如何查看内存使用情况

free来查看整个系统的内存使用情况

top/ps来查看某个进程的内存使用情况

  • VIRT 程序的虛擬記憶體大小
  • #RES 常駐記憶體的大小,即程式實際使用的物理內存大小,不包括swap和共享內存
  • SHR 共享內存大小,與其他進程共享的內存,加載的動態鏈接庫以及程序代碼段
  • %MEM 程序使用實體記憶體佔系統總記憶體的百分比

怎麼理解記憶體中的Buffer和Cache?

buffer是對磁碟資料的緩存,cache是​​對檔案資料的緩存,它們既會用在讀取請求也會用在寫入請求中

#

如何利用系統快取最佳化程式的運作效率

#快取命中率

##快取命中率是指直接透過快取取得資料的請求次數,佔所有請求次數的百分比。 命中率越高表示快取帶來的效益越高,應用程式的效能也越好。

安裝bcc套件後可以透過cachestat和cachetop來監測快取的讀寫命中情況。

安裝pcstat後可以查看檔案在記憶體中的快取大小以及快取比例

#首先安装Go
export GOPATH=~/go
export PATH=~/go/bin:$PATH
go get golang.org/x/sys/unix
go ge github.com/tobert/pcstat/pcstat
登入後複製

dd缓存加速

dd if=/dev/sda1 of=file bs=1M count=512 #生产一个512MB的临时文件
echo 3 > /proc/sys/vm/drop_caches #清理缓存
pcstat file #确定刚才生成文件不在系统缓存中,此时cached和percent都是0
cachetop 5
dd if=file of=/dev/null bs=1M #测试文件读取速度
#此时文件读取性能为30+MB/s,查看cachetop结果发现并不是所有的读都落在磁盘上,读缓存命中率只有50%。
dd if=file of=/dev/null bs=1M #重复上述读文件测试
#此时文件读取性能为4+GB/s,读缓存命中率为100%
pcstat file #查看文件file的缓存情况,100%全部缓存
登入後複製

O_DIRECT选项绕过系统缓存

cachetop 5
sudo docker run --privileged --name=app -itd feisky/app:io-direct
sudo docker logs app #确认案例启动成功
#实验结果表明每读32MB数据都要花0.9s,且cachetop输出中显示1024次缓存全部命中
登入後複製

但是凭感觉可知如果缓存命中读速度不应如此慢,读次数时1024,页大小为4K,五秒的时间内读取了1024*4KB数据,即每秒0.8MB,和结果中32MB相差较大。说明该案例没有充分利用缓存,怀疑系统调用设置了直接I/O标志绕过系统缓存。因此接下来观察系统调用.

strace -p $(pgrep app)
#strace 结果可以看到openat打开磁盘分区/dev/sdb1,传入参数为O_RDONLY|O_DIRECT
登入後複製

这就解释了为什么读32MB数据那么慢,直接从磁盘读写肯定远远慢于缓存。找出问题后我们再看案例的源代码发现flags中指定了直接IO标志。删除该选项后重跑,验证性能变化。

内存泄漏,如何定位和处理?

对应用程序来说,动态内存的分配和回收是核心又复杂的一个逻辑功能模块。管理内存的过程中会发生各种各样的“事故”:

  • 沒正確回收分配的內存,導致了洩漏
  • 訪問的是已分配記憶體邊界外的位址,導致程式異常退出

#記憶體的分配與回收

虛擬記憶體分佈從低到高分別是只讀段,資料段,堆,記憶體映射段,堆疊五部分。其中會導致記憶體洩漏的是:

  • 堆:由應用程式自行分配和管理,除非程式退出這些堆記憶體不會被系統自動釋放。
  • 記憶體映射段:包括動態連結庫和共享內存,其中共享記憶體由程式自動分配和管理

##記憶體洩漏的危害比較大,這些忘記釋放的內存,不僅應用程式自己不能訪問,系統也不能把它們再次分配給其他應用。  記憶體洩漏不斷累積甚至會耗盡系統記憶體.

如何检测内存泄漏

预先安装systat,docker,bcc

sudo docker run --name=app -itd feisky/app:mem-leak
sudo docker logs app
vmstat 3
登入後複製

可以看到free在不断下降,buffer和cache基本保持不变。说明系统的内存一致在升高。但并不能说明存在内存泄漏。此时可以通过memleak工具来跟踪系统或进程的内存分配/释放请求

/usr/share/bcc/tools/memleak -a -p $(pidof app)
登入後複製

从memleak输出可以看到,应用在不停地分配内存,并且这些分配的地址并没有被回收。通过调用栈看到是fibonacci函数分配的内存没有释放。定位到源码后查看源码来修复增加内存释放函数即可.

为什么系统的Swap变高

系统内存资源紧张时通过内存回收和OOM杀死进程来解决。其中可回收内存包括:

  • 快取/緩衝區,屬於可回收資源,在檔案管理中通常稱為檔案頁
    • 在應用程式中透過fsync將髒頁面同步到磁碟
    • 交給系統,核心執行緒pdflush負責這些髒頁的刷新
    • 被應用程式修改過暫時沒寫入磁碟的資料(髒頁),要先寫入磁碟然後才能記憶體釋放
  • #記憶體映射所取得的檔案對應頁,也可以被釋放掉,下次造訪時從檔案重新讀取

對於程式自動分配的堆內存,也就是我們在記憶體管理中的匿名頁,雖然這些記憶體不能直接釋放,但是Linux提供了Swap機制將不常存取的內存寫入到磁碟來釋放內存,再次訪問時從磁碟讀取到內存即可。

Swap原理

Swap本質就是把一塊磁碟空間或一個本機檔案當作記憶體來使用,包括換入和換出兩個流程:

  • #換出:將進程暫時不用的記憶體資料儲存到磁碟中,並釋放這些記憶體
  • 換入:當進程再次存取記憶體時,將它們從磁碟讀到記憶體中

Linux如何衡量記憶體資源是否緊張?

  • 直接記憶體回收 新的大塊記憶體分配請求,但剩餘記憶體不足。此時系統會回收一部分記憶體;

  • kswapd0 核心執行緒定期回收記憶體。為了衡量記憶體使用情況,定義了pages_min,pages_low,pages_high三個閾值,並根據其來進行記憶體的回收操作。

    • 剩餘記憶體< pages_min,進程可用記憶體耗盡了,只有核心才可以分配記憶體

    • pages_min < 剩餘記憶體< pages_low,記憶體壓力較大,kswapd0執行記憶體回收,直到剩餘記憶體> pages_high

    • pages_low < 剩余内存 < pages_high,内存有一定压力,但可以满足新内存请求

    • 剩余内存 > pages_high,说明剩余内存较多,无内存压力

      pages_low = pages_min 5 / 4 pages_high = pages_min 3 / 2

NUMA 与 SWAP

很多情况下系统剩余内存较多,但SWAP依旧升高,这是由于处理器的NUMA架构。

在NUMA架构下多个处理器划分到不同的Node,每个Node都拥有自己的本地内存空间。在分析内存的使用时应该针对每个Node单独分析

numactl --hardware #查看处理器在Node的分布情况,以及每个Node的内存使用情况
登入後複製

内存三个阈值可以通过/proc/zoneinfo来查看,该文件中还包括活跃和非活跃的匿名页/文件页数。

当某个Node内存不足时,系统可以从其他Node寻找空闲资源,也可以从本地内存中回收内存。通过/proc/sys/vm/zone_raclaim_mode来调整。

  • 0表示既可以从其他Node寻找空闲资源,也可以从本地回收内存
  • 1,2,4表示只回收本地内存,2表示可以会回脏数据回收内存,4表示可以用Swap方式回收内存。

swappiness

在实际回收过程中Linux根据/proc/sys/vm/swapiness选项来调整使用Swap的积极程度,从0-100,数值越大越积极使用Swap,即更倾向于回收匿名页;数值越小越消极使用Swap,即更倾向于回收文件页。

注意:这只是调整Swap积极程度的权重,即使设置为0,当剩余内存+文件页小于页高阈值时,还是会发生Swap。

Swap升高时如何定位分析

free #首先通过free查看swap使用情况,若swap=0表示未配置Swap
#先创建并开启swap
fallocate -l 8G /mnt/swapfile
chmod 600 /mnt/swapfile
mkswap /mnt/swapfile
swapon /mnt/swapfile

free #再次执行free确保Swap配置成功

dd if=/dev/sda1 of=/dev/null bs=1G count=2048 #模拟大文件读取
sar -r -S 1  #查看内存各个指标变化 -r内存 -S swap
#根据结果可以看出,%memused在不断增长,剩余内存kbmemfress不断减少,缓冲区kbbuffers不断增大,由此可知剩余内存不断分配给了缓冲区
#一段时间之后,剩余内存很小,而缓冲区占用了大部分内存。此时Swap使用之间增大,缓冲区和剩余内存只在小范围波动

停下sar命令
cachetop5 #观察缓存
#可以看到dd进程读写只有50%的命中率,未命中数为4w+页,说明正式dd进程导致缓冲区使用升高
watch -d grep -A 15 ‘Normal’ /proc/zoneinfo #观察内存指标变化
#发现升级内存在一个小范围不停的波动,低于页低阈值时会突然增大到一个大于页高阈值的值
登入後複製

说明剩余内存和缓冲区的波动变化正是由于内存回收和缓存再次分配的循环往复。有时候Swap用的多,有时候缓冲区波动更多。此时查看swappiness值为60,是一个相对中和的配置,系统会根据实际运行情况来选去合适的回收类型.

如何「快準狠」找到系統記憶體存在的問題

#記憶體效能指標

系統記憶體指標

  • 已使用記憶體/剩餘記憶體
  • 共享記憶體(tmpfs實作)
  • 可用記憶體:包含剩餘記憶體和可回收記憶體
  • 快取:磁碟讀取檔案的頁緩存,slab分配器中的可回收部分
  • #緩衝區:原始磁碟區塊的暫存,快取將要寫入磁碟的資料

進程記憶體指標

#
  • 虛擬記憶體:5大部分
  • 常駐記憶體:進程實際使用的實體內存,不包括Swap和共享記憶體
  • #共享內存:與其他進程共享的內存,以及動態鏈接庫和程序的代碼段
  • 另外,搜索公眾號技術社區後台回复“算法”,取得一份驚喜禮包。
  • Swap記憶體:透過Swap換出到磁碟的記憶體

缺頁例外

  • 可以直接從實體記憶體中分配,次缺頁異常
  • #需要磁碟IO介入(如Swap),主缺頁異常。此時記憶體存取會慢很多

記憶體效能工具

#根據不同的效能指標來找合適的工具:

Linux效能最佳化知識點總結大全 · 實作+收藏版
圖片來自: www.ctq6.cn

記憶體分析工具包含的效能指標:

Linux效能最佳化知識點總結大全 · 實作+收藏版
圖片來自: www .ctq6.cn

如何迅速分析記憶體的效能瓶頸

通常先執行幾個覆寫面比較大的效能工具,如free,top,vmstat ,pidstat等

  • 先用free和top查看系統整體記憶體使用量
  • #再用vmstat和pidstat,查看一段時間的趨勢,從而判斷記憶體問題的類型
  • 最後進行詳細分析,例如記憶體分配分析,快取/緩衝區分析,具體進程的記憶體使用分析等

常見的最佳化思路:

  • 最好禁止Swap,若必须开启则尽量降低swappiness的值
  • 减少内存的动态分配,如可以用内存池,HugePage等
  • 尽量使用缓存和缓冲区来访问数据。如用堆栈明确声明内存空间来存储需要缓存的数据,或者用Redis外部缓存组件来优化数据的访问
  • cgroups等方式来限制进程的内存使用情况,确保系统内存不被异常进程耗尽
  • /proc/pid/oom_adj调整核心应用的oom_score,保证即使内存紧张核心应用也不会被OOM杀死
vmstat使用详解

vmstat命令是最常见的Linux/Unix监控工具,可以展现给定时间间隔的服务器的状态值,包括服务器的CPU使用率,内存使用,虚拟内存交换情况,IO读写情况。可以看到整个机器的CPU,内存,IO的使用情况,而不是单单看到各个进程的CPU使用率和内存使用率(使用场景不一样)。

vmstat 2
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 1  0      0 1379064 282244 11537528    0    0     3   104    0    0  3  0 97  0  0
 0  0      0 1372716 282244 11537544    0    0     0    24 4893 8947  1  0 98  0  0
 0  0      0 1373404 282248 11537544    0    0     0    96 5105 9278  2  0 98  0  0
 0  0      0 1374168 282248 11537556    0    0     0     0 5001 9208  1  0 99  0  0
 0  0      0 1376948 282248 11537564    0    0     0    80 5176 9388  2  0 98  0  0
 0  0      0 1379356 282256 11537580    0    0     0   202 5474 9519  2  0 98  0  0
 1  0      0 1368376 282256 11543696    0    0     0     0 5894 8940 12  0 88  0  0
 1  0      0 1371936 282256 11539240    0    0     0 10554 6176 9481 14  1 85  1  0
 1  0      0 1366184 282260 11542292    0    0     0  7456 6102 9983  7  1 91  0  0
 1  0      0 1353040 282260 11556176    0    0     0 16924 7233 9578 18  1 80  1  0
 0  0      0 1359432 282260 11549124    0    0     0 12576 5495 9271  7  0 92  1  0
 0  0      0 1361744 282264 11549132    0    0     0    58 8606 15079  4  2 95  0  0
 1  0      0 1367120 282264 11549140    0    0     0     2 5716 9205  8  0 92  0  0
 0  0      0 1346580 282264 11562644    0    0     0    70 6416 9944 12  0 88  0  0
 0  0      0 1359164 282264 11550108    0    0     0  2922 4941 8969  3  0 97  0  0
 1  0      0 1353992 282264 11557044    0    0     0     0 6023 8917 15  0 84  0  0

# 结果说明
- r 表示运行队列(就是说多少个进程真的分配到CPU),我测试的服务器目前CPU比较空闲,没什么程序在跑,当这个值超过了CPU数目,就会出现CPU瓶颈了。这个也和top的负载有关系,一般负载超过了3就比较高,超过了5就高,超过了10就不正常了,服务器的状态很危险。top的负载类似每秒的运行队列。如果运行队列过大,表示你的CPU很繁忙,一般会造成CPU使用率很高。

- b 表示阻塞的进程,这个不多说,进程阻塞,大家懂的。

- swpd 虚拟内存已使用的大小,如果大于0,表示你的机器物理内存不足了,如果不是程序内存泄露的原因,那么你该升级内存了或者把耗内存的任务迁移到其他机器。

- free   空闲的物理内存的大小,我的机器内存总共8G,剩余3415M。

- buff   Linux/Unix系统是用来存储,目录里面有什么内容,权限等的缓存,我本机大概占用300多M

- cache cache直接用来记忆我们打开的文件,给文件做缓冲,我本机大概占用300多M(这里是Linux/Unix的聪明之处,把空闲的物理内存的一部分拿来做文件和目录的缓存,是为了提高 程序执行的性能,当程序使用内存时,buffer/cached会很快地被使用。)

- si  每秒从磁盘读入虚拟内存的大小,如果这个值大于0,表示物理内存不够用或者内存泄露了,要查找耗内存进程解决掉。我的机器内存充裕,一切正常。

- so  每秒虚拟内存写入磁盘的大小,如果这个值大于0,同上。

- bi  块设备每秒接收的块数量,这里的块设备是指系统上所有的磁盘和其他块设备,默认块大小是1024byte,我本机上没什么IO操作,所以一直是0,但是我曾在处理拷贝大量数据(2-3T)的机器上看过可以达到140000/s,磁盘写入速度差不多140M每秒

- bo 块设备每秒发送的块数量,例如我们读取文件,bo就要大于0。bi和bo一般都要接近0,不然就是IO过于频繁,需要调整。

- in 每秒CPU的中断次数,包括时间中断

- cs 每秒上下文切换次数,例如我们调用系统函数,就要进行上下文切换,线程的切换,也要进程上下文切换,这个值要越小越好,太大了,要考虑调低线程或者进程的数目,例如在apache和nginx这种web服务器中,我们一般做性能测试时会进行几千并发甚至几万并发的测试,选择web服务器的进程可以由进程或者线程的峰值一直下调,压测,直到cs到一个比较小的值,这个进程和线程数就是比较合适的值了。系统调用也是,每次调用系统函数,我们的代码就会进入内核空间,导致上下文切换,这个是很耗资源,也要尽量避免频繁调用系统函数。上下文切换次数过多表示你的CPU大部分浪费在上下文切换,导致CPU干正经事的时间少了,CPU没有充分利用,是不可取的。

- us 用户CPU时间,我曾经在一个做加密解密很频繁的服务器上,可以看到us接近100,r运行队列达到80(机器在做压力测试,性能表现不佳)。

- sy 系统CPU时间,如果太高,表示系统调用时间长,例如是IO操作频繁。

- id 空闲CPU时间,一般来说,id + us + sy = 100,一般我认为id是空闲CPU使用率,us是用户CPU使用率,sy是系统CPU使用率。

- wt 等待IO CPU时间
登入後複製
pidstat 使用详解

pidstat主要用于监控全部或指定进程占用系统资源的情况,如CPU,内存、设备IO、任务切换、线程等。

使用方法:

  • pidstat –d interval times 統計各個進程的IO使用情況
  • ##pidstat –u interval times 統計各個流程的CPU統計資訊
  • pidstat –r interval times 統計各個程序的記憶體使用資訊
  • ##pidstat -w interval times 統計各行程的上下文切換
  • p PID 指定PID
  • #1、統計IO使用量
pidstat -d 1 10

03:02:02 PM   UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s  Command
03:02:03 PM     0       816      0.00    918.81      0.00  jbd2/vda1-8
03:02:03 PM     0      1007      0.00      3.96      0.00  AliYunDun
03:02:03 PM   997      7326      0.00   1904.95    918.81  java
03:02:03 PM   997      8539      0.00      3.96      0.00  java
03:02:03 PM     0     16066      0.00     35.64      0.00  cmagent

03:02:03 PM   UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s  Command
03:02:04 PM     0       816      0.00   1924.00      0.00  jbd2/vda1-8
03:02:04 PM   997      7326      0.00  11156.00   1888.00  java
03:02:04 PM   997      8539      0.00      4.00      0.00  java
登入後複製
  • UID
  • PID
  • kB_rd/s: 每秒进程从磁盘读取的数据量 KB 单位 read from disk each second KB
  • kB_wr/s: 每秒进程向磁盘写的数据量 KB 单位 write to disk each second KB
  • kB_ccwr/s: 每秒进程向磁盘写入,但是被取消的数据量,This may occur when the task truncates some dirty pagecache.
  • iodelay: Block I/O delay, measured in clock ticks
  • Command: 进程名 task name

2、统计CPU使用情况

# 统计CPU
pidstat -u 1 10
03:03:33 PM   UID       PID    %usr %system  %guest    %CPU   CPU  Command
03:03:34 PM     0      2321    3.96    0.00    0.00    3.96     0  ansible
03:03:34 PM     0      7110    0.00    0.99    0.00    0.99     4  pidstat
03:03:34 PM   997      8539    0.99    0.00    0.00    0.99     5  java
03:03:34 PM   984     15517    0.99    0.00    0.00    0.99     5  java
03:03:34 PM     0     24406    0.99    0.00    0.00    0.99     5  java
03:03:34 PM     0     32158    3.96    0.00    0.00    3.96     2  ansible
登入後複製
  • UID
  • PID
  • %usr: 进程在用户空间占用 cpu 的百分比
  • %system: 进程在内核空间占用 CPU 百分比
  • %guest: 进程在虚拟机占用 CPU 百分比
  • %wait: 进程等待运行的百分比
  • %CPU: 进程占用 CPU 百分比
  • CPU: 处理进程的 CPU 编号
  • Command: 进程名

3、统计内存使用情况

# 统计内存
pidstat -r 1 10
Average:      UID       PID  minflt/s  majflt/s     VSZ    RSS   %MEM  Command
Average:        0         1      0.20      0.00  191256   3064   0.01  systemd
Average:        0      1007      1.30      0.00  143256  22720   0.07  AliYunDun
Average:        0      6642      0.10      0.00 6301904 107680   0.33  java
Average:      997      7326     10.89      0.00 13468904 8395848  26.04  java
Average:        0      7795    348.15      0.00  108376   1233   0.00  pidstat
Average:      997      8539      0.50      0.00 8242256 2062228   6.40  java
Average:      987      9518      0.20      0.00 6300944 1242924   3.85  java
Average:        0     10280      3.70      0.00  807372   8344   0.03  aliyun-service
Average:      984     15517      0.40      0.00 6386464 1464572   4.54  java
Average:        0     16066    236.46      0.00 2678332  71020   0.22  cmagent
Average:      995     20955      0.30      0.00 6312520 1408040   4.37  java
Average:      995     20956      0.20      0.00 6093764 1505028   4.67  java
Average:        0     23936      0.10      0.00 5302416 110804   0.34  java
Average:        0     24406      0.70      0.00 10211672 2361304   7.32  java
Average:        0     26870      1.40      0.00 1470212  36084   0.11  promtail
登入後複製
  • UID
  • PID
  • Minflt/s : 每秒次缺页错误次数 (minor page faults),虚拟内存地址映射成物理内存地址产生的 page fault 次数
  • Majflt/s : 每秒主缺页错误次数 (major page faults), 虚拟内存地址映射成物理内存地址时,相应 page 在 swap 中
  • VSZ virtual memory usage : 该进程使用的虚拟内存 KB 单位
  • RSS : 该进程使用的物理内存 KB 单位
  • %MEM : 内存使用率
  • Command : 该进程的命令 task name

4、查看具体进程使用情况

pidstat -T ALL -r -p 20955 1 10
03:12:16 PM   UID       PID  minflt/s  majflt/s     VSZ    RSS   %MEM  Command
03:12:17 PM   995     20955      0.00      0.00 6312520 1408040   4.37  java

03:12:16 PM   UID       PID minflt-nr majflt-nr  Command
03:12:17 PM   995     20955         0         0  java
登入後複製

以上是Linux效能最佳化知識點總結大全 · 實作+收藏版的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:Linux中文社区
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板