导言:在查看Linux兴趣小组学生的博客时,看到这篇文章,转此,对很多初学者也许有所启发。
一个程序经过编译连接后形成的地址空间是一个虚拟地址空间,而Linux在内存寻址时简化了分段机制,使得虚拟地址与线性地址是一致的,比如程序test_wait.c代码如下:
- #include
<
stdio.h
> - #include
<
stdlib.h
>
- #include
<
unistd.h
> - int
main
(
int
argc
,
char
**
argv
) - {
-
int
i
; -
unsigned
char
*
buff
; -
buff
=
(
char
*
)
malloc
(
sizeof
(
char
)
*
1024
)
; -
printf
(
"
pid is :%d
/
n
"
,
getpid
())
; -
for
(
i
=
0
;
i
<
60
;
i
++
)
{ -
sleep
(
60
)
; -
}
-
return
0
; - }
经过编译后形成的文件是test_wait,然后用命令objdump反汇编后如下(只取部分代码):
$
objdump -d test_wait
test_wait: file format elf32-i386
Disassembly of
section .init:
08048304 <_init>:
8048304: 55
push %ebp
8048305: 89 e5 mov
%esp,%ebp
8048307: 53 push %ebx
8048308: 83
ec 04 sub $0x4,%esp
804830b: e8 00 00 00 00
call 8048310 <_init+0xc>
8048310: 5b pop
%ebx
8048311: 81 c3 e4 1c 00 00 add $0x1ce4,%ebx
8048317:
8b 93 fc ff ff ff mov -0x4(%ebx),%edx
804831d: 85
d2 test %edx,%edx
8048301: e8 2e 00 00 00
call 8048334 <__gmon_start__@plt>
8048306: e8 15 01 00 00
call 8048420
可以看到,其中的地址就是虚拟地址,整个虚拟地址空间大小为3GB,再加上可以通过系统调用进入内核的1GB空间,于是每个进程可以拥有4GB的虚拟地址空间(也叫虚拟内存)。某个进程的虚拟地址空间可以通过/proc文件系统看到:
$
./test_wait
pid is :9840
重新开一个终端:
cat
/proc/9840/maps
08048000-08049000 r-xp 00000000 08:01 212891
/home/chen/mem/test_wait
08049000-0804a000 r--p 00000000 08:01 212891
/home/chen/mem/test_wait
0804a000-0804b000 rw-p 00001000 08:01 212891
/home/chen/mem/test_wait
096d5000-096f6000 rw-p 096d5000 00:00 0
[heap]
b7dee000-b7def000 rw-p b7dee000 00:00 0
b7def000-b7f47000 r-xp
00000000 08:01 409724 /lib/tls/i686/cmov/libc-2.8.90.so
b7f47000-b7f49000
r--p 00158000 08:01 409724
/lib/tls/i686/cmov/libc-2.8.90.so
b7f49000-b7f4a000 rw-p 0015a000 08:01
409724 /lib/tls/i686/cmov/libc-2.8.90.so
b7f4a000-b7f4d000 rw-p b7f4a000
00:00 0
b7f59000-b7f5c000 rw-p b7f59000 00:00 0
b7f5c000-b7f76000 r-xp
00000000 08:01 392460 /lib/ld-2.8.90.so
b7f76000-b7f77000 r-xp b7f76000
00:00 0 [vdso]
b7f77000-b7f78000 r--p 0001a000 08:01 392460
/lib/ld-2.8.90.so
b7f78000-b7f79000 rw-p 0001b000 08:01 392460
/lib/ld-2.8.90.so
bf964000-bf979000 rw-p bffeb000 00:00 0
[stack]
关于此文件的详细信息可以参看:
http://www.kerneltravel.net/?p=287
由上面的信息可以看到
08048000-08049000地址段的标志是r-xp(读,执行)是代码段,
08049000-0804a000的标志是rw-p(读写)是数据段
096d5000-096f6000是堆也叫空洞,只有当程序中调用malloc()申请空间时才有堆段。
bf964000-bf979000
是堆栈段
这样我们可以看到进程的用户空间的分配了。如下图:
可以看出代码段在最低地址依次往上是数据段,空洞、堆栈段在最高地址,栈指针向下移动。
进程的虚拟地址在保存在内核中的task_struct(PCB)结构中,定义如下:
struct
task_struct { //进程结构体
//……
struct mm_struct
*mm;//描述进程的整个用户空间
}
而stuct mm_struct 结构中包含了虚拟空间的结构体字段
mmap(struct
vm_area_struct *
mmap),所以可以通过模块编程来查看进程的虚拟地址空间。
关于模块编程可以看这里:
http://www.kerneltravel.net/?p=80,程序清单如下:
- #
include
<
linux
/
module
.
h
>
- #include
<
linux/init.h
> - #include
<
linux/interrupt.h
> - #include
<
linux/sched.h
> - static
int
pid
; - module_param
(
pid
,
int
,
0644
)
; - static
int
__init
memtest_init
(
void
) - {
-
struct
task_struct
*
p
; -
struct
vm_area_struct
*
temp
; -
printk
(
"
My module
worked!
/
n
"
)
; -
p
=
find_task_by_vpid
(
pid
)
; -
temp
=
p
->
mm
->
mmap
; -
while
(
temp
)
{ -
printk
(
"
start:%p
/
tend:%p
/
n
"
,
(
unsigned
long
*
)
temp
->
vm_start
, - (
unsigned
long
*
)
temp
->
vm_end
)
; -
temp
=
temp
->
vm_next
; -
}
-
return
0
; - }
- static
void
__exit
memtest_exit
(
void
) - {
-
printk
(
"
Unloading my
module.
/
n
"
)
; -
return
; - }
- module_init
(
memtest_init
)
; - module_exit
(
memtest_exit
)
; - MODULE_LICENSE
(
"
GPL
"
)
;
编译模块,运行刚才的程序test_wait,然后带参数插入模块,如下:
$ ./test_wait &
pid is
:9413
$ sudo insmod mem.ko pid=9413
[ 2690.715913] My module worked!
[
2690.715992] start:08048000 end:08049000
[ 2690.716005] start:08049000
end:0804a000
[ 2690.717029] start:0804a000 end:0804b000
[ 2690.717065]
start:096d5000 end:096f6000
[ 2690.717096] start:b7dee000
end:b7def000
[ 2690.717126] start:b7def000 end:b7f47000
[ 2690.717157]
start:b7f47000 end:b7f49000
[ 2690.717187] start:b7f49000
end:b7f4a000
[ 2690.717217] start:b7f4a000 end:b7f4d000
[ 2690.717248]
start:b7f59000 end:b7f5c000
[ 2690.717304] start:b7f5c000
end:b7f76000
[ 2690.717334] start:b7f76000 end:b7f77000
[ 2690.717364]
start:b7f77000 end:b7f78000
[ 2690.717395] start:b7f78000
end:b7f79000
[ 2690.717425] start:bf964000
end:bf979000
可以看出和刚才/proc文件系统中的地址是一样的。
在任意一个时刻,一个CPU只有一个进程在运行,所以虽然有时候很多进程的虚拟地址值有相同的,但是由于每次只有一个进程运行,在当某个进程运行时cpu就将其虚拟地址也切换进来,这样就保证了每个进程都拥有4GB的地址空间。
原文:http://chenkiss.cn/blog/?p=151#more-151