下面的结论是调试中得出的,没有看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;