日本黄色一级经典视频|伊人久久精品视频|亚洲黄色色周成人视频九九九|av免费网址黄色小短片|黄色Av无码亚洲成年人|亚洲1区2区3区无码|真人黄片免费观看|无码一级小说欧美日免费三级|日韩中文字幕91在线看|精品久久久无码中文字幕边打电话

當(dāng)前位置:首頁 > 嵌入式 > 嵌入式軟件
[導(dǎo)讀]Linux 的虛擬內(nèi)存管理有幾個(gè)關(guān)鍵概念: 每個(gè)進(jìn)程有獨(dú)立的虛擬地址空間,進(jìn)程訪問的虛擬地址并不是真正的物理地址 虛擬地址可通過每個(gè)進(jìn)程上頁表與物理地址進(jìn)行映射,獲得真正物理地址 如果虛擬地址對應(yīng)物理地址不在物理內(nèi)存中,則產(chǎn)生缺頁中斷,真正分配物理地址,同時(shí)更新進(jìn)程的頁表;如果此時(shí)物理內(nèi)存已耗盡,則根據(jù)內(nèi)存替換算法淘汰部分頁面至物理磁盤中。

Linux 的虛擬內(nèi)存管理有幾個(gè)關(guān)鍵概念:

每個(gè)進(jìn)程有獨(dú)立的虛擬地址空間,進(jìn)程訪問的虛擬地址并不是真正的物理地址

虛擬地址可通過每個(gè)進(jìn)程上頁表與物理地址進(jìn)行映射,獲得真正物理地址

如果虛擬地址對應(yīng)物理地址不在物理內(nèi)存中,則產(chǎn)生缺頁中斷,真正分配物理地址,同時(shí)更新進(jìn)程的頁表;如果此時(shí)物理內(nèi)存已耗盡,則根據(jù)內(nèi)存替換算法淘汰部分頁面至物理磁盤中。

基于以上認(rèn)識,這篇文章通過本人以前對虛擬內(nèi)存管理的疑惑由淺入深整理了以下十個(gè)問題,并通過例子和系統(tǒng)命令嘗試進(jìn)行解答。

Linux 虛擬地址空間如何分布? 32 位和 64 位有何不同?

malloc 是如何分配內(nèi)存的?

malloc 分配多大的內(nèi)存,就占用多大的物理內(nèi)存空間嗎?

如何查看進(jìn)程虛擬地址空間的使用情況?

free 的內(nèi)存真的釋放了嗎(還給 OS ) ?

程序代碼中 malloc 的內(nèi)存都有相應(yīng)的 free ,就不會(huì)出現(xiàn)內(nèi)存泄露了嗎?

既然堆內(nèi)內(nèi)存不能直接釋放,為什么不全部使用 mmap 來分配?

如何查看進(jìn)程的缺頁中斷信息?

如何查看堆內(nèi)內(nèi)存的碎片情況?

除了 glibc 的 malloc/free ,還有其他第三方實(shí)現(xiàn)嗎?

一.Linux 虛擬地址空間如何分布? 32 位和 64 位有何不同?

Linux 使用虛擬地址空間,大大增加了進(jìn)程的尋址空間,由低地址到高地址分別為:

只讀段:該部分空間只能讀,不可寫,包括代碼段、 rodata 段( C 常量字符串和 #define 定義的常量)

數(shù)據(jù)段:保存全局變量、靜態(tài)變量的空間

堆 :就是平時(shí)所說的動(dòng)態(tài)內(nèi)存, malloc/new 大部分都來源于此。其中堆頂?shù)奈恢每赏ㄟ^函數(shù) brk 和 sbrk 進(jìn)行動(dòng)態(tài)調(diào)整。

文件映射區(qū)域 :如動(dòng)態(tài)庫、共享內(nèi)存等映射物理空間的內(nèi)存,一般是 mmap 函數(shù)所分配的虛擬地址空間。

棧:用于維護(hù)函數(shù)調(diào)用的上下文空間,一般為 8M ,可通過 ulimit –s 查看。

內(nèi)核虛擬空間:用戶代碼不可見的內(nèi)存區(qū)域,由內(nèi)核管理。

下圖是 32 位系統(tǒng)典型的虛擬地址空間分布(來自《深入理解計(jì)算機(jī)系統(tǒng)》)。

 

 

32 位系統(tǒng)有 4G 的地址空間,其中0x08048000~0xbfffffff 是用戶空間,0xc0000000~0xffffffff 是內(nèi)核空間,包括內(nèi)核代碼和數(shù)據(jù)、與進(jìn)程相關(guān)的數(shù)據(jù)結(jié)構(gòu)(如頁表、內(nèi)核棧)等。另外, %esp 執(zhí)行棧頂,往低地址方向變化; brk/sbrk 函數(shù)控制堆頂往高地址方向變化。

可通過以下代碼驗(yàn)證進(jìn)程的地址空間分布,其中 sbrk(0) 函數(shù)用于返回棧頂指針。

#include

#include

#include

#include

int global_num = 0;

char global_str_arr [65536] = {‘a(chǎn)‘};

int main(int argc, char** argv)

{

char* heap_var = NULL;

int local_var = 0;

printf("Address of function main 0x%lxn", main);

printf("Address of global_num 0x%lxn", &global_num);

printf("Address of global_str_arr 0x%lx ~ 0x%lxn", &global_str_arr[0], &global_str_arr[65535]);

printf("Top of stack is 0x%lxn", &local_var);

printf("Top of heap is 0x%lxn", sbrk(0));

heap_var = malloc(sizeof(char) * 127 * 1024);

printf("Address of heap_var is 0x%lxn", heap_var);

printf("Top of heap after malloc is 0x%lxn", sbrk(0));

free(heap_var);

heap_var = NULL;

printf("Top of heap after free is 0x%lxn", sbrk(0));

return 1;

}

32 位系統(tǒng)的結(jié)果如下,與上圖的劃分保持一致,并且棧頂指針在 mallloc 和 free 一個(gè) 127K 的存儲(chǔ)空間時(shí)都發(fā)生了變化(增大和縮小)。

Address of function main 0x8048474

Address of global_num 0x8059904

Address of global_str_arr 0x8049900 ~ 0x80598ff

Top of stack is 0xbfd0886c

Top of heap is 0x805a000

Address of heap_var is 0x805a008

Top of heap after malloc is 0x809a000

Top of heap after free is 0x807b000

但是, 64 位系統(tǒng)結(jié)果怎樣呢? 64 位系統(tǒng)是否擁有 2^64 的地址空間嗎?

64 位系統(tǒng)運(yùn)行結(jié)果如下:

Address of function main 0x400594

Address of global_num 0x610b90

Address of global_str_arr 0x600b80 ~ 0x610b7f

Top of stack is 0x7fff2e9e4994

Top of heap is 0x8f5000

Address of heap_var is 0x8f5010

Top of heap after malloc is 0x935000

Top of heap after free is 0x916000

從結(jié)果知,與上圖的分布并不一致。而事實(shí)上, 64 位系統(tǒng)的虛擬地址空間劃分發(fā)生了改變:

地址空間大小不是 2^32 ,也不是 2^64 ,而一般是 2^48 。因?yàn)椴⒉恍枰?2^64 這么大的尋址空間,過大空間只會(huì)導(dǎo)致資源的浪費(fèi)。 64 位 Linux 一般使用 48 位來表示虛擬地址空間, 40 位表示物理地址,這可通過 /proc/cpuinfo 來查看

address sizes : 40 bits physical, 48 bits virtual

其中, 0x0000000000000000~0x00007fffffffffff表示用戶空間,0xFFFF800000000000~ 0xFFFFFFFFFFFFFFFF表示內(nèi)核空間,共提供 256TB(2^48) 的尋址空間。這兩個(gè)區(qū)間的特點(diǎn)是,第 47 位與 48~63 位相同,若這些位為 0 表示用戶空間,否則表示內(nèi)核空間。

用戶空間由低地址到高地址仍然是只讀段、數(shù)據(jù)段、堆、文件映射區(qū)域和棧

二.malloc 是如何分配內(nèi)存的?

malloc 是 glibc 中內(nèi)存分配函數(shù),也是最常用的動(dòng)態(tài)內(nèi)存分配函數(shù),其內(nèi)存必須通過 free 進(jìn)行釋放,否則導(dǎo)致內(nèi)存泄露。

關(guān)于 malloc 獲得虛存空間的實(shí)現(xiàn),與 glibc 的版本有關(guān),但大體邏輯是:

若分配內(nèi)存小于 128k ,調(diào)用 sbrk() ,將堆頂指針向高地址移動(dòng),獲得新的虛存空間。

若分配內(nèi)存大于 128k ,調(diào)用 mmap() ,在文件映射區(qū)域中分配匿名虛存空間。

這里討論的是簡單情況,如果涉及并發(fā)可能會(huì)復(fù)雜一些,不過先不討論。

其中 sbrk 就是修改棧頂指針位置,而 mmap 可用于生成文件的映射以及匿名頁面的內(nèi)存,這里指的是匿名頁面。

而這個(gè) 128k ,是 glibc 的默認(rèn)配置,可通過函數(shù) mallopt 來設(shè)置,可通過以下例子說明。

#include

#include

#include

#include

#include

#include

void print_info(

char* var_name,

char* var_ptr,

size_t size_in_kb

)

{

printf("Address of %s(%luk) 0x%lx, now heap top is 0x%lxn",

var_name, size_in_kb, var_ptr, sbrk(0));

}

int main(int argc, char** argv)

{

char *heap_var1, *heap_var2, *heap_var3 ;

char *mmap_var1, *mmap_var2, *mmap_var3 ;

char *maybe_mmap_var;

printf("Orginal heap top is 0x%lxn", sbrk(0));

heap_var1 = malloc(32*1024);

print_info("heap_var1", heap_var1, 32);

heap_var2 = malloc(64*1024);

print_info("heap_var2", heap_var2, 64);

heap_var3 = malloc(127*1024);

print_info("heap_var3", heap_var3, 127);

printf("n");

maybe_mmap_var = malloc(128*1024);

print_info("maybe_mmap_var", maybe_mmap_var, 128);

//mmap

mmap_var1 = malloc(128*1024);

print_info("mmap_var1", mmap_var1, 128);

// set M_MMAP_THRESHOLD to 64k

mallopt(M_MMAP_THRESHOLD, 64*1024);

printf("set M_MMAP_THRESHOLD to 64kn");

mmap_var2 = malloc(64*1024);

print_info("mmap_var2", mmap_var2, 64);

mmap_var3 = malloc(127*1024);

print_info("mmap_var3", mmap_var3, 127);

return 1;

}

這個(gè)例子很簡單,通過 malloc 申請多個(gè)不同大小的動(dòng)態(tài)內(nèi)存,同時(shí)通過接口 print_info 打印變量大小和地址等相關(guān)信息,其中 sbrk(0) 可返回堆頂指針位置。另外,粗體部分是將 MMAP 分配的臨界點(diǎn)由 128k 轉(zhuǎn)為 64k ,再打印變量地址的不同。

下面是 Linux 64 位機(jī)器的執(zhí)行結(jié)果(后文所有例子都是通過 64 位機(jī)器上的測試結(jié)果)。

Orginal heap top is 0x17da000

Address of heap_var1(32k) 0x17da010, now heap top is 0x1803000

Address of heap_var2(64k) 0x17e2020, now heap top is 0x1803000

Address of heap_var3(127k) 0x17f2030, now heap top is 0x1832000

Address of maybe_mmap_var(128k) 0x1811c40, now heap top is 0x1832000

Address of mmap_var1(128k) 0x7f4a0b1f2010, now heap top is 0x1832000

set M_MMAP_THRESHOLD to 64k

Address of mmap_var2(64k) 0x7f4a0b1e1010, now heap top is 0x1832000

Address of mmap_var3(127k) 0x7f4a0b1c1010, now heap top is 0x1832000

三.malloc 分配多大的內(nèi)存,就占用多大的物理內(nèi)存空間嗎?

我們知道, malloc 分配的的內(nèi)存是虛擬地址空間,而虛擬地址空間和物理地址空間使用進(jìn)程頁表進(jìn)行映射,那么分配了空間就是占用物理內(nèi)存空間了嗎?

首先,進(jìn)程使用多少內(nèi)存可通過 ps aux 命令 查看,其中關(guān)鍵的兩信息(第五、六列)為:

VSZ , virtual memory size ,表示進(jìn)程總共使用的虛擬地址空間大小,包括進(jìn)程地址空間的代碼段、數(shù)據(jù)段、堆、文件映射區(qū)域、棧、內(nèi)核空間等所有虛擬地址使用的總和,單位是 K

RSS , resident set size ,表示進(jìn)程實(shí)際使用的物理內(nèi)存空間, RSS 總小于 VSZ 。

可通過一個(gè)例子說明這個(gè)問題:

#include

#include

#include

#include

#include

#include

char ps_cmd[1024];

void print_info(

char* var_name,

char* var_ptr,

size_t size_in_kb

)

{

printf("Address of %s(%luk) 0x%lx, now heap top is 0x%lxn",

var_name, size_in_kb, var_ptr, sbrk(0));

system(ps_cmd);

}

int main(int argc, char** argv)

{

char *non_set_var, *set_1k_var, *set_5k_var, *set_7k_var;

pid_t pid;

pid = getpid();

sprintf(ps_cmd, "ps aux | grep %lu | grep -v grep", pid);

non_set_var = malloc(32*1024);

print_info("non_set_var", non_set_var, 32);

set_1k_var = malloc(64*1024);

memset(set_1k_var, 0, 1024);

print_info("set_1k_var", set_1k_var, 64);

set_5k_var = malloc(127*1024);

memset(set_5k_var, 0, 5*1024);

print_info("set_5k_var", set_5k_var, 127);

set_7k_var = malloc(64*1024);

memset(set_1k_var, 0, 7*1024);

print_info("set_7k_var", set_7k_var, 64);

return 1;

}

該代碼擴(kuò)展了上一個(gè)例子print_info能力,處理打印變量信息,同時(shí)通過 ps aux 命令獲得當(dāng)前進(jìn)程的 VSZ 和 RSS 值。并且程序 malloc 一塊內(nèi)存后,會(huì) memset 內(nèi)存的若干 k 內(nèi)容。

執(zhí)行結(jié)果為

Address of non_set_var(32k) 0x502010, now heap top is 0x52b000

mysql 12183 0.0 0.0 2692 452 pts/3 S+ 20:29 0:00 ./test_vsz

Address of set_1k_var(64k) 0x50a020, now heap top is 0x52b000

mysql 12183 0.0 0.0 2692 456 pts/3 S+ 20:29 0:00 ./test_vsz

Address of set_5k_var(127k) 0x51a030, now heap top is 0x55a000

mysql 12183 0.0 0.0 2880 464 pts/3 S+ 20:29 0:00 ./test_vsz

Address of set_7k_var(64k) 0x539c40, now heap top is 0x55a000

mysql 12183 0.0 0.0 2880 472 pts/3 S+ 20:29 0:00 ./test_vsz

由以上結(jié)果知:

VSZ 并不是每次 malloc 后都增長,是與上一節(jié)說的堆頂沒發(fā)生變化有關(guān),因?yàn)榭芍赜枚秧攦?nèi)剩余的空間,這樣的 malloc 是很輕量快速的。

但如果 VSZ 發(fā)生變化,基本與分配內(nèi)存量相當(dāng),因?yàn)?VSZ 是計(jì)算虛擬地址空間總大小。

RSS 的增量很少,是因?yàn)?malloc 分配的內(nèi)存并不就馬上分配實(shí)際存儲(chǔ)空間,只有第一次使用,如第一次 memset 后才會(huì)分配。

由于每個(gè)物理內(nèi)存頁面大小是 4k ,不管 memset 其中的 1k 還是 5k 、 7k ,實(shí)際占用物理內(nèi)存總是 4k 的倍數(shù)。所以 RSS 的增量總是 4k 的倍數(shù)。

因此,不是 malloc 后就馬上占用實(shí)際內(nèi)存,而是第一次使用時(shí)發(fā)現(xiàn)虛存對應(yīng)的物理頁面未分配,產(chǎn)生缺頁中斷,才真正分配物理頁面,同時(shí)更新進(jìn)程頁面的映射關(guān)系。這也是 Linux 虛擬內(nèi)存管理的核心概念之一。

四. 如何查看進(jìn)程虛擬地址空間的使用情況?

進(jìn)程地址空間被分為了代碼段、數(shù)據(jù)段、堆、文件映射區(qū)域、棧等區(qū)域,那怎么查詢這些虛擬地址空間的使用情況呢?

Linux 提供了 pmap 命令來查看這些信息,通常使用 pmap -d $pid (高版本可提供 pmap -x $pid)查詢,如下所示:

mysql@ TLOG_590_591:~/vin/test_memory> pmap -d 17867

17867: test_mmap

START SIZE RSS DIRTY PERM OFFSET DEVICE MAPPING

00400000 8K 4K 0K r-xp 00000000 08:01 /home/mysql/vin/test_memory/test_mmap

00501000 68K 8K 8K rw-p 00001000 08:01 /home/mysql/vin/test_memory/test_mmap

00512000 76K 0K 0K rw-p 00512000 00:00 [heap]

0053e000 256K 0K 0K rw-p 0053e000 00:00 [anon]

2b3428f97000 108K 92K 0K r-xp 00000000 08:01 /lib64/ld-2.4.so

2b3428fb2000 8K 8K 8K rw-p 2b3428fb2000 00:00 [anon]

2b3428fc1000 4K 4K 4K rw-p 2b3428fc1000 00:00 [anon]

2b34290b1000 8K 8K 8K rw-p 0001a000 08:01 /lib64/ld-2.4.so

2b34290b3000 1240K 248K 0K r-xp 00000000 08:01 /lib64/libc-2.4.so

2b34291e9000 1024K 0K 0K ---p 00136000 08:01 /lib64/libc-2.4.so

2b34292e9000 12K 12K 12K r--p 00136000 08:01 /lib64/libc-2.4.so

2b34292ec000 8K 8K 8K rw-p 00139000 08:01 /lib64/libc-2.4.so

2b34292ee000 1048K 36K 36K rw-p 2b34292ee000 00:00 [anon]

7fff81afe000 84K 12K 12K rw-p 7fff81afe000 00:00 [stack]

ffffffffff600000 8192K 0K 0K ---p 00000000 00:00 [vdso]

Total: 12144K 440K 96K

從這個(gè)結(jié)果可以看到進(jìn)程虛擬地址空間的使用情況,包括起始地址、大小、實(shí)際使用內(nèi)存、臟頁大小、權(quán)限、偏移、設(shè)備和映射文件等。 pmap 命令就是基于下面兩文件內(nèi)容進(jìn)行解析的:

/proc/$pid/maps

/proc/$pid/smaps

并且對于上述每個(gè)內(nèi)存塊區(qū)間,內(nèi)核會(huì)使用一個(gè) vm_area_struct 結(jié)構(gòu)來維護(hù),同時(shí)通過頁面建立與物理內(nèi)存的映射關(guān)系,如下圖所示。

 

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時(shí)聯(lián)系本站刪除( 郵箱:macysun@21ic.com )。
換一批
延伸閱讀

作為Altium加入瑞薩電子后深化中國市場投入的重要戰(zhàn)略舉措,Altium Develop平臺(tái)以“植根中國,服務(wù)中國”為核心理念,致力于打破電子設(shè)計(jì)、供應(yīng)鏈與制造環(huán)節(jié)的信息壁壘,為中國工程師與各類企業(yè)提供高效、開放的協(xié)同...

關(guān)鍵字: 軟件開發(fā)

在Zynq MPSoC開發(fā)中,實(shí)現(xiàn)PS端Linux與PL端自定義IP核的AXI互聯(lián)是構(gòu)建高性能異構(gòu)系統(tǒng)的關(guān)鍵環(huán)節(jié)。這種互聯(lián)方式充分發(fā)揮了ARM處理器的軟件優(yōu)勢與FPGA的硬件加速能力,為復(fù)雜應(yīng)用提供了強(qiáng)大的計(jì)算平臺(tái)。

關(guān)鍵字: Zynq MPSoC Linux

在物聯(lián)網(wǎng)與智能設(shè)備飛速普及的當(dāng)下,嵌入式系統(tǒng)的安全性與穩(wěn)定性愈發(fā)關(guān)鍵。實(shí)時(shí)操作系統(tǒng)(RTOS)憑借其高確定性、低延遲的特性,成為工業(yè)控制、醫(yī)療設(shè)備、航空電子等安全敏感領(lǐng)域的核心支撐。而內(nèi)存保護(hù)單元(MPU)作為硬件級安全...

關(guān)鍵字: Linux Windows

深圳2026年3月19日 /美通社/ -- 2026年的招聘市場,正陷入一場奇特的"算法互博":求職者用AI美化簡歷以通過篩選,企業(yè)用AI深挖細(xì)節(jié)以識別真?zhèn)?。這場博弈的背后,是簡歷日益"豐滿...

關(guān)鍵字: AI 代碼 LAB 模型

3月10日消息,2026年開年,一個(gè)名為OpenClaw的開源項(xiàng)目以閃電般的速度席卷了GitHub。它在短短一天內(nèi)就斬獲了9000顆星

關(guān)鍵字: OpenClaw Linux

3月6日消息,在摩根士丹利會(huì)議上,NVIDIA CEO黃仁勛分享了關(guān)于Agentic AI(代理式人工智能)轉(zhuǎn)折點(diǎn)的見解,并將開源軟件OpenClaw評價(jià)為“當(dāng)代最重磅的軟件發(fā)布”。

關(guān)鍵字: OpenClaw Linux

Linux內(nèi)存管理是操作系統(tǒng)的核心機(jī)制之一,通過虛擬內(nèi)存與物理內(nèi)存的分離設(shè)計(jì),實(shí)現(xiàn)了多進(jìn)程內(nèi)存隔離、高效資源利用和系統(tǒng)穩(wěn)定性保障。

關(guān)鍵字: Linux 內(nèi)存

在Linux系統(tǒng)中,進(jìn)程管理是內(nèi)核的核心功能之一,其核心目標(biāo)是通過高效的調(diào)度機(jī)制和進(jìn)程切換技術(shù),實(shí)現(xiàn)多任務(wù)并發(fā)執(zhí)行。

關(guān)鍵字: Linux CPU

內(nèi)核是操作系統(tǒng)的核心,它作為應(yīng)用程序與硬件設(shè)備之間的"中間人",負(fù)責(zé)進(jìn)程調(diào)度、內(nèi)存管理、硬件通信和系統(tǒng)調(diào)用等關(guān)鍵功能。Linux和Windows作為全球使用最廣泛的兩大操作系統(tǒng),其內(nèi)核設(shè)計(jì)理念、架構(gòu)和運(yùn)行機(jī)制存在本質(zhì)差異...

關(guān)鍵字: Linux Windows

在Linux系統(tǒng)中,當(dāng)開發(fā)者使用mmap()系統(tǒng)調(diào)用將磁盤文件映射到進(jìn)程的虛擬地址空間時(shí),一個(gè)看似簡單的指針操作背后,隱藏著操作系統(tǒng)內(nèi)核與硬件協(xié)同工作的復(fù)雜機(jī)制。這種機(jī)制不僅突破了傳統(tǒng)文件IO的效率瓶頸,更重新定義了內(nèi)存...

關(guān)鍵字: Linux 文件IO 內(nèi)存映射
關(guān)閉