return 0;
}
我们用gcc -c test.c -o test.o生成目标文件。这个ELF文件是可重定位的,用readelf -S ./test.o可以看到类似如下的输出:
[ 1] .text PROGBITS 00000000 000034 000097 00 AX 0 0 4
[ 2] .rel.text REL 00000000 0004a0 000070 08 9 1 4
[ 3] .data PROGBITS 00000000 0000cc 000004 00 WA 0 0 4
[ 4] .bss NOBITS 00000000 0000d0 000004 00 WA 0 0 4
[ 5] .rodata PROGBITS 00000000 0000d0 00009f 00 A 0 0 4
...
我们可以看到所有目标文件的各种基地址都是从0x0开始,只是偏移不一样。.rel.text中记录了哪些是需要重定位的,如上面的printf,malloc。这些函数定义都需要链接的时候从相应的库中找到,并重定位。
然后我们用gcc test.o -o test生成可执行的ELF文件。用readelf -S ./test 可以看到类似如下的输出:
[ 9] .rel.dyn REL 080482b8 0002b8 000008 08 A 5 0 4
[10] .rel.plt REL 080482c0 0002c0 000020 08 A 5 12 4
[11] .init PROGBITS 080482e0 0002e0 000017 00 AX 0 0 4
[12] .plt PROGBITS 080482f8 0002f8 000050 04 AX 0 0 4
[13] .text PROGBITS 08048350 000350 0001dc 00 AX 0 0 16
[14] .fini PROGBITS 0804852c 00052c 00001c 00 AX 0 0 4
[15] .rodata PROGBITS 08048548 000548 0000a7 00 A 0 0 4
...
[23] .data PROGBITS 080496f0 0006f0 000010 00 WA 0 0 4
[24] .bss NOBITS 08049700 000700 000008 00 WA 0 0 4
...
我们看到经过链接后,目标文件的的基地址都发生了变化,都是唯一的逻辑地址了。其中.rel.dyn,.rel.plt等段中记录了哪些是需要运行时,由动态链接器加载重新定位的, 链接只是做了个标记,这些函数应该从哪个动态链接库中去找。如printf,malloc。
我们运行./test程序,可以看到输出:
Address of main (Text):0x80483f4
Address of init_var (Data):0x80496fc
Address of uninit_var (BSS):0x8049704
Address of stack_var (Stack):0xbfcc6a4c
Address of heap_var (Heap):0x804a008
即main函数的逻辑地址是0x80483f4,比对上面readelf -S ./test的输出,可以看到它位于.text段中
初始化的static变量init_var的逻辑地址是0x80496fc, 位于.data段中
未初始化的static变量uninit_var的逻辑地址是0x8049704,位于.bss段中
栈上的变量stack_var的逻辑地址是0xbfcc6a4c,从用户态空间的高地址(0xBFFFFFFF)向低地址增加
堆上的变量heap_var的逻辑地址是0x804a008,从用户态空间的低地址向高地址增加
所以用户程序进程在虚拟内存中的大致分布是:
0xFFFFFFFF
内核空间(1G)
0xBFFFFFFF
栈
.
.
. 用户空间(3G)
堆
BSS
数据段
代码段
0x00000000
当然这只是一个示意简图,只画出了主要的区段,其实虚存中还有其他的区段,包括内核空间也有自己的堆栈,还有ELF文件格式中区/段的区别,这里我就不详述了。感兴趣的同学可以去查看ELF文件格式说明等资料。
这一次,我们分析出了用户进程的虚存布局,下一次,我们将来研究,这些虚存是如何使用,如何被分配回收的。以及相应的物理内存又是怎么被分配回收的。
Pthread 08/01/21
}
我们用gcc -c test.c -o test.o生成目标文件。这个ELF文件是可重定位的,用readelf -S ./test.o可以看到类似如下的输出:
[ 1] .text PROGBITS 00000000 000034 000097 00 AX 0 0 4
[ 2] .rel.text REL 00000000 0004a0 000070 08 9 1 4
[ 3] .data PROGBITS 00000000 0000cc 000004 00 WA 0 0 4
[ 4] .bss NOBITS 00000000 0000d0 000004 00 WA 0 0 4
[ 5] .rodata PROGBITS 00000000 0000d0 00009f 00 A 0 0 4
...
我们可以看到所有目标文件的各种基地址都是从0x0开始,只是偏移不一样。.rel.text中记录了哪些是需要重定位的,如上面的printf,malloc。这些函数定义都需要链接的时候从相应的库中找到,并重定位。
然后我们用gcc test.o -o test生成可执行的ELF文件。用readelf -S ./test 可以看到类似如下的输出:
[ 9] .rel.dyn REL 080482b8 0002b8 000008 08 A 5 0 4
[10] .rel.plt REL 080482c0 0002c0 000020 08 A 5 12 4
[11] .init PROGBITS 080482e0 0002e0 000017 00 AX 0 0 4
[12] .plt PROGBITS 080482f8 0002f8 000050 04 AX 0 0 4
[13] .text PROGBITS 08048350 000350 0001dc 00 AX 0 0 16
[14] .fini PROGBITS 0804852c 00052c 00001c 00 AX 0 0 4
[15] .rodata PROGBITS 08048548 000548 0000a7 00 A 0 0 4
...
[23] .data PROGBITS 080496f0 0006f0 000010 00 WA 0 0 4
[24] .bss NOBITS 08049700 000700 000008 00 WA 0 0 4
...
我们看到经过链接后,目标文件的的基地址都发生了变化,都是唯一的逻辑地址了。其中.rel.dyn,.rel.plt等段中记录了哪些是需要运行时,由动态链接器加载重新定位的, 链接只是做了个标记,这些函数应该从哪个动态链接库中去找。如printf,malloc。
我们运行./test程序,可以看到输出:
Address of main (Text):0x80483f4
Address of init_var (Data):0x80496fc
Address of uninit_var (BSS):0x8049704
Address of stack_var (Stack):0xbfcc6a4c
Address of heap_var (Heap):0x804a008
即main函数的逻辑地址是0x80483f4,比对上面readelf -S ./test的输出,可以看到它位于.text段中
初始化的static变量init_var的逻辑地址是0x80496fc, 位于.data段中
未初始化的static变量uninit_var的逻辑地址是0x8049704,位于.bss段中
栈上的变量stack_var的逻辑地址是0xbfcc6a4c,从用户态空间的高地址(0xBFFFFFFF)向低地址增加
堆上的变量heap_var的逻辑地址是0x804a008,从用户态空间的低地址向高地址增加
所以用户程序进程在虚拟内存中的大致分布是:
0xFFFFFFFF
内核空间(1G)
0xBFFFFFFF
栈
.
.
. 用户空间(3G)
堆
BSS
数据段
代码段
0x00000000
当然这只是一个示意简图,只画出了主要的区段,其实虚存中还有其他的区段,包括内核空间也有自己的堆栈,还有ELF文件格式中区/段的区别,这里我就不详述了。感兴趣的同学可以去查看ELF文件格式说明等资料。
这一次,我们分析出了用户进程的虚存布局,下一次,我们将来研究,这些虚存是如何使用,如何被分配回收的。以及相应的物理内存又是怎么被分配回收的。
Pthread 08/01/21