1.進程地址空間
? ?1.1程序的結構與進程的結構
? ? ? ?[root@localhost demo]# size test
? text ? data ? ?bss ? ?dec ? ?hex filename
? ? 1193 ? ?492 ? ? 16 ? 1701 ? ?6a5 test
? ? ? ? 一個可執行程序包含三個部分:
? ? ? ? 代碼段:主要存放指令,操作以及只讀的常量數據例如字符串常量。
? ? ? ? 數據段:全局或者靜態的已經初始化的變量
? ? ? ? BSS段:全局或者靜態的未初始化的變量
? ? ? ? 執行一個程序要運行,需要將程序加載到內存(磁盤有專門的讀寫驅動,但是速度比較慢,內存可有cpu直接訪問讀寫,比磁盤速度要快)
? ?
名稱 | 速度 |
CPU寄存器 | 1ns |
cache | 1ns~10ns |
DRAM | 100ns |
磁盤 | 10ss |
? ? ? ? ? ? ? ? 其資源分為內核資源和用戶資源。內核資源是PCB---進程控制塊,也就是一個結構體,負責管理進程所有資源。
? ? ? ? ? ? ? ? mm_struct指向該進程相關的內存資源:從低到高地址分為:代碼段->數據段->BSS段->堆段->棧段
? ? ? ? ? ? ? ? 在執行程序時,系統首先在內核空間創建一個進程,為這個進程申請一個進程控制塊PCB(task_struct),用于管理整個進城的所有資源。
? ?? 代碼段,數據段,BSS段,直接從磁盤拷貝到當前內存空間,大小相等。
? ?? 動態的空間:堆和棧空間,以及mmap段(主要是映射其他庫的相關信息)
? ? ? ? ? ? ? ? 命令:pmap 進程號或者cat /proc/進程號/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]
? ? ? ? ? ? ? ? ?所看到的地址不是物理地址,都是虛擬地址。出于對資源的保護,一個程序并不需要立即將所有的資源全部加載到內存,而是實際上可采用寫時申請的方法。而且保護系統,很多用戶非法訪問不會造成內核的崩潰。
? ? ?1.2虛擬地址空間的申請
? ? ? ? ? ? ? 32位平臺下,一個進程擁有4G的虛擬地址,這個地址如何來分配和使用呢

- ?代碼段,數據段,BSS段,這三個部分直接從磁盤拷貝到內存,起始地址在當前的32位平臺下的地址為0x08048000.
- ?堆棧。是動態變化
- mmap映射的文件
- 棧段
- 高地址1G空間,僅供內核映射處理,用戶空間不能直接處理
內核空間 1G gap間隙 為了安全 棧 高地址向低地址增長 map 映射區域 堆 ? gap間隙 為了安全 BSS ? 數據段 ? 代碼段 ?
- 堆和棧的起始地址是隨機產生的,其目的是為了避免安全漏洞。但是可以指定在堆中申請空間的起始地址(brk/sbrk函數);
#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;
}
結果: - [root@localhost demo]# ./t
brk=0x9050000
new=0x9050010
- 系統執行一個過程,到底怎么來加載這些空間的?可以用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) ? ? ? ? ? ? ? ? ? ? ? ? ? = ?
- 堆空間的起始地址是隨機的,可設置為不隨機。大小也可以設置成固定大小
代碼段,數據段,BSS段的地址在編譯鏈接時固定
在BSS結束和堆起始地址有空隙,這個空隙是隨機的
brk函數僅僅是調整在堆中申請空間的起始值。使用以下命令可以指定堆的起始地址固定
sudo sysctl -w kernel/randomize_va_space=0
- 系統默認為每個進程分配的堆空間大小是固定的。使用 sbrk(0)得到的是我們堆空間的結束
值。第一次使用 malloc 申請資源返回的地址接近于堆空間的起始值。全用 brk(addr)改變的是新申請數據
的堆空間起始值。 - 在真正編程中,很少全用 brk/sbrk,使用 malloc函數來新申請堆空間,效率更高。部分時間使
用 mmap來映射 mmap區,
棧:棧從高地址向低地址增長,棧的起始值也是隨機的。棧中主要存放的是局部變量,新調用子函
數時函數的參數及返回值。由 OS自動管理。
1.3內存空間的申請和釋放
- 代碼段中:由只讀數據和代碼組成: const,字符串常量,這些內容的空間在編譯鏈接時申請好
且指定存儲地址。? - 數據段 BSS 段申請:定義的全局的或者是靜態的變量。已經初始化的在數據段,未初始化的
在 BSS段中。因此也是在編譯鏈接時已經申請且指定的地址。
以上空間的申請在運行時候就加載到內存中,直到程序的結束,不在發生變化,除了exec函數執行 - 堆:由程序員自己動態管理。
動態申請,堆的起始位置由brk函數指定,但是具體編程中一般不會自己使用它,而是使用malloc函數。內核對內存的管理是頁式管理,因此在 malloc申請空間時,使用鏈式結構來管理已經
申請的堆空間。
- 棧:由os管理
- mmap的庫以及相關文件
將一個文件的內容全部或者部分的映射到虛擬地址空間中,后面釋放時操作這段內存空間可以被同
步到磁盤文件的操作,效率比較高。共享映射,其它進程如果也映射這個文件,可以立即看到這個更改,但并不是立即更新磁盤文件的內容,如果要更新到磁盤,必須使用 msync以及 munmap時。?
且指定存儲地址。?
在 BSS段中。因此也是在編譯鏈接時已經申請且指定的地址。
以上空間的申請在運行時候就加載到內存中,直到程序的結束,不在發生變化,除了exec函數執行
動態申請,堆的起始位置由brk函數指定,但是具體編程中一般不會自己使用它,而是使用malloc函數。內核對內存的管理是頁式管理,因此在 malloc申請空間時,使用鏈式結構來管理已經
申請的堆空間。
將一個文件的內容全部或者部分的映射到虛擬地址空間中,后面釋放時操作這段內存空間可以被同
步到磁盤文件的操作,效率比較高。共享映射,其它進程如果也映射這個文件,可以立即看到這個更改,但并不是立即更新磁盤文件的內容,如果要更新到磁盤,必須使用 msync以及 munmap時。?
2.常見內存錯誤以及valgrind使用
? ? ? ? ?代碼段:只讀數據。因此對這一部分的數據,試圖寫只讀數據。這個在編譯的時候基本可以檢測。
數據段/BSS段:未初始化直接訪問。即使沒有顯式初始化,仍然會初始化為 0。
棧空間數據:
(1)局部變量,未初始化這類變量會給隨機的初值,出現異常情況更詭異。
(2) 棧溢出,在棧中申請過大的局部變量。
堆空間數據:
內存泄漏,( 1)申請未釋放( 2)申請后,雙重釋放。
對于所有的地址空間:
( 1)野指針的問題。未初始化指針。去訪問這個指針指向的空間。
( 2)越界訪問,例如一個數組 a[10],試圖訪問 a[10]以及以后。
( 3)非法的越權訪問。例如 mmap的空間只讀,但試圖寫。
( 4)空間不在控制范圍仍然去訪問空間,例如返回局部變量地址,且后面訪問這個空間。
使用工具,來檢測常見的內存錯誤。 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,這個工具名字叫 valkyrie。
? ? ? ? ?