精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

大疆嵌入式面試:Linux內(nèi)存泄漏與高占用排查方法

系統(tǒng) Linux
在 Linux 系統(tǒng)中,內(nèi)存泄漏就像是一個悄無聲息的殺手,慢慢侵蝕著系統(tǒng)的資源。簡單來說,內(nèi)存泄漏是指程序在申請內(nèi)存后,當該內(nèi)存不再被使用時,卻沒有將其釋放回系統(tǒng) ,導致這部分內(nèi)存一直被占用,無法被其他程序使用。

Linux 內(nèi)存泄漏與高占用問題是大疆嵌入式面試中的重點內(nèi)容,掌握有效的排查方法和工具至關(guān)重要。Valgrind 和 AddressSanitizer 是排查內(nèi)存泄漏的利器,top、htop 以及 /proc 文件系統(tǒng)則是監(jiān)控和分析內(nèi)存占用的得力助手 。在實際面試中,不僅要熟悉這些工具和方法的理論知識,更要能夠結(jié)合具體的場景和問題,靈活運用,展示出自己解決問題的能力。

對于準備大疆嵌入式面試的同學,建議大家在復習時,多進行實際操作和案例分析,加深對這些排查方法的理解和掌握。可以自己編寫一些包含內(nèi)存問題的測試程序,然后使用各種工具進行排查和解決,積累實踐經(jīng)驗。同時,要關(guān)注內(nèi)存管理在嵌入式系統(tǒng)中的重要性,理解內(nèi)存泄漏和高占用對系統(tǒng)性能的影響機制,這樣在面試中才能更加深入地回答問題,展現(xiàn)出自己的專業(yè)素養(yǎng)和技術(shù)能力 。

Part1.內(nèi)存泄漏:悄無聲息的系統(tǒng)殺手

在 Linux 系統(tǒng)中,內(nèi)存泄漏就像是一個悄無聲息的殺手,慢慢侵蝕著系統(tǒng)的資源。簡單來說,內(nèi)存泄漏是指程序在申請內(nèi)存后,當該內(nèi)存不再被使用時,卻沒有將其釋放回系統(tǒng) ,導致這部分內(nèi)存一直被占用,無法被其他程序使用。就好比你向圖書館借了一本書,看完后卻不歸還,隨著時間推移,越來越多的人借書不還,圖書館的書就會越來越少,可供其他人借閱的資源也就越來越稀缺。

在嵌入式系統(tǒng)里,內(nèi)存資源本就十分有限,內(nèi)存泄漏帶來的后果往往更加嚴重。每一次內(nèi)存泄漏,都像是從系統(tǒng)的 “內(nèi)存儲備庫” 中偷走了一部分資源,隨著泄漏的不斷積累,系統(tǒng)可用內(nèi)存越來越少。這會導致系統(tǒng)頻繁進行內(nèi)存交換操作,從磁盤的虛擬內(nèi)存中讀寫數(shù)據(jù),而磁盤的讀寫速度遠遠慢于內(nèi)存,從而使得系統(tǒng)性能急劇下降,響應(yīng)變得遲緩,原本流暢運行的程序可能變得卡頓甚至無響應(yīng)。當內(nèi)存泄漏嚴重到一定程度,系統(tǒng)再也無法分配到足夠的內(nèi)存來滿足正常的運行需求,就如同水庫干涸,無法為下游提供足夠的水源,系統(tǒng)便會陷入崩潰,造成無人機飛行異常、工業(yè)控制設(shè)備故障等嚴重問題。

(1)內(nèi)存占用過大為什么?

內(nèi)存占用過大的原因可能有很多,以下是一些常見的情況:

  • 內(nèi)存泄漏:當程序在運行時動態(tài)分配了內(nèi)存但未正確釋放時,會導致內(nèi)存泄漏。這意味著那部分內(nèi)存將無法再被其他代碼使用,最終導致內(nèi)存占用增加。
  • 頻繁的動態(tài)內(nèi)存分配和釋放:如果程序中頻繁進行大量的動態(tài)內(nèi)存分配和釋放操作,可能會導致內(nèi)存碎片化問題。這樣系統(tǒng)將難以有效地管理可用的物理內(nèi)存空間。
  • 數(shù)據(jù)結(jié)構(gòu)和算法選擇不當:某些數(shù)據(jù)結(jié)構(gòu)或算法可能對特定場景具有較高的空間復雜度,從而導致內(nèi)存占用過大。在設(shè)計和選擇數(shù)據(jù)結(jié)構(gòu)和算法時應(yīng)綜合考慮時間效率和空間效率。
  • 緩存未及時清理:如果程序中使用了緩存機制,并且沒有及時清理或管理緩存大小,就會導致緩存占用過多的內(nèi)存空間。
  • 高并發(fā)環(huán)境下資源競爭:在高并發(fā)環(huán)境下,多個線程同時訪問共享資源(包括對內(nèi)存的申請和釋放)可能引發(fā)資源競爭問題。若沒有適當?shù)耐綑C制或鎖策略,可能導致內(nèi)存占用過大。
  • 第三方庫或框架問題:使用的第三方庫或框架可能存在內(nèi)存管理不當、內(nèi)存泄漏等問題,從而導致整體程序的內(nèi)存占用過大。

(2)內(nèi)存泄露和內(nèi)存占用過大區(qū)別?

內(nèi)存泄漏指的是在程序運行過程中,動態(tài)分配的內(nèi)存空間沒有被正確釋放,導致這些內(nèi)存無法再被其他代碼使用。每次發(fā)生內(nèi)存泄漏時,系統(tǒng)可用的物理內(nèi)存空間就會減少一部分,最終導致整體的內(nèi)存占用量增加。

而內(nèi)存占用過大則是指程序在運行時所消耗的物理內(nèi)存超出了合理范圍或預期值。除了因為內(nèi)存泄漏導致的額外占用外,其他原因如頻繁的動態(tài)內(nèi)存分配和釋放、數(shù)據(jù)結(jié)構(gòu)和算法選擇不當、緩存管理問題等都可能導致程序的內(nèi)存占用過大。

可以說,內(nèi)存在被正確管理和使用時,即使有一定程度的動態(tài)分配和釋放操作,也不會造成明顯的長期累積效應(yīng),即不會出現(xiàn)持續(xù)性的內(nèi)存占用過大情況。而如果存在未及時釋放或回收的資源(即發(fā)生了內(nèi)存泄漏),隨著時間推移會逐漸積累并導致整體的內(nèi)存占用越來越高。

因此,在排查和解決內(nèi)存占用過大問題時,需要注意是否存在內(nèi)存泄漏,并且還需綜合考慮其他可能導致內(nèi)存占用過大的因素。

(3)產(chǎn)生的原因

我們在進行程序開發(fā)的過程使用動態(tài)存儲變量時,不可避免地面對內(nèi)存管理的問題。程序中動態(tài)分配的存儲空間,在程序執(zhí)行完畢后需要進行釋放。沒有釋放動態(tài)分配的存儲空間而造成內(nèi)存泄漏,是使用動態(tài)存儲變量的主要問題。

一般情況下,作為開發(fā)人員會經(jīng)常使用系統(tǒng)提供的內(nèi)存管理基本函數(shù),如malloc、realloc、calloc、free等,完成動態(tài)存儲變量存儲空間的分配和釋放。但是,當開發(fā)程序中使用動態(tài)存儲變量較多和頻繁使用函數(shù)調(diào)用時,就會經(jīng)常發(fā)生內(nèi)存管理錯誤。

Part2.虛擬內(nèi)存泄露

一般來說,我們觀察系統(tǒng)的內(nèi)存占用喜歡用top命令,然后輸入m,對系統(tǒng)中整體的內(nèi)存占用情況做個排序,然后在重點觀察,內(nèi)存占用排在前幾位的進程,再逐步的分析。

[root@VM-0-2-centos ~]# top -p 5576
top - 18:21:46 up 198 days, 20:07,  2 users,  load average: 0.10, 0.04, 0.05
Tasks:   1 total,   0 running,   1 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.7 us,  0.3 sy,  0.0 ni, 99.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  1882008 total,    78532 free,   116516 used,  1686960 buff/cache
KiB Swap:        0 total,        0 free,        0 used.  1606660 avail Mem 

 PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND                                                                                                                 
 5576 root      20   0  184064  11248   1124 S  0.0  0.6  10:34.98 nginx

雖然top 也可以觀察到單獨的進程的內(nèi)存變化,不過一般不太好比較內(nèi)存變化的規(guī)律,推薦使用pidstat工具,此工具需要先安裝,通過命令:

yum install sysstat

pidstat 基本說明如下:

  • u:默認的參數(shù),顯示各個進程的cpu使用統(tǒng)計
  • r:顯示各個進程的內(nèi)存使用統(tǒng)計
  • d:顯示各個進程的IO使用情況
  • p:指定進程號
  • w:顯示每個進程的上下文切換情況
  • t:顯示選擇任務(wù)的線程的統(tǒng)計信息外的額外信息
  • T { TASK | CHILD | ALL }

假如我們觀察到如下的內(nèi)存占用情況:pidstat -r -p pid 5

[root@VM-0-2-centos ~]# pidstat -r -p 5981 5
Linux 3.10.0-1127.19.1.el7.x86_64 (VM-0-2-centos)   07/24/2021  _x86_64_    (1 CPU)

06:25:55 PM   UID       PID  minflt/s  majflt/s     VSZ    RSS   %MEM  Command
06:26:00 PM     0      5981      0.20      0.00    4416    352   0.02  a.out
06:26:05 PM     0      5981      0.00      0.00    4416    352   0.02  a.out
06:26:10 PM     0      5981      0.20      0.00    4456    352   0.02  a.out
06:26:15 PM     0      5981      0.00      0.00    4456    352   0.02  a.out
06:26:20 PM     0      5981      0.00      0.00    4456    352   0.02  a.out
06:26:25 PM     0      5981      0.20      0.00    4496    352   0.02  a.out
06:26:30 PM     0      5981      0.00      0.00    4496    352   0.02  a.out
06:26:35 PM     0      5981      0.20      0.00    4536    352   0.02  a.out
06:26:40 PM     0      5981      0.00      0.00    4536    352   0.02  a.out
06:26:45 PM     0      5981      0.20      0.00    4576    352   0.02  a.out
06:26:50 PM     0      5981      0.00      0.00    4576    352   0.02  a.out
06:26:55 PM     0      5981      0.20      0.00    4616    352   0.02  a.out

我們注意下,VSZ即虛擬內(nèi)存的占用每10s增加40,單位為k,即10s增加40k的虛擬內(nèi)存,下面來具體分析下這個程序的內(nèi)存泄露情況。

Part3.分析泄露原因

我們來分析這個進程的內(nèi)存分布情況,來分析這泄露的內(nèi)存有什么特點:

[root@VM-0-2-centos ~]# pmap -x 5981
5981:   ./a.out
Address           Kbytes     RSS   Dirty Mode  Mapping
0000000000400000       4       4       0 r-x-- a.out
0000000000600000       4       4       4 r---- a.out
0000000000601000       4       4       4 rw--- a.out
00007faab436e000    2720     272     272 rw---   [ anon ]
00007faab4616000    1804     260       0 r-x-- libc-2.17.so
00007faab47d9000    2048       0       0 ----- libc-2.17.so
00007faab49d9000      16      16      16 r---- libc-2.17.so
00007faab49dd000       8       8       8 rw--- libc-2.17.so
00007faab49df000      20      12      12 rw---   [ anon ]
00007faab49e4000     136     108       0 r-x-- ld-2.17.so
00007faab4a06000    2012     212     212 rw---   [ anon ]
00007faab4c03000       8       8       8 rw---   [ anon ]
00007faab4c05000       4       4       4 r---- ld-2.17.so
00007faab4c06000       4       4       4 rw--- ld-2.17.so
00007faab4c07000       4       4       4 rw---   [ anon ]
00007ffe0f3f5000     132      16      16 rw---   [ stack ]
00007ffe0f47c000       8       4       0 r-x--   [ anon ]
ffffffffff600000       4       0       0 r-x--   [ anon ]
---------------- ------- ------- ------- 
total kB            8940     940     564

其中Address為開始的地址,Kbytes是虛擬內(nèi)存的大小,RSS為真實內(nèi)存的大小,Dirty為未同步到磁盤上的臟頁,Mode為內(nèi)存的權(quán)限,rw為可寫可讀,rx為可讀和可執(zhí)行。通過幾次觀察,我們發(fā)現(xiàn):

00007faab436e000    2720     272     272 rw---   [ anon ]

為泄露部分,此為匿名內(nèi)存區(qū),也就是沒有映射文件,為malloc或mmap分配的內(nèi)存。同樣是每次增加40K。

此時還是只能大概知道內(nèi)存泄露的位置,我們還先找到具體的代碼位置,這個該怎么分析?代碼的申請,無非是通過malloc和brk這些庫函數(shù)進行內(nèi)存調(diào)用,我們可以用strace跟蹤下。

[root@VM-0-2-centos ~]# strace -f -t -p 5981 -o trace.strace
strace: Process 5981 attached
strace: Process 8519 attached
strace: Process 8533 attached
strace: Process 8547 attached
strace: Process 8557 attached
strace: Process 8575 attached
^Cstrace: Process 5981 detached

我們通過-t選項來顯示時間,-f來跟蹤子進程。直接用cat命令查看跟蹤的文件內(nèi)容,會發(fā)現(xiàn)內(nèi)容相當多,只要是系統(tǒng)調(diào)用都打印了出來,可以通過每次增加40k這個有用的信息搜索下:

[root@VM-0-2-centos ~]# grep 40960  trace.strace 
5981  19:01:44 mmap(NULL, 40960, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0x7faab403a000
5981  19:01:55 mmap(NULL, 40960, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0x7faab4030000
5981  19:02:06 mmap(NULL, 40960, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0x7faab4026000
5981  19:02:17 mmap(NULL, 40960, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0x7faab401c000
5981  19:02:28 mmap(NULL, 40960, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0x7faab4012000

至此我們找到了具體的泄露代碼位置。看下這個測試代碼:

#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/wait.h>
#define _SCHED_H 
#define __USE_GNU 
#include <bits/sched.h>

#define STACK_SIZE 40960

int func(void *arg)
{
    printf("thread enter.\n");
    sleep(1);
    printf("thread exit.\n");

    return 0;
}


int main()
{
    int thread_pid;
    int status;
    int w;

    while (1) {
        void *addr = mmap(NULL, STACK_SIZE, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0);
        if (addr == NULL) {
            perror("mmap");
            goto error;
        }
        printf("creat new thread...\n");
        thread_pid = clone(&func, addr + STACK_SIZE, CLONE_SIGHAND|CLONE_FS|CLONE_VM|CLONE_FILES, NULL);
        printf("Done! Thread pid: %d\n", thread_pid);
        if (thread_pid != -1) {
            do {
                w = waitpid(-1, NULL, __WCLONE | __WALL);
                if (w == -1) {
                    perror("waitpid");
                    goto error;
                }
            } while (!WIFEXITED(status) && !WIFSIGNALED(status));
        }
        sleep(10);
   }

 error:
    return 0;
}

這個測試程序利用mmap申請一塊匿名私有的內(nèi)存,clone為系統(tǒng)函數(shù),pthread_create 和fork底層都是調(diào)用它,用來創(chuàng)建進程/線程,將func的地址指針存放在子進程堆棧的某個位置處,該位置就是該封裝函數(shù)本身返回地址存放的位置,最后一個參數(shù)為func的執(zhí)行參數(shù)。clone可以更靈活控制共享,比如可以控制是否共享內(nèi)存空間,是否共享打開文件,是否共享相同的信號處理函數(shù)等。

我們可以看到,mmap申請內(nèi)存后,需要通過munmap來釋放,這里面沒有釋放,所以導致了虛擬內(nèi)存泄露,這里面申請的內(nèi)存只實際使用了4個字節(jié),即復制了func的指針,其他的內(nèi)存均沒有使用,其實仔細觀察會發(fā)現(xiàn)還有部分的物理內(nèi)存泄露,每次4個字節(jié),可以通過pmap -x 查到。

在 waitpid后面添加:munmap(addr,STACK_SIZE); 即可以實現(xiàn)內(nèi)存的釋放。

如何排查內(nèi)存泄漏

我們平時開發(fā)過程中不可避免的會遇到內(nèi)存泄漏問題,這是常見的問題。既然發(fā)生了內(nèi)存泄漏,我們就要排查內(nèi)存泄漏的問題。想必大家也經(jīng)常會用到以下排查內(nèi)存問題的工具,如下:

  • memwatch
  • mtrace
  • dmalloc
  • ccmalloc
  • valgrind
  • debug_new

Part4.Valgrind 排查工具

在眾多排查 Linux 內(nèi)存泄漏與高占用的工具中,Valgrind 堪稱一把鋒利的 “寶劍”,在嵌入式開發(fā)中被廣泛使用。Valgrind 是一套 Linux 下開放源代碼(GPL V2)的仿真調(diào)試工具的集合 ,它由內(nèi)核(core)以及基于內(nèi)核的其他調(diào)試工具組成。其內(nèi)核就像一個精心搭建的舞臺框架,模擬出一個 CPU 環(huán)境,為其他工具提供表演的場地和基礎(chǔ)服務(wù);而其他工具則如同一個個身懷絕技的演員,在這個舞臺上利用內(nèi)核提供的服務(wù),完成各種特定的內(nèi)存調(diào)試任務(wù)。

(1)安裝與準備

在使用 Valgrind 之前,我們首先得把它安裝到系統(tǒng)中。安裝方法很簡單,對于基于 Debian 或 Ubuntu 的系統(tǒng),只需在終端中輸入 “sudo apt-get install valgrind” ,系統(tǒng)就會自動從軟件源中下載并安裝 Valgrind 及其依賴項,就像從云端倉庫中取來一件趁手的工具。而對于 CentOS 等基于 Red Hat 的系統(tǒng),安裝命令則變?yōu)?“sudo yum install valgrind” ,同樣能快速將這個強大的工具收入囊中。

(2)基本使用命令

安裝完成后,就可以使用 Valgrind 來檢測程序的內(nèi)存問題了。它的基本使用命令格式為:“valgrind [valgrind-options] your-prog [your-prog-options]” 。這里的 “valgrind-options” 是 Valgrind 自身的一些選項,用于控制它的行為和輸出;“your-prog” 是你要檢測的目標程序;“your-prog-options” 則是目標程序運行時需要的參數(shù)。

其中,最常用的選項是 “--tool=memcheck” ,它指定使用 Memcheck 工具,這是 Valgrind 應(yīng)用最廣泛的工具,一個重量級的內(nèi)存檢查器,能夠發(fā)現(xiàn)開發(fā)中絕大多數(shù)內(nèi)存錯誤使用情況。比如使用未初始化的內(nèi)存,使用已經(jīng)釋放了的內(nèi)存,內(nèi)存訪問越界等。若想讓 Valgrind 在程序結(jié)束時詳細敘述每一個內(nèi)存泄漏情況,就可以加上 “--leak-check=full” 選項;如果希望在錯誤報告中顯示可到達的內(nèi)存塊(即雖然沒有釋放,但仍有指針指向的內(nèi)存塊),則可以使用 “--show-reachable=yes” 選項。

(3)工作原理

Valgrind 中的 Memcheck 工具能夠檢測出內(nèi)存問題,關(guān)鍵在于其建立了兩個全局表。第一個是 Valid-Value 表,對于進程的整個地址空間中的每一個字節(jié) (byte),都有與之對應(yīng)的 8 個 bits;對于 CPU 的每個寄存器,也有一個與之對應(yīng)的 bit 向量 。這些 bits 就像一個個小衛(wèi)士,負責記錄該字節(jié)或者寄存器值是否具有有效的、已初始化的值。第二個是 Valid-Address 表,對于進程整個地址空間中的每一個字節(jié) (byte),還有與之對應(yīng)的 1 個 bit,它就像一把地址鎖,負責記錄該地址是否能夠被讀寫。

當程序要讀寫內(nèi)存中某個字節(jié)時,Memcheck 首先會檢查這個字節(jié)對應(yīng)的 A bit。如果該 A bit 顯示該位置是無效位置,就如同試圖打開一把被鎖住的門,Memcheck 則會報告讀寫錯誤。內(nèi)核(core)類似于一個虛擬的 CPU 環(huán)境,這樣當內(nèi)存中的某個字節(jié)被加載到真實的 CPU 中時,該字節(jié)對應(yīng)的 V bit 也會被加載到虛擬的 CPU 環(huán)境中。一旦寄存器中的值被用來產(chǎn)生內(nèi)存地址,或者該值能夠影響程序輸出,Memcheck 就會像一個警惕的監(jiān)察員,檢查對應(yīng)的 V bits,如果該值尚未初始化,則會報告使用未初始化內(nèi)存錯誤。

(4)實際案例分析

為了更直觀地感受 Valgrind 的強大功能,我們來看一個實際案例。假設(shè)有如下一段簡單的 C 語言代碼:

#include <stdio.h>
#include <stdlib.h>

void leak() {
    int* ptr = (int*)malloc(10 * sizeof(int));
    // 這里沒有釋放ptr指向的內(nèi)存,造成內(nèi)存泄漏
}

void uninit() {
    int x;
    if (x > 0) {
        // 使用了未初始化的變量x,這是一個錯誤
    }
}

int main() {
    int* arr = (int*)malloc(20 * sizeof(int));
    arr[20] = 0; // 數(shù)組越界訪問,訪問了arr[20],而數(shù)組arr只有20個元素,合法索引范圍是0到19
    leak();
    uninit();
    free(arr);
    return 0;
}

這段代碼中存在內(nèi)存泄漏、使用未初始化變量以及數(shù)組越界訪問的問題。我們使用 Valgrind 來檢測它。首先,使用 “gcc -g -O0 -Wall test.c -o test” 命令進行編譯,其中 “-g” 選項用于生成調(diào)試信息,這對于 Valgrind 準確報告錯誤位置至關(guān)重要;“-O0” 選項表示不進行優(yōu)化,防止優(yōu)化影響調(diào)試結(jié)果;“-Wall” 選項用于開啟所有常見的警告信息。

編譯完成后,執(zhí)行 “valgrind --tool=memcheck --leak-check=full --show-reachable=yes./test” 命令。Valgrind 運行后,會輸出詳細的錯誤報告:

==2596== Memcheck, a memory error detector
==2596== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==2596== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==2596== Command:./test
==2596== 
==2596== Invalid write of size 4
==2596==    at 0x4005C3: main (test.c:16)
==2596==  Address 0x51f3050 is 0 bytes after a block of size 80 alloc'd
==2596==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2596==    at 0x4005A9: main (test.c:14)
==2596== 
==2596== Conditional jump or move depends on uninitialised value(s)
==2596==    at 0x4005F5: uninit (test.c:9)
==2596==    by 0x40060A: main (test.c:18)
==2596== 
==2596== 100 bytes in 1 blocks are definitely lost in loss record 1 of 1
==2596==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2596==    by 0x4005D8: leak (test.c:5)
==2596==    by 0x400605: main (test.c:17)
==2596== 
==2596== LEAK SUMMARY:
==2596==    definitely lost: 100 bytes in 1 blocks
==2596==    indirectly lost: 0 bytes in 0 blocks
==2596==      possibly lost: 0 bytes in 0 blocks
==2596==    still reachable: 0 bytes in 0 blocks
==2596==         suppressed: 0 bytes in 0 blocks
==2596== 
==2596== For counts of detected and suppressed errors, rerun with: -v
==2596== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)

從報告中可以清晰地看到,Valgrind 準確地指出了代碼中的數(shù)組越界訪問錯誤(“Invalid write of size 4”),發(fā)生在 “test.c” 文件的 16 行;使用未初始化變量錯誤(“Conditional jump or move depends on uninitialised value (s)”),發(fā)生在 “test.c” 文件的 9 行;以及內(nèi)存泄漏錯誤(“100 bytes in 1 blocks are definitely lost”),發(fā)生在 “test.c” 文件的 5 行,泄漏的內(nèi)存大小為 100 字節(jié)(因為分配了 10 個 int 類型的內(nèi)存空間,每個 int 通常為 4 字節(jié),共 40 字節(jié),但這里報告 100 字節(jié)可能包含了一些內(nèi)部管理信息)。

根據(jù)這些詳細的錯誤信息,我們就可以輕松地定位并修復代碼中的問題,讓程序更加健壯和穩(wěn)定 。通過這個案例,我們可以看到 Valgrind 在排查內(nèi)存問題時的高效和精準,它就像一位經(jīng)驗豐富的偵探,能夠在復雜的代碼迷宮中,準確地找出內(nèi)存相關(guān)的 “罪犯”,為我們解決內(nèi)存泄漏和高占用問題提供了有力的支持。

Part5.AddressSanitizer(ASan)排查工具

AddressSanitizer(ASAN)是一款由 Google 開發(fā)的內(nèi)存錯誤檢測器 ,在檢測內(nèi)存泄漏方面展現(xiàn)出獨特的優(yōu)勢。它主要通過編譯器插樁的方式,在程序編譯階段將內(nèi)存訪問檢查代碼巧妙地插入到目標程序中,就像在程序的關(guān)鍵位置安插了許多 “小哨兵”,時刻監(jiān)視著內(nèi)存的使用情況。

(1)特點與優(yōu)勢

與傳統(tǒng)的內(nèi)存檢測工具相比,AddressSanitizer 最大的特點就是快。它對程序性能的影響相對較小,在運行時的開銷僅會使程序速度減慢約 2 倍 ,而 Valgrind 則可能使程序減慢 10 倍之多。這使得開發(fā)者在使用 ASAN 進行內(nèi)存檢測時,不必花費過多時間等待程序運行結(jié)果,大大提高了調(diào)試效率。

ASAN 能夠檢測到多種類型的內(nèi)存錯誤,除了內(nèi)存泄漏,還包括釋放后使用(懸空指針)、堆緩沖區(qū)溢出、堆棧緩沖區(qū)溢出、全局緩沖區(qū)溢出、在作用域之后使用、初始化順序錯誤等 。它就像一個全能的內(nèi)存問題探測器,能夠全面地掃描程序中的內(nèi)存隱患,為開發(fā)者提供更全面的內(nèi)存錯誤信息。

(2)使用步驟

使用 AddressSanitizer 進行內(nèi)存檢測,步驟也相對簡潔明了。首先是編譯階段,需要在編譯命令中添加 “-fsanitize=address” 選項。例如,使用 GCC 編譯 C++ 程序時,可以這樣操作:

gcc -fsanitize=address -g -O0 -Wall test.c -o test

這里的 “-g” 選項用于生成調(diào)試信息,“-O0” 表示不進行優(yōu)化,“-Wall” 用于開啟所有常見的警告信息,這些選項與 Valgrind 編譯時類似,都是為了確保在調(diào)試過程中能夠獲取更準確的信息。

編譯完成后,直接運行生成的可執(zhí)行文件即可。當程序運行過程中出現(xiàn)內(nèi)存錯誤時,AddressSanitizer 會立即捕獲并在控制臺上輸出詳細的錯誤報告。例如,對于下面這段存在內(nèi)存泄漏的代碼:

#include <stdio.h>
#include <stdlib.h>

int main() {
    int* ptr = (int*)malloc(10 * sizeof(int));
    // 這里沒有釋放ptr指向的內(nèi)存,造成內(nèi)存泄漏
    return 0;
}

運行編譯后的程序,ASAN 會輸出類似如下的錯誤報告:

=================================================================
==1234==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 40 byte(s) in 1 object(s) allocated from:
    #0 0x7f919d0c8b97 in malloc (/lib/x86_64-linux-gnu/libasan.so.5+0x10eb97)
    #1 0x40059d in main /home/user/test.c:5

SUMMARY: AddressSanitizer: 40 byte(s) leaked in 1 allocation(s).

從報告中可以清晰地看到,ASAN 準確地指出了內(nèi)存泄漏的位置在 “test.c” 文件的第 5 行,泄漏的內(nèi)存大小為 40 字節(jié)(因為分配了 10 個 int 類型的內(nèi)存空間,每個 int 通常為 4 字節(jié),共 40 字節(jié)),并且還給出了內(nèi)存分配的調(diào)用棧信息,方便開發(fā)者快速定位問題根源。

Part6.其他內(nèi)存泄露分析

其實上面的內(nèi)存泄露是我們知道了具體的泄露的進程,然后再做詳細分析。那么如果不知道哪里內(nèi)存泄露了,有什么辦法,可以通過分析meminfo文件,來觀察泄露的類型。

[root@VM-0-2-centos test]# cat /proc/meminfo
MemTotal:        1882008 kB
MemFree:          752948 kB
MemAvailable:    1610108 kB
Buffers:          564900 kB
Cached:           399584 kB
SwapCached:            0 kB
Active:           808140 kB
Inactive:         220812 kB
Active(anon):      64548 kB
Inactive(anon):      488 kB
Active(file):     743592 kB
Inactive(file):   220324 kB
Unevictable:           0 kB
Mlocked:               0 kB
SwapTotal:             0 kB
SwapFree:              0 kB
....

meminfo文件是Linux系統(tǒng)中用于顯示內(nèi)存使用情況的詳細信息文件,它位于/proc目錄下,提供了關(guān)于系統(tǒng)內(nèi)存使用的全面信息。通過查看和分析meminfo文件的內(nèi)容,可以了解系統(tǒng)的內(nèi)存使用狀況,包括總內(nèi)存、空閑內(nèi)存、緩存、交換分區(qū)等信息,這對于排查內(nèi)存相關(guān)的問題非常有幫助。

meminfo文件包含的主要信息及其含義如下:

  • MemTotal:系統(tǒng)總內(nèi)存大小。
  • MemFree:系統(tǒng)空閑內(nèi)存大小。
  • MemAvailable:可用內(nèi)存大小,包括空閑內(nèi)存和緩存。
  • Buffers:用于緩存數(shù)據(jù)的內(nèi)存大小。
  • Cached:用于緩存文件系統(tǒng)的內(nèi)存大小。
  • SwapCached:用于緩存交換分區(qū)的內(nèi)存大小。
  • Active 和 Inactive:分別表示活動和非活動內(nèi)存大小,即正在使用或最近使用的內(nèi)存和最近沒有使用的內(nèi)存。
  • SwapTotal 和 SwapFree:交換分區(qū)總大小和空閑大小。
  • Dirty 和 Writeback:等待寫回到磁盤的內(nèi)存大小和正在寫回到磁盤的內(nèi)存大小。
  • AnonPages、Mapped、Shmem 等:分別表示用于匿名映射、已映射到文件的內(nèi)存、共享內(nèi)存大小等。
  • Slab、SReclaimable、SUnreclaim 等:內(nèi)核數(shù)據(jù)結(jié)構(gòu)緩存的內(nèi)存大小以及可回收和不可回收的Slab內(nèi)存大小。
  • KernelStack、PageTables 等:內(nèi)核棧的內(nèi)存大小和頁面表的內(nèi)存大小。
  • CommitLimit 和 Committed_AS:可用內(nèi)存可支持的最大內(nèi)存大小和已分配的內(nèi)存大小,包括內(nèi)存和交換分區(qū)。
  • VmallocTotal、VmallocUsed 等:虛擬內(nèi)存總大小和已使用的虛擬內(nèi)存大小。

排查內(nèi)存問題時,可以通過以下步驟進行:

  1. 首先,使用cat /proc/meminfo命令查看meminfo文件的內(nèi)容,了解系統(tǒng)的整體內(nèi)存使用情況。
  2. 分析MemTotal和MemFree的值,了解系統(tǒng)的總內(nèi)存和可用空閑內(nèi)存。
  3. 注意MemAvailable的值,它表示應(yīng)用程序可用的內(nèi)存,與MemFree的區(qū)別在于MemAvailable考慮了Buffers和Cached的大小,這些通常在系統(tǒng)需要時可以被回收。
  4. 檢查SwapUsage(雖然meminfo文件中沒有直接顯示SwapUsage,但可以通過SwapTotal和SwapFree計算得出),如果Swap空間被大量使用,可能意味著物理內(nèi)存不足。
  5. 注意Active、Inactive、Dirty和Writeback等值,這些指標可以幫助你了解系統(tǒng)當前的內(nèi)存使用模式和可能的性能瓶頸。
  6. 如果發(fā)現(xiàn)某些特定類型的內(nèi)存使用異常高(如AnonPages、Shmem等),可能需要進一步調(diào)查這些類型的內(nèi)存使用情況,以確定是否存在內(nèi)存泄漏或其他問題。
  7. 使用其他工具如freevmstattophtop等命令提供的信息與meminfo文件的內(nèi)容進行對比,以獲得更全面的系統(tǒng)內(nèi)存使用情況視圖。

通過綜合分析meminfo文件的內(nèi)容和其他相關(guān)工具的輸出,可以有效地排查和解決Linux系統(tǒng)中的內(nèi)存相關(guān)問題

Part7.高內(nèi)存占用:系統(tǒng)性能的隱形障礙

高內(nèi)存占用就像是系統(tǒng)性能的隱形障礙,雖然不像內(nèi)存泄漏那樣直觀,但同樣會給系統(tǒng)帶來嚴重的影響。當系統(tǒng)內(nèi)存被大量占用時,就好比一個原本寬敞的房間堆滿了雜物,活動空間變得狹小,行動也變得困難。

在嵌入式系統(tǒng)中,高內(nèi)存占用可能會導致系統(tǒng)響應(yīng)速度明顯變慢。當系統(tǒng)需要處理新的任務(wù)或響應(yīng)用戶的操作時,由于內(nèi)存中已經(jīng)充滿了各種數(shù)據(jù)和程序,沒有足夠的空閑內(nèi)存來快速加載和運行新的任務(wù),就像倉庫里沒有足夠的空間存放新的貨物,只能花費時間去整理和騰出空間,這就使得系統(tǒng)的響應(yīng)變得遲緩。原本能夠迅速執(zhí)行的指令,現(xiàn)在可能需要等待較長時間才能完成,用戶會明顯感覺到設(shè)備的操作變得卡頓,例如無人機在執(zhí)行復雜飛行指令時,反應(yīng)變得不及時,工業(yè)控制設(shè)備對傳感器數(shù)據(jù)的處理出現(xiàn)延遲等。

高內(nèi)存占用還會極大地降低系統(tǒng)的多任務(wù)處理能力。在現(xiàn)代嵌入式應(yīng)用中,很多設(shè)備需要同時運行多個任務(wù),如無人機既要實時處理飛行姿態(tài)數(shù)據(jù),又要傳輸高清圖像、接收遠程控制指令等。如果內(nèi)存被某個或某些任務(wù)大量占用,其他任務(wù)就無法獲得足夠的內(nèi)存資源來正常運行,就像多個工人在一個狹小的工作空間里工作,互相干擾,效率低下。這可能導致一些任務(wù)被迫暫停或中斷,影響整個系統(tǒng)的功能完整性和穩(wěn)定性,甚至可能引發(fā)任務(wù)之間的沖突,導致系統(tǒng)出現(xiàn)錯誤或異常行為。

高內(nèi)存占用與內(nèi)存泄漏之間也存在著緊密的關(guān)聯(lián)。內(nèi)存泄漏是導致高內(nèi)存占用的一個重要原因,隨著內(nèi)存泄漏的不斷發(fā)生,系統(tǒng)中被占用卻無法釋放的內(nèi)存越來越多,直接導致了系統(tǒng)內(nèi)存占用率的持續(xù)上升 。就像一個不斷漏水的水桶,水不斷地流進桶里卻無法流出,桶里的水就會越來越滿。而高內(nèi)存占用又會加劇內(nèi)存泄漏的影響,因為當系統(tǒng)內(nèi)存緊張時,程序在分配和釋放內(nèi)存時更容易出現(xiàn)錯誤,進一步增加了內(nèi)存泄漏的風險,形成一個惡性循環(huán)。例如,在內(nèi)存緊張的情況下,程序可能會為了獲取內(nèi)存而采取一些不安全的內(nèi)存分配方式,或者在釋放內(nèi)存時出現(xiàn)誤判,導致本應(yīng)釋放的內(nèi)存沒有被釋放,從而加重內(nèi)存泄漏問題。

★排查手段一:top 與 htop 實時監(jiān)控

在排查 Linux 內(nèi)存泄漏與高占用問題時,top 和 htop 命令是我們最先會用到的 “偵察兵”,它們就像系統(tǒng)的實時監(jiān)控儀表盤,能夠讓我們快速了解系統(tǒng)內(nèi)存的使用狀況,直觀地發(fā)現(xiàn)那些占用內(nèi)存過高的 “嫌疑進程”。

①top 命令:系統(tǒng)資源的實時監(jiān)視器

top 命令是 Linux 系統(tǒng)中常用的性能分析工具,它能夠?qū)崟r顯示系統(tǒng)中各個進程的資源占用狀況 ,如同一個忙碌的指揮中心,時刻匯報著系統(tǒng)的 “健康狀態(tài)”。在終端中輸入 “top” 命令,即可打開這個實時監(jiān)控界面,系統(tǒng)默認每 3 秒刷新一次信息 ,讓你能夠及時捕捉到系統(tǒng)狀態(tài)的變化。

在 top 命令的輸出界面中,頭部區(qū)域是系統(tǒng)整體概覽的展示區(qū),這里包含了當前時間、系統(tǒng)已經(jīng)運行的時長、登錄用戶數(shù),以及系統(tǒng)負載等關(guān)鍵信息 ,就像汽車儀表盤上的時間、里程和負載指示燈,讓你對系統(tǒng)的整體運行環(huán)境有一個初步了解。同時,這里還會顯示 CPU 和內(nèi)存的使用情況,包括總內(nèi)存、已用內(nèi)存、空閑內(nèi)存等,讓你一眼就能看出系統(tǒng)內(nèi)存的 “庫存” 狀態(tài)。

主體部分則是以列表形式展示系統(tǒng)中的各個進程,每一行都代表著一個進程,詳細列出了進程的各種信息,如進程 ID(PID),就像是進程的身份證號碼,用于唯一標識每個進程;用戶(USER),表明該進程是由哪個用戶啟動的;優(yōu)先級(PR),反映了進程在系統(tǒng)資源分配中的優(yōu)先程度;虛擬內(nèi)存使用量(VIRT),表示進程占用的虛擬內(nèi)存大小,包括進程使用的代碼、數(shù)據(jù)和共享庫等;

物理內(nèi)存使用量(RES),即進程實際占用的物理內(nèi)存大小;共享內(nèi)存(SHR),是進程與其他進程共享的內(nèi)存部分;狀態(tài)(S),顯示進程當前是正在運行(R)、休眠(S)、停止(T)還是處于僵尸狀態(tài)(Z);CPU 使用率(% CPU),直觀地展示了該進程占用 CPU 資源的百分比;內(nèi)存使用率(% MEM),表示進程占用物理內(nèi)存和總內(nèi)存的比例;累計 CPU 時間(TIME+),記錄了該進程從啟動到當前累計使用的 CPU 時間;以及進程名稱(COMMAND),讓你清楚知道這個進程對應(yīng)的是哪個程序或服務(wù)。

在實際使用 top 命令時,我們還可以通過一些選項和交互命令來更靈活地獲取所需信息。比如,使用 “-d” 選項可以設(shè)置更新間隔時間,例如 “top -d 2” 表示每 2 秒刷新一次信息,就像你可以調(diào)整汽車儀表盤上某些數(shù)據(jù)的更新頻率,以適應(yīng)不同的觀察需求。如果只想監(jiān)控特定的進程 ID,可以使用 “-p” 選項,如 “top -p 1234”,只關(guān)注 PID 為 1234 的進程,這對于排查某個關(guān)鍵服務(wù)的內(nèi)存使用情況非常有用。“-u” 選項則用于指定要監(jiān)控的用戶所屬的進程,如 “top -u username”,方便查看特定用戶相關(guān)進程的內(nèi)存占用。

而在 top 命令的交互界面中,按下 “M” 鍵,就可以按內(nèi)存使用率對進程進行排序,讓占用內(nèi)存較多的進程排在前面,快速定位到內(nèi)存占用大戶,就像在一個貨物清單中,按照貨物重量進行排序,找出最重的那些貨物。按下 “P” 鍵則可以按 CPU 使用率排序,幫助我們發(fā)現(xiàn)占用 CPU 資源較多的進程。如果想要殺死某個進程,可以按下 “k” 鍵,然后輸入要殺死的進程 ID;若要重新調(diào)整進程的優(yōu)先級,按下 “r” 鍵,再輸入進程 ID 和新的優(yōu)先級值即可。

②htop 命令:更直觀強大的監(jiān)控利器

htop 命令是 top 命令的增強版本,它在功能和用戶體驗上都有了顯著的提升,就像是在普通相機的基礎(chǔ)上,增加了更多高級功能和更清晰的顯示效果,讓我們對系統(tǒng)內(nèi)存狀況的監(jiān)控更加得心應(yīng)手。

與 top 命令類似,在終端輸入 “htop” 即可啟動這個強大的監(jiān)控工具。htop 的界面布局與 top 有相似之處,但更加美觀和直觀 。頭部同樣展示了系統(tǒng)整體概覽信息,包括 CPU 使用率、內(nèi)存使用率、交換空間使用率等,不過它是以彩色條形圖的形式呈現(xiàn),讓你能更直觀地感受到系統(tǒng)資源的使用比例,就像一個更生動的儀表盤,不同顏色的進度條代表著不同資源的使用情況。同時,這里也顯示了系統(tǒng)時間、運行時間、登錄用戶數(shù)和系統(tǒng)負載等信息。

主體部分的進程列表也列出了與 top 命令類似的進程信息,但 htop 的顯示更加清晰,并且支持鼠標操作,操作起來更加便捷 。例如,你可以直接用鼠標點擊某一列的標題,對進程按照該列信息進行排序,而無需像 top 命令那樣記住特定的按鍵操作。

htop 命令還提供了許多豐富的功能和交互命令。按下 “F2” 鍵可以進入設(shè)置菜單,在這里你可以根據(jù)自己的需求和喜好,自定義顯示的列、顏色主題、排序方式等,就像給你的監(jiān)控工具進行個性化定制,讓它更符合你的使用習慣。按下 “F3” 鍵可以搜索進程,當系統(tǒng)中進程眾多時,通過輸入進程名稱或關(guān)鍵字,就能快速定位到相關(guān)進程,節(jié)省查找時間。如果想要殺死某個進程,在 htop 中操作更加簡單,直接選擇要殺死的進程,然后按下 “F9” 鍵即可,無需手動輸入進程 ID,這對于需要快速處理問題的場景非常實用。

使用 “+” 和 “-” 鍵可以輕松調(diào)整進程的優(yōu)先級,選擇要調(diào)整優(yōu)先級的進程后,按 “+” 鍵提高優(yōu)先級,按 “-” 鍵降低優(yōu)先級,方便你根據(jù)實際情況優(yōu)化系統(tǒng)資源分配。按下 “F5” 鍵可以切換到樹狀圖模式,顯示進程之間的父子關(guān)系,這對于分析復雜的進程結(jié)構(gòu)和依賴關(guān)系非常有幫助,就像查看一個家族樹,清晰地了解各個進程之間的傳承和關(guān)聯(lián)。按下 “F6” 鍵可以選擇不同的排序方式,除了按 CPU 使用率和內(nèi)存使用率排序外,還可以根據(jù)其他指標進行排序,滿足不同的監(jiān)控需求。

通過 top 和 htop 命令的實時監(jiān)控,我們能夠快速定位到高內(nèi)存占用的進程,為進一步深入排查內(nèi)存問題提供了重要線索。它們就像我們在排查內(nèi)存泄漏與高占用問題道路上的得力助手,讓我們在面對復雜的系統(tǒng)內(nèi)存狀況時,能夠做到心中有數(shù),有的放矢地進行后續(xù)的分析和處理。

★排查手段二:/proc 文件系統(tǒng)深度剖析

在 Linux 系統(tǒng)中,/proc 文件系統(tǒng)是一個非常強大且特殊的工具,它就像一個系統(tǒng)信息的寶藏庫,為我們提供了豐富的系統(tǒng)運行時信息,其中與內(nèi)存相關(guān)的文件,更是排查內(nèi)存泄漏與高占用問題的關(guān)鍵線索來源。

①/proc/[PID]/status:進程狀態(tài)與內(nèi)存概況

在 /proc 文件系統(tǒng)中,每個正在運行的進程都有一個以其進程 ID(PID)命名的目錄 ,如 /proc/1234,其中的 status 文件就像是這個進程的 “健康報告”,詳細記錄了進程的各種狀態(tài)信息以及內(nèi)存使用的概況。

打開 /proc/[PID]/status 文件,我們可以看到一系列關(guān)鍵信息。其中,“VmPeak” 表示進程虛擬內(nèi)存使用量的峰值 ,它記錄了進程在運行過程中曾經(jīng)達到的最高虛擬內(nèi)存占用情況,就像一個運動員在比賽中跳出的最高紀錄,反映了進程在內(nèi)存需求上的最大值。“VmSize” 代表當前進程虛擬內(nèi)存的實際使用量 ,這是進程當前占用的虛擬內(nèi)存大小,包括代碼段、數(shù)據(jù)段、堆、棧以及共享庫等所占用的虛擬內(nèi)存空間,是我們了解進程當前內(nèi)存 “胃口” 大小的重要指標。“VmLck” 表示被鎖定的內(nèi)存大小 ,當進程使用了 mlock () 函數(shù)等機制將部分內(nèi)存鎖定,使其不能被交換到磁盤時,這部分內(nèi)存的大小就會記錄在這里,被鎖定的內(nèi)存就像是被特殊標記的 “保險箱”,始終保留在物理內(nèi)存中。

“VmHWM” 即 High Water Mark,是進程實際使用物理內(nèi)存的峰值 ,它記錄了進程在運行過程中曾經(jīng)占用物理內(nèi)存的最大值,反映了進程在物理內(nèi)存使用上的最高水平。“VmRSS” 則是進程當前實際占用的物理內(nèi)存大小 ,這是我們最關(guān)注的指標之一,它直接反映了進程當前實實在在占用的物理內(nèi)存資源,就像一個房間里實際擺放的物品所占據(jù)的空間大小。

“VmData” 表示進程數(shù)據(jù)段的大小 ,包括已初始化的數(shù)據(jù)和未初始化的數(shù)據(jù),這部分內(nèi)存主要用于存儲進程運行時的數(shù)據(jù)變量等信息。“VmStk” 代表進程用戶態(tài)棧的大小 ,棧是一種后進先出的數(shù)據(jù)結(jié)構(gòu),用于函數(shù)調(diào)用、局部變量存儲等,VmStk 記錄了進程棧所占用的內(nèi)存空間。“VmExe” 是進程代碼段的大小 ,也就是進程可執(zhí)行文件所占用的內(nèi)存空間,這里存放著進程運行的指令代碼。“VmLib” 表示進程使用的庫映射到虛擬內(nèi)存空間的大小 ,當進程依賴各種動態(tài)鏈接庫時,這些庫會被映射到進程的虛擬內(nèi)存空間中,VmLib 記錄了這部分庫所占用的內(nèi)存大小。

通過分析這些內(nèi)存相關(guān)的字段,我們可以對進程的內(nèi)存使用情況有一個全面的了解。例如,當我們發(fā)現(xiàn) “VmRSS” 不斷增長,而 “VmSize” 也同步增加,且沒有明顯的釋放跡象時,就可能存在內(nèi)存泄漏的隱患。這就好比一個人不斷地往房間里搬東西,卻從不清理,房間里的物品(內(nèi)存占用)就會越來越多。而如果 “VmPeak” 與當前的 “VmSize” 相差很大,說明進程在運行過程中曾經(jīng)有過較大的內(nèi)存需求波動,這也可能是我們需要進一步關(guān)注的點,也許在某個特定的操作或時間段內(nèi),進程的內(nèi)存使用出現(xiàn)了異常增長。

②/proc/[PID]/smaps:內(nèi)存映射區(qū)域的詳細剖析

/proc/[PID]/smaps 文件則像是一個放大鏡,為我們提供了比 /proc/[PID]/status 文件更加詳細的內(nèi)存映射區(qū)域信息 ,讓我們能夠深入了解進程內(nèi)存使用的每一個細節(jié)。

smaps 文件中,每一個內(nèi)存映射區(qū)域都有詳細的記錄,包括該區(qū)域的起始地址和結(jié)束地址,以及一系列描述內(nèi)存使用情況的字段。“Size” 表示該虛擬內(nèi)存區(qū)域的大小 ,它明確了這個內(nèi)存塊在虛擬地址空間中的范圍,就像劃定了一塊土地的邊界。“Rss” 是實際分配給該段落物理頁框的數(shù)量(單位 KB) ,也就是該內(nèi)存區(qū)域當前實際占用的物理內(nèi)存大小,它反映了進程在物理內(nèi)存中實實在在占據(jù)的 “地盤”。“Pss(Proportional Set Size)” 表示共享頁面按比例分攤后的大小 ,對于共享內(nèi)存區(qū)域,Pss 能夠更準確地反映該進程實際使用的內(nèi)存份額。

當多個進程共享同一個動態(tài)鏈接庫時,庫所占用的內(nèi)存會根據(jù)每個進程的使用情況按比例分攤,Pss 就是這個分攤后的大小,避免了在共享內(nèi)存情況下單純以 Rss 計算可能導致的內(nèi)存使用統(tǒng)計偏差。“Shared_Clean” 和 “Shared_Dirty” 分別表示和其它進程共享的未修改的 page 的大小以及共享的被改寫的 page 的大小 ,這兩個字段幫助我們了解共享內(nèi)存中數(shù)據(jù)的狀態(tài)。如果 “Shared_Dirty” 不斷增加,可能意味著多個進程對共享內(nèi)存的頻繁修改,這可能會帶來數(shù)據(jù)一致性等問題,也可能影響內(nèi)存的使用效率。

“Private_Clean” 和 “Private_Dirty” 則是未被改寫的私有頁面的大小和已被改寫的私有頁面的大小 ,用于描述進程私有的內(nèi)存頁面的狀態(tài)。“Referenced” 表示已經(jīng)被引用過的頁面數(shù)目 ,它反映了內(nèi)存頁面的使用活躍度,被引用次數(shù)多的頁面說明是進程運行過程中經(jīng)常訪問的數(shù)據(jù)或代碼所在的頁面。“Anonymous” 是不關(guān)聯(lián)任何磁盤文件的匿名映射空間大小 ,例如通過 malloc () 函數(shù)分配的堆內(nèi)存,通常就是匿名映射的,這個字段可以幫助我們了解進程中匿名內(nèi)存的使用情況。“Swap” 表示當前已交換出去至 swap 分區(qū)中的數(shù)據(jù)量 ,當物理內(nèi)存不足時,系統(tǒng)會將一些不常用的內(nèi)存頁面交換到磁盤的 swap 分區(qū)中,Swap 字段記錄了這部分被交換出去的數(shù)據(jù)量。如果 Swap 的值持續(xù)增加,說明系統(tǒng)內(nèi)存緊張,頻繁進行內(nèi)存交換,這會嚴重影響系統(tǒng)性能,因為磁盤的讀寫速度遠遠慢于內(nèi)存。

在實際排查內(nèi)存問題時,我們可以通過監(jiān)控 smaps 文件中相關(guān)字段的變化來發(fā)現(xiàn)異常。比如,當某個進程的 “Rss” 持續(xù)上升,而 “Pss” 也同步增加,且 “Anonymous” 區(qū)域不斷擴大,同時 “Swap” 也開始出現(xiàn)增長趨勢,這很可能是該進程存在內(nèi)存泄漏或者內(nèi)存使用不合理的情況。我們可以進一步分析具體是哪些內(nèi)存映射區(qū)域出現(xiàn)了異常,結(jié)合進程的功能和代碼邏輯,定位到問題的根源。例如,如果發(fā)現(xiàn)某個動態(tài)庫的內(nèi)存映射區(qū)域中 “Shared_Dirty” 異常高,可能是該庫在使用共享內(nèi)存時存在頻繁的寫入操作,導致內(nèi)存使用效率低下,甚至可能存在數(shù)據(jù)競爭等問題,需要進一步檢查庫的使用方式和相關(guān)代碼。

通過深入分析 /proc/[PID]/status 和 /proc/[PID]/smaps 文件,我們能夠獲取到進程內(nèi)存使用的詳細信息,為排查 Linux 內(nèi)存泄漏與高占用問題提供有力的支持,就像一位經(jīng)驗豐富的醫(yī)生通過詳細的檢查報告,準確地診斷出病人身體的問題所在。

Part8.面試真題實戰(zhàn)演練

為了讓大家更清楚地了解在實際面試中如何運用這些排查方法,我們來模擬一個大疆面試中可能出現(xiàn)的場景。假設(shè)你遇到了一個多線程程序,在運行一段時間后,系統(tǒng)出現(xiàn)了內(nèi)存泄漏和高內(nèi)存占用的問題,導致系統(tǒng)性能嚴重下降。現(xiàn)在,面試官要求你找出問題所在并提出解決方案 。

(1)場景描述

這是一個基于 Linux 系統(tǒng)的多線程數(shù)據(jù)處理程序,用于實時處理傳感器采集的數(shù)據(jù)。程序中有多個線程,分別負責數(shù)據(jù)采集、數(shù)據(jù)處理和結(jié)果存儲。在程序運行初期,一切正常,但隨著時間的推移,系統(tǒng)內(nèi)存占用不斷上升,最終導致系統(tǒng)響應(yīng)遲緩,甚至出現(xiàn)死機現(xiàn)象。

(2)排查步驟

  1. 初步觀察與進程定位:首先,使用 top 或 htop 命令,查看系統(tǒng)中各個進程的內(nèi)存占用情況。通過這一步,我們發(fā)現(xiàn)數(shù)據(jù)處理線程所在的進程內(nèi)存占用增長異常明顯,確定了問題所在的進程,為后續(xù)的排查工作明確了方向。這就像是在一片森林中找到了冒煙的那片區(qū)域,知道了問題的大致范圍。
  2. 深入分析進程內(nèi)存使用:進入 /proc/[PID]/status 文件,查看該進程的內(nèi)存使用概況,發(fā)現(xiàn) VmRSS 和 VmSize 持續(xù)增長,這進一步驗證了內(nèi)存泄漏的可能性。接著,查看 /proc/[PID]/smaps 文件,詳細分析內(nèi)存映射區(qū)域,發(fā)現(xiàn)某個動態(tài)分配的內(nèi)存區(qū)域 Rss 不斷增加,且沒有釋放的跡象,初步確定了內(nèi)存泄漏的位置就在這個動態(tài)內(nèi)存分配的部分。這就像在鎖定的問題區(qū)域內(nèi),通過更細致的搜索,找到了具體的 “問題點”。
  3. 利用工具精確檢測:為了更準確地定位內(nèi)存泄漏的具體代碼行,使用 Valgrind 工具。在編譯程序時,加上 “-g” 選項生成調(diào)試信息,然后執(zhí)行 “valgrind --tool=memcheck --leak-check=full --show-reachable=yes./your-program” 命令。Valgrind 運行后,輸出了詳細的錯誤報告,指出了在數(shù)據(jù)處理線程中的某個函數(shù)里,存在內(nèi)存分配后未釋放的問題,就像用高精度的探測器,精準地找到了內(nèi)存泄漏的 “漏洞”。
  4. 分析多線程環(huán)境下的內(nèi)存問題:由于這是一個多線程程序,線程之間的同步和資源競爭也可能導致內(nèi)存問題。檢查線程同步機制,發(fā)現(xiàn)線程之間在訪問共享內(nèi)存時,沒有正確地使用互斥鎖,導致內(nèi)存訪問混亂,進一步加劇了內(nèi)存問題。這就像是在一個多人合作的項目中,發(fā)現(xiàn)了人員之間的協(xié)作流程出現(xiàn)了混亂,影響了整體的工作效果。
  5. 解決問題與驗證:根據(jù)排查結(jié)果,在數(shù)據(jù)處理線程的對應(yīng)函數(shù)中,添加正確的內(nèi)存釋放代碼,并確保線程同步機制的正確性,使用互斥鎖來保護共享內(nèi)存的訪問。修改代碼后,重新編譯并運行程序,再次使用 top、htop 以及 Valgrind 等工具進行檢查,發(fā)現(xiàn)內(nèi)存占用穩(wěn)定,沒有再出現(xiàn)泄漏的情況,系統(tǒng)性能也恢復正常,這表明問題已得到有效解決 。這就像是給出現(xiàn)故障的機器換上了新的零件,并進行了全面的測試,確保機器能夠正常運轉(zhuǎn)。

通過這個實戰(zhàn)演練,我們可以看到,在面對 Linux 內(nèi)存泄漏與高占用問題時,只要掌握了正確的排查方法和工具,按照合理的步驟進行分析,就能夠準確地找到問題的根源,并提出有效的解決方案,這也是在大疆嵌入式面試中,面試官希望看到的解決問題的能力和思路。

責任編輯:武曉燕 來源: 深度Linux
相關(guān)推薦

2025-08-07 09:42:15

2025-08-04 01:00:00

JavaScript內(nèi)存泄漏前端

2011-01-14 13:13:23

嵌入式Linux開發(fā)

2025-07-29 02:00:00

2010-01-07 10:45:38

嵌入式Linux入門

2009-12-09 10:12:28

嵌入式Linux

2009-12-16 15:41:40

嵌入式Linux入門

2009-12-17 10:33:05

嵌入式Linux

2021-02-26 13:35:46

JavaCPU內(nèi)存

2009-12-23 16:52:26

Linux自學嵌入式

2009-12-09 10:50:53

嵌入式Linux

2009-12-24 17:21:38

嵌入式Linux

2022-01-04 22:19:38

Linux開發(fā)嵌入式

2022-01-03 23:33:40

Linux組件系統(tǒng)

2010-09-25 15:05:49

LINUXJVM

2021-12-19 22:34:45

Linux容器系統(tǒng)

2009-05-14 09:28:16

嵌入式面試求職

2011-11-08 15:40:47

LwIP協(xié)議棧嵌入式

2017-12-21 10:43:44

Linux嵌入式終端

2011-07-05 15:59:18

Qt 嵌入式 linux
點贊
收藏

51CTO技術(shù)棧公眾號

一二三区免费视频| 女女调教被c哭捆绑喷水百合| 91caoporm在线视频| 老色鬼精品视频在线观看播放| 久久久www成人免费精品| 成年人小视频在线观看| 日本成人三级电影| 一区二区三区在线免费| 欧美精品七区| 国产高清在线观看视频| 嫩草成人www欧美| 成人97在线观看视频| 国产网站无遮挡| 成人短视频软件网站大全app| 精品久久久久久电影| 正在播放国产精品| 青青视频在线观| 国产精品一二三区| 国产精品成人在线| 欧美亚韩一区二区三区| 欧美黄色大片在线观看| 精品无人国产偷自产在线| 99精品视频国产| 欧美va在线观看| 亚洲成人在线观看视频| 亚洲制服欧美久久| 可以在线观看的av| jlzzjlzz亚洲日本少妇| 91精品在线观看视频| 亚洲 日本 欧美 中文幕| 伊人久久亚洲热| 久热爱精品视频线路一| 日本爱爱爱视频| 亚洲精品国产setv| 亚洲精品在线三区| 韩国三级与黑人| 91精品网站在线观看| 在线一区二区观看| 免费高清在线观看免费| 182在线播放| 亚洲影视资源网| 无码人妻精品一区二区蜜桃百度| 欧美69xxx| 国产精品麻豆99久久久久久| 日韩免费av电影| 精品电影在线| 久久人人超碰精品| 久久人人九九| 秋霞av在线| 久久久久99精品一区| 久久av二区| 五月婷婷六月丁香| 99久久精品免费| 国产伦精品一区| 人人妻人人玩人人澡人人爽| 成人一区二区三区| 国产精品夜夜夜一区二区三区尤| 国产三区在线播放| 国产一区二区免费在线| 成人性生交xxxxx网站| 国产乱码精品一区二三区蜜臂| 久久99精品国产| 国产日韩欧美在线看| 一级黄色大片免费| 激情久久五月天| 亚洲va久久久噜噜噜| 国产乱色精品成人免费视频| 国产一区二区视频在线| 亚洲精品免费在线视频| www.看毛片| 不卡av在线免费观看| 久久久精品国产一区二区三区| 日本激情视频网站| 91在线观看一区二区| 欧日韩一区二区三区| 在线免费看av| 亚洲日本在线视频观看| 日韩小视频网站| 水蜜桃在线视频| 欧美性猛交xxxxxx富婆| 日本55丰满熟妇厨房伦| 爱高潮www亚洲精品| 亚洲男人天堂古典| 一级二级黄色片| 欧美日韩第一区| 欧美中在线观看| 在线免费看毛片| 成人国产精品免费观看动漫| 欧美久久在线| av官网在线播放| 狠狠色香婷婷久久亚洲精品| 狠狠躁狠狠躁视频专区| 999久久精品| 国产一区二区三区视频| 亚洲伦理一区二区三区| 国产亚洲精品自拍| 成人av在线亚洲| 天天操天天干天天插| 日本一二三不卡| 黄色一级片黄色| 成人h在线观看| 亚洲国产美女精品久久久久∴| 舐め犯し波多野结衣在线观看| 亚洲字幕久久| 日本一区二区三区在线播放| 99久久精品国产色欲| 久久综合久久综合久久| 日本丰满大乳奶| 欧美momandson| 亚洲成人黄色网址| 人妻互换一区二区激情偷拍| 亚洲大黄网站| 亚洲综合视频1区| 超碰免费97在线观看| 亚洲成人第一页| 日本国产一级片| 久久av超碰| 欧美激情一级精品国产| 夜夜嗨av禁果av粉嫩avhd| 99久久99久久综合| a级黄色片免费| 久久久久黄色| 亚洲深夜福利网站| 91看片在线播放| 国产91丝袜在线观看| 亚洲 日韩 国产第一区| 欧美freesex黑人又粗又大| 日韩一区二区三区三四区视频在线观看| 日韩一级视频在线观看| 欧美性色综合| **亚洲第一综合导航网站| 自拍视频在线播放| 91久久精品一区二区二区| 国产美女视频免费观看下载软件| 中文无码久久精品| 成人黄色影片在线| 成人精品一区| 在线视频亚洲一区| 午夜理伦三级做爰电影| 国产日韩亚洲欧美精品| 国产乱码精品一区二区三区中文 | 日本熟妇毛耸耸xxxxxx| 国产一区二区三区免费在线观看 | 最新97超碰在线| 在线观看91视频| 超碰97av在线| 老司机一区二区| 亚洲精品9999| 国产情侣一区二区三区| 在线日韩av观看| 在线免费观看日韩视频| 18成人在线观看| 欧美性受xxxxxx黑人xyx性爽| 欧美激情777| 91在线观看免费观看| 怡红院红怡院欧美aⅴ怡春院| 日韩限制级电影在线观看| 久久久久久久久艹| av中文字幕不卡| 久久网站免费视频| 国产日产精品_国产精品毛片| 国产成人精品在线播放| 91在线视频免费看| 日韩一区二区三区视频在线观看| 精品97人妻无码中文永久在线| 粉嫩av一区二区三区在线播放| 亚洲熟妇无码另类久久久| 亚洲人成精品久久久| 国产精品视频最多的网站| 麻豆视频在线免费观看| 日韩美女一区二区三区四区| www.国产高清| 欧美国产一区二区在线观看 | 精品中文字幕一区二区三区| 欧美激情视频在线| 日韩porn| 欧美一区二区免费| 午夜毛片在线观看| 国产精品第一页第二页第三页| 九色91porny| 国产亚洲精品bv在线观看| 亚洲高清不卡一区| 综合欧美亚洲| 国产不卡视频在线| 国产淫片在线观看| 日韩激情视频在线播放| 91丨porny丨在线中文| 亚洲图片欧美视频| 青娱乐国产视频| 国产91精品免费| av视屏在线播放| 欧美久久99| 日韩电影免费观看高清完整| 日本免费一区二区视频| 欧美在线视频在线播放完整版免费观看| 最新电影电视剧在线观看免费观看 | 国产精品国产三级国产专区51| 日本三级久久| 亚洲aⅴ男人的天堂在线观看| 麻豆mv在线看| 操人视频在线观看欧美| 欧美日韩国产亚洲沙发| 日韩亚洲欧美高清| 中文字幕av久久爽| 精品久久久久久电影| 少妇被躁爽到高潮无码文| 久久久久久影视| 亚洲一区和二区| 久久国产福利国产秒拍| 91黄色小网站| 精品二区久久| 色哺乳xxxxhd奶水米仓惠香| 九一亚洲精品| 精品欧美日韩在线| 日韩区欧美区| 国产欧亚日韩视频| 偷拍中文亚洲欧美动漫| 午夜精品一区二区三区在线| 成人午夜在线影视| 亚洲一区二区黄| 天天操天天干天天舔| 欧美videofree性高清杂交| 在线观看免费观看在线| 欧美日韩亚洲天堂| 日本a在线观看| 亚洲自拍欧美精品| 亚洲少妇xxx| 国产精品私人影院| 中文字幕 自拍| www.视频一区| 四虎精品一区二区| 国产不卡视频一区二区三区| 手机免费看av网站| 麻豆精品久久精品色综合| 免费观看成人在线视频| 久久成人一区| 成人黄色片视频| 国产精品一页| 日日鲁鲁鲁夜夜爽爽狠狠视频97 | 丰满人妻一区二区三区免费| 欧美军同video69gay| 中文天堂在线播放| 欧美无人高清视频在线观看| 在线观看国产区| 欧美性色黄大片手机版| 在线观看色网站| 欧美精品三级日韩久久| 国产又粗又黄又爽| 欧美一区二区三区白人| 性一交一乱一乱一视频| 欧美成人三级在线| 成人精品在线播放| 亚洲国产精品高清久久久| 天堂8在线视频| 亚洲女人被黑人巨大进入al| 国模吧精品人体gogo| 中文字幕日韩欧美在线| 欧美人xxx| 欧美精品在线极品| 国产丝袜精品丝袜| 国内久久久精品| 欧美momandson| 国产精品一区二区三区久久| 伊人久久大香线蕉综合影院首页| 亚洲已满18点击进入在线看片| 99久久免费精品国产72精品九九 | 懂色aⅴ精品一区二区三区蜜月| 日韩不卡视频在线| 欧美网站一区二区| 国产成人精品av在线观| 亚洲国产小视频| 高清国产福利在线观看| 久久精品国产一区| a毛片不卡免费看片| 日本在线精品视频| 成人噜噜噜噜| 久久国产主播精品| 日韩欧美精品一区| 日韩一区二区高清视频| 久久福利影视| 爽爽爽在线观看| 99国产精品久| 呻吟揉丰满对白91乃国产区| 夜夜嗨av一区二区三区中文字幕| 天天干天天干天天操| 欧美日韩大陆一区二区| 婷婷视频在线观看| 日韩资源在线观看| 7777kkk亚洲综合欧美网站| 国产精品入口免费视| 97精品久久| 日韩欧美一区二区在线观看 | 国产精品熟女一区二区不卡| 91麻豆免费看片| 91视频综合网| 在线中文字幕一区二区| 午夜精品久久久久久久第一页按摩| 精品亚洲一区二区三区在线播放| 黄视频网站在线| 日韩免费精品视频| 超碰精品在线观看| 国产成年人在线观看| 另类激情亚洲| 三上悠亚 电影| 中文字幕视频一区二区三区久| 成年人午夜视频| 日韩欧美国产麻豆| av在线第一页| 51午夜精品视频| 亚洲综合色婷婷在线观看| 亚洲欧美久久久久一区二区三区| 91久久综合| 91丨porny丨九色| 国产精品乱码人人做人人爱| 天天干天天干天天| 精品久久久久久久久久久久久久久久久 | 午夜肉伦伦影院| 国产.精品.日韩.另类.中文.在线.播放| 精品国产成人亚洲午夜福利| 五月天久久比比资源色| 精品人妻一区二区三区三区四区| 中文字幕亚洲第一| 日本电影欧美片| 欧美日韩一区二区三区在线观看免| 亚洲午夜91| 一级黄色大片免费看| 亚洲欧洲无码一区二区三区| 一本色道久久综合亚洲| 中文字幕欧美国内| 欧美日韩五码| 欧美在线一二三区| 久久都是精品| 欧洲美一区二区三区亚洲 | 一级黄色片在线播放| 尤物九九久久国产精品的特点 | 在线亚洲国产精品网站| jjzz黄色片| 亚洲国产色一区| 亚洲国产精品二区| 久久久久国产精品免费| 日本亚洲视频| 日韩欧美精品免费| jlzzjlzz国产精品久久| 日韩欧美高清在线观看| 亚洲国产精品福利| 亚洲美女炮图| 日本一区美女| 蜜臀av性久久久久蜜臀av麻豆| 免费成人深夜天涯网站| 欧美中文字幕一区| 日韩在线资源| 亚洲直播在线一区| 欧美日韩爆操| 欧美精品欧美极品欧美激情| 欧美日韩美女在线观看| 免费在线国产| 国产精品福利网| 久久影院100000精品| 少妇愉情理伦片bd| 午夜日韩在线观看| 青青草在线视频免费观看| 国产精品成人v| **女人18毛片一区二区| 中文字幕在线播放一区二区| 亚洲午夜精品在线| 全色精品综合影院| 国产热re99久久6国产精品| 欧美国产91| 久久精品综合视频| 欧美三级三级三级| 在线播放免费av| 精品综合在线| 久久99精品国产.久久久久久| 久草视频在线资源| 亚洲欧洲激情在线| 国产精品亚洲一区二区在线观看| 精品丰满人妻无套内射| 久久久久久久久蜜桃| 国产人妖在线播放| 97视频在线观看免费高清完整版在线观看 | 日韩精品电影在线观看| 我要看黄色一级片| 亚洲精品电影在线| 国产亚洲人成a在线v网站| 97碰在线视频| 国产欧美一区二区精品性色 | 福利一区二区免费视频| 欧美 亚洲 视频| 久久久精品综合| www.精品久久| 国产精品视频一| 99在线热播精品免费99热| 1024手机在线观看你懂的| 精品1区2区在线观看| 六九午夜精品视频| 怡红院av亚洲一区二区三区h| 中文字幕亚洲电影| 日本国产在线| 成人做爰66片免费看网站| 日本不卡视频在线观看|