Linux02进程内存管理
1.進(jìn)程地址空間
? ?1.1程序的結(jié)構(gòu)與進(jìn)程的結(jié)構(gòu)
? ? ? ?[root@localhost demo]# size test
? text ? data ? ?bss ? ?dec ? ?hex filename
? ? 1193 ? ?492 ? ? 16 ? 1701 ? ?6a5 test
? ? ? ? 一個(gè)可執(zhí)行程序包含三個(gè)部分:
? ? ? ? 代碼段:主要存放指令,操作以及只讀的常量數(shù)據(jù)例如字符串常量。
? ? ? ? 數(shù)據(jù)段:全局或者靜態(tài)的已經(jīng)初始化的變量
? ? ? ? BSS段:全局或者靜態(tài)的未初始化的變量
? ? ? ? 執(zhí)行一個(gè)程序要運(yùn)行,需要將程序加載到內(nèi)存(磁盤有專門的讀寫驅(qū)動(dòng),但是速度比較慢,內(nèi)存可有cpu直接訪問讀寫,比磁盤速度要快)
? ?
| 名稱 | 速度 |
| CPU寄存器 | 1ns |
| cache | 1ns~10ns |
| DRAM | 100ns |
| 磁盤 | 10ss |
? ? ? ? ? ? ? ? 其資源分為內(nèi)核資源和用戶資源。內(nèi)核資源是PCB---進(jìn)程控制塊,也就是一個(gè)結(jié)構(gòu)體,負(fù)責(zé)管理進(jìn)程所有資源。
? ? ? ? ? ? ? ? mm_struct指向該進(jìn)程相關(guān)的內(nèi)存資源:從低到高地址分為:代碼段->數(shù)據(jù)段->BSS段->堆段->棧段
? ? ? ? ? ? ? ? 在執(zhí)行程序時(shí),系統(tǒng)首先在內(nèi)核空間創(chuàng)建一個(gè)進(jìn)程,為這個(gè)進(jìn)程申請(qǐng)一個(gè)進(jìn)程控制塊PCB(task_struct),用于管理整個(gè)進(jìn)城的所有資源。
? ?? 代碼段,數(shù)據(jù)段,BSS段,直接從磁盤拷貝到當(dāng)前內(nèi)存空間,大小相等。
? ?? 動(dòng)態(tài)的空間:堆和棧空間,以及mmap段(主要是映射其他庫的相關(guān)信息)
? ? ? ? ? ? ? ? 命令:pmap 進(jìn)程號(hào)或者cat /proc/進(jìn)程號(hào)/maps
? ? ? ? ? ? ? ? ? ? ? ?[root@localhost proc]# cat 31281/maps
00400000-004d4000 r-xp 00000000 08:02 390951 ? ? ? ? ? ? ? ? ? ? ? ? ? ? /bin/bash
006d3000-006dd000 rw-p 000d3000 08:02 390951 ? ? ? ? ? ? ? ? ? ? ? ? ? ? /bin/bash
006dd000-006e2000 rw-p 00000000 00:00 0?
008dc000-008e5000 rw-p 000dc000 08:02 390951 ? ? ? ? ? ? ? ? ? ? ? ? ? ? /bin/bash
00b45000-00ba8000 rw-p 00000000 00:00 0 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?[heap]
32c0c00000-32c0c20000 r-xp 00000000 08:02 781828 ? ? ? ? ? ? ? ? ? ? ? ? /lib64/ld-2.12.so
32c0e1f000-32c0e20000 r--p 0001f000 08:02 781828 ? ? ? ? ? ? ? ? ? ? ? ? /lib64/ld-2.12.so
32c0e20000-32c0e21000 rw-p 00020000 08:02 781828 ? ? ? ? ? ? ? ? ? ? ? ? /lib64/ld-2.12.so
32c0e21000-32c0e22000 rw-p 00000000 00:00 0?
32c1000000-32c1002000 r-xp 00000000 08:02 782740 ? ? ? ? ? ? ? ? ? ? ? ? /lib64/libdl-2.12.so
32c1002000-32c1202000 ---p 00002000 08:02 782740 ? ? ? ? ? ? ? ? ? ? ? ? /lib64/libdl-2.12.so
32c1202000-32c1203000 r--p 00002000 08:02 782740 ? ? ? ? ? ? ? ? ? ? ? ? /lib64/libdl-2.12.so
32c1203000-32c1204000 rw-p 00003000 08:02 782740 ? ? ? ? ? ? ? ? ? ? ? ? /lib64/libdl-2.12.so
32c1400000-32c158b000 r-xp 00000000 08:02 782725 ? ? ? ? ? ? ? ? ? ? ? ? /lib64/libc-2.12.so
32c158b000-32c178a000 ---p 0018b000 08:02 782725 ? ? ? ? ? ? ? ? ? ? ? ? /lib64/libc-2.12.so
32c178a000-32c178e000 r--p 0018a000 08:02 782725 ? ? ? ? ? ? ? ? ? ? ? ? /lib64/libc-2.12.so
32c178e000-32c178f000 rw-p 0018e000 08:02 782725 ? ? ? ? ? ? ? ? ? ? ? ? /lib64/libc-2.12.so
32c178f000-32c1794000 rw-p 00000000 00:00 0?
3e6a000000-3e6a01d000 r-xp 00000000 08:02 781958 ? ? ? ? ? ? ? ? ? ? ? ? /lib64/libtinfo.so.5.7
3e6a01d000-3e6a21c000 ---p 0001d000 08:02 781958 ? ? ? ? ? ? ? ? ? ? ? ? /lib64/libtinfo.so.5.7
3e6a21c000-3e6a220000 rw-p 0001c000 08:02 781958 ? ? ? ? ? ? ? ? ? ? ? ? /lib64/libtinfo.so.5.7
3e6a220000-3e6a221000 rw-p 00000000 00:00 0?
7f2a4f326000-7f2a551b7000 r--p 00000000 08:02 914111 ? ? ? ? ? ? ? ? ? ? /usr/lib/locale/locale-archive
7f2a551b7000-7f2a551c3000 r-xp 00000000 08:02 781857 ? ? ? ? ? ? ? ? ? ? /lib64/libnss_files-2.12.so
7f2a551c3000-7f2a553c3000 ---p 0000c000 08:02 781857 ? ? ? ? ? ? ? ? ? ? /lib64/libnss_files-2.12.so
7f2a553c3000-7f2a553c4000 r--p 0000c000 08:02 781857 ? ? ? ? ? ? ? ? ? ? /lib64/libnss_files-2.12.so
7f2a553c4000-7f2a553c5000 rw-p 0000d000 08:02 781857 ? ? ? ? ? ? ? ? ? ? /lib64/libnss_files-2.12.so
7f2a553c5000-7f2a553c8000 rw-p 00000000 00:00 0?
7f2a553cc000-7f2a553ce000 rw-p 00000000 00:00 0?
7f2a553ce000-7f2a553d5000 r--s 00000000 08:02 914369 ? ? ? ? ? ? ? ? ? ? /usr/lib64/gconv/gconv-modules.cache
7f2a553d5000-7f2a553d6000 rw-p 00000000 00:00 0?
7fffc0a71000-7fffc0a86000 rw-p 00000000 00:00 0 ? ? ? ? ? ? ? ? ? ? ? ? ?[stack]
7fffc0b0a000-7fffc0b0b000 r-xp 00000000 00:00 0 ? ? ? ? ? ? ? ? ? ? ? ? ?[vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 ? ? ? ? ? ? ? ? ?[vsyscall]
? ? ? ? ? ? ? ? ?所看到的地址不是物理地址,都是虛擬地址。出于對(duì)資源的保護(hù),一個(gè)程序并不需要立即將所有的資源全部加載到內(nèi)存,而是實(shí)際上可采用寫時(shí)申請(qǐng)的方法。而且保護(hù)系統(tǒng),很多用戶非法訪問不會(huì)造成內(nèi)核的崩潰。
? ? ?1.2虛擬地址空間的申請(qǐng)
? ? ? ? ? ? ? 32位平臺(tái)下,一個(gè)進(jìn)程擁有4G的虛擬地址,這個(gè)地址如何來分配和使用呢
- ?代碼段,數(shù)據(jù)段,BSS段,這三個(gè)部分直接從磁盤拷貝到內(nèi)存,起始地址在當(dāng)前的32位平臺(tái)下的地址為0x08048000.
- ?堆棧。是動(dòng)態(tài)變化
- mmap映射的文件
- 棧段
- 高地址1G空間,僅供內(nèi)核映射處理,用戶空間不能直接處理
內(nèi)核空間 1G gap間隙 為了安全 棧 高地址向低地址增長 map 映射區(qū)域 堆 ? gap間隙 為了安全 BSS ? 數(shù)據(jù)段 ? 代碼段 ?
- 堆和棧的起始地址是隨機(jī)產(chǎn)生的,其目的是為了避免安全漏洞。但是可以指定在堆中申請(qǐng)空間的起始地址(brk/sbrk函數(shù));
#include<stdio.h>
#include<stdlib.h>
#include<stdio.h>
#include<stdlib.h>
int main(int argc,char*argv[])
{
? brk(0x09050000);
? printf("brk=%p\n",sbrk(0));
? char *ptr=malloc(1024);
? printf("new=%p\n",ptr);
? return 1;
}
結(jié)果: - [root@localhost demo]# ./t
brk=0x9050000
new=0x9050010
- 系統(tǒng)執(zhí)行一個(gè)過程,到底怎么來加載這些空間的?可以用strace工具來查看。
- ?strace ./t
execve("./t", ["./t"], [/* 25 vars */]) = 0
brk(0) ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?= 0x7e0000//起始地址
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb578bba000
access("/etc/ld.so.preload", R_OK) ? ? ?= -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY) ? ? ?= 3
fstat(3, {st_mode=S_IFREG|0644, st_size=52315, ...}) = 0
mmap(NULL, 52315, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fb578bad000
close(3) ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?= 0
open("/lib64/libc.so.6", O_RDONLY) ? ? ?= 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0000\356A\3012\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1926800, ...}) = 0
mmap(0x32c1400000, 3750152, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x32c1400000
mprotect(0x32c158b000, 2093056, PROT_NONE) = 0
mmap(0x32c178a000, 20480, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x18a000) = 0x32c178a000
mmap(0x32c178f000, 18696, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x32c178f000
close(3) ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?= 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb578bac000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb578bab000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb578baa000
arch_prctl(ARCH_SET_FS, 0x7fb578bab700) = 0
mprotect(0x32c178a000, 16384, PROT_READ) = 0
mprotect(0x32c0e1f000, 4096, PROT_READ) = 0
munmap(0x7fb578bad000, 52315) ? ? ? ? ? = 0
brk(0x9050000) ? ? ? ? ? ? ? ? ? ? ? ? ?= 0x9050000
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 2), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb578bb9000
write(1, "brk=0x9050000\n", 14brk=0x9050000
) ? ? ? ? = 14
brk(0x9071000) ? ? ? ? ? ? ? ? ? ? ? ? ?= 0x9071000
write(1, "new=0x9050010\n", 14new=0x9050010
) ? ? ? ? = 14
exit_group(1) ? ? ? ? ? ? ? ? ? ? ? ? ? = ?
- 堆空間的起始地址是隨機(jī)的,可設(shè)置為不隨機(jī)。大小也可以設(shè)置成固定大小
代碼段,數(shù)據(jù)段,BSS段的地址在編譯鏈接時(shí)固定
在BSS結(jié)束和堆起始地址有空隙,這個(gè)空隙是隨機(jī)的
brk函數(shù)僅僅是調(diào)整在堆中申請(qǐng)空間的起始值。使用以下命令可以指定堆的起始地址固定
sudo sysctl -w kernel/randomize_va_space=0
- 系統(tǒng)默認(rèn)為每個(gè)進(jìn)程分配的堆空間大小是固定的。使用 sbrk(0)得到的是我們堆空間的結(jié)束
值。第一次使用 malloc 申請(qǐng)資源返回的地址接近于堆空間的起始值。全用 brk(addr)改變的是新申請(qǐng)數(shù)據(jù)
的堆空間起始值。 - 在真正編程中,很少全用 brk/sbrk,使用 malloc函數(shù)來新申請(qǐng)堆空間,效率更高。部分時(shí)間使
用 mmap來映射 mmap區(qū),
棧:棧從高地址向低地址增長,棧的起始值也是隨機(jī)的。棧中主要存放的是局部變量,新調(diào)用子函
數(shù)時(shí)函數(shù)的參數(shù)及返回值。由 OS自動(dòng)管理。
1.3內(nèi)存空間的申請(qǐng)和釋放
- 代碼段中:由只讀數(shù)據(jù)和代碼組成: const,字符串常量,這些內(nèi)容的空間在編譯鏈接時(shí)申請(qǐng)好
且指定存儲(chǔ)地址。? - 數(shù)據(jù)段 BSS 段申請(qǐng):定義的全局的或者是靜態(tài)的變量。已經(jīng)初始化的在數(shù)據(jù)段,未初始化的
在 BSS段中。因此也是在編譯鏈接時(shí)已經(jīng)申請(qǐng)且指定的地址。
以上空間的申請(qǐng)?jiān)谶\(yùn)行時(shí)候就加載到內(nèi)存中,直到程序的結(jié)束,不在發(fā)生變化,除了exec函數(shù)執(zhí)行 - 堆:由程序員自己動(dòng)態(tài)管理。
動(dòng)態(tài)申請(qǐng),堆的起始位置由brk函數(shù)指定,但是具體編程中一般不會(huì)自己使用它,而是使用malloc函數(shù)。內(nèi)核對(duì)內(nèi)存的管理是頁式管理,因此在 malloc申請(qǐng)空間時(shí),使用鏈?zhǔn)浇Y(jié)構(gòu)來管理已經(jīng)
申請(qǐng)的堆空間。
- 棧:由os管理
- mmap的庫以及相關(guān)文件
將一個(gè)文件的內(nèi)容全部或者部分的映射到虛擬地址空間中,后面釋放時(shí)操作這段內(nèi)存空間可以被同
步到磁盤文件的操作,效率比較高。共享映射,其它進(jìn)程如果也映射這個(gè)文件,可以立即看到這個(gè)更改,但并不是立即更新磁盤文件的內(nèi)容,如果要更新到磁盤,必須使用 msync以及 munmap時(shí)。?
且指定存儲(chǔ)地址。?
在 BSS段中。因此也是在編譯鏈接時(shí)已經(jīng)申請(qǐng)且指定的地址。
以上空間的申請(qǐng)?jiān)谶\(yùn)行時(shí)候就加載到內(nèi)存中,直到程序的結(jié)束,不在發(fā)生變化,除了exec函數(shù)執(zhí)行
動(dòng)態(tài)申請(qǐng),堆的起始位置由brk函數(shù)指定,但是具體編程中一般不會(huì)自己使用它,而是使用malloc函數(shù)。內(nèi)核對(duì)內(nèi)存的管理是頁式管理,因此在 malloc申請(qǐng)空間時(shí),使用鏈?zhǔn)浇Y(jié)構(gòu)來管理已經(jīng)
申請(qǐng)的堆空間。
將一個(gè)文件的內(nèi)容全部或者部分的映射到虛擬地址空間中,后面釋放時(shí)操作這段內(nèi)存空間可以被同
步到磁盤文件的操作,效率比較高。共享映射,其它進(jìn)程如果也映射這個(gè)文件,可以立即看到這個(gè)更改,但并不是立即更新磁盤文件的內(nèi)容,如果要更新到磁盤,必須使用 msync以及 munmap時(shí)。?
2.常見內(nèi)存錯(cuò)誤以及valgrind使用
? ? ? ? ?代碼段:只讀數(shù)據(jù)。因此對(duì)這一部分的數(shù)據(jù),試圖寫只讀數(shù)據(jù)。這個(gè)在編譯的時(shí)候基本可以檢測(cè)。
數(shù)據(jù)段/BSS段:未初始化直接訪問。即使沒有顯式初始化,仍然會(huì)初始化為 0。
棧空間數(shù)據(jù):
(1)局部變量,未初始化這類變量會(huì)給隨機(jī)的初值,出現(xiàn)異常情況更詭異。
(2) 棧溢出,在棧中申請(qǐng)過大的局部變量。
堆空間數(shù)據(jù):
內(nèi)存泄漏,( 1)申請(qǐng)未釋放( 2)申請(qǐng)后,雙重釋放。
對(duì)于所有的地址空間:
( 1)野指針的問題。未初始化指針。去訪問這個(gè)指針指向的空間。
( 2)越界訪問,例如一個(gè)數(shù)組 a[10],試圖訪問 a[10]以及以后。
( 3)非法的越權(quán)訪問。例如 mmap的空間只讀,但試圖寫。
( 4)空間不在控制范圍仍然去訪問空間,例如返回局部變量地址,且后面訪問這個(gè)空間。
使用工具,來檢測(cè)常見的內(nèi)存錯(cuò)誤。 valgrind工具。
1041 gcc -o valgrind_example01 valgrind_example01.c -g
1042 valgrind --tool=memcheck --show-reachable=yes --read-var-info=yes --verbose --time-stamp=yes --
leak-check=full --log-file=mycode.log ./valgrind_example01
1043 less mycode.log
如果要使用圖形化的工具,要安裝 QT,這個(gè)工具名字叫 valkyrie。
? ? ? ? ?
總結(jié)
以上是生活随笔為你收集整理的Linux02进程内存管理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux程序设计01:开发工具和开发平
- 下一篇: Linux网络编程博客目录