下面的結論是調試中得出的,沒有看kenel代碼。。。可能有問題、歡迎指正、謝謝
<code>
.section .data
brk_addr_fmt: .ascii "brk address is %p/n"
.section .bss
.section .text
.type _start, @function
.globl _start
.type mybrk, @function
_start:
movl %esp, %ebp
pushl $0
call _mybrk
addl $4, %esp
pushl %eax
pushl $brk_addr_fmt
call printf
addl $4, %esp
popl %eax
# 雖然這裏只申請了 8 個字節,參後
addl $8, %eax
pushl %eax
call _mybrk
addl $4, %esp
# 雖然前面只申請了 8 個字節,但是 1(%eax) 不會越界的
# 這是因此本次是第一次 brk , kernel 發現只申請 8 個,
# 但實際會申請 4096(0x1000) 個字節的內存
# 因此即使 movl $1, FFC(%eax) 也不會出錯
# 但是如果 movl $1, FFD(%eax) 就會出現 segemntation fault 了
movl $1, 1(%eax)
pushl %eax
pushl $brk_addr_fmt
call printf
addl $8, %ebp
pushl $0
call _mybrk
addl $4, %esp
pushl %eax
pushl $brk_addr_fmt
call printf
addl $8, %ebp
_exit:
movl $1, %eax
movl $0, %ebx
int $0x80
_mybrk:
pushl %ebp
movl %esp, %ebp
movl $45, %eax
movl 8(%ebp), %ebx
int $0x80
movl %ebp, %esp
popl %ebp
ret
</code>
break到底是什麼,break就是一個地址,該地址是從.text到stack方向、i.e. 從低地址到高地址方向中,第一個尚未map的內存單元,或者說成下一次map邏輯地址到物理地址時、會被map的第一個內存單元更合適
statck
是向下生長的,通過brk(45)
map內存時,是向上推動break邊界的,如下面的邏輯地址圖所示,該圖表示尚未進行過brk時,系統的邏輯地址示意圖,其中...表示內容,xxx表
示尚未map的邏輯地址,???表示已經map、但是尚未使用的邏輯地址
| stack_bottom | 0xBFFF FFFF
| ... |
| ... |
| stack_top |
| xxx |
| xxx |
| xxx |
| xxx |
| xxx |
| xxx |
| xxx |
| xxx |
| xxx | break
| ... |
| ... |
| ... |
| ... | .section
| ... |
| ... | 0x0804 8000
現在進行提高break邊界的操作,i.e. 請求kernel將部分虛擬內存map到物理內存
movl $45, %eax; movl $0, %ebx; int $0x80
movl %eax, %ebx; addl $8, %ebx; movl $45, %eax; int 0x80;
該圖會變爲如下,i.e. brk後kernel會分配頁的整數倍的空間,x86的頁大小是4096,上面三條指令執行後,break的地址會變成break+0x8
| stack_bottom | 0xBFFF FFFF
| ... |
| ... |
| stack_top |
| xxx |
| xxx |
| xxx |
| xxx | break+0x1000
| ??? |
| ??? |
| ... | break'= break + 0x8
| ... |
| ... | break
| ... |
| ... |
| ... |
| ... | .section
| ... |
| ... | 0x0804 8000
注意:以上的討論是第一次進行brk時的情況,如果多次進行brk,不一定是上圖所示,例如連續兩次brk,第二次brk時,kernel就不會進行map
movl $45, %eax; movl $0, %ebx; int $0x80
movl %eax, %ebx; addl $8, %ebx; movl $45, %eax; int 0x80;
movl %eax, %ebx; addl $8, %ebx; movl $45, %eax; int 0x80;