进程的地址空间

 导言:在查看Linux兴趣小组学生的博客时,看到这篇文章,转此,对很多初学者也许有所启发。

-----------------------------------------------------------------------------------------------------------

一个程序经过编译连接后形成的地址空间是一个虚拟地址空间,而Linux在内存寻址时简化了分段机制,使得虚拟地址与线性地址是一致的,比如程序test_wait.c代码如下:

  1. #include < stdio.h >
  2. #include < stdlib.h >                                                                                           
  3. #include < unistd.h >
  4. int main ( int argc , char ** argv )
  5. {
  6.         int   i ;
  7.         unsigned   char * buff ;
  8.         buff = ( char * ) malloc ( sizeof ( char ) * 1024 ) ;
  9.         printf ( " pid is :%d / n " , getpid ()) ;
  10.         for   ( i = 0 ; i < 60 ; i ++ ) {
  11.                 sleep ( 60 ) ;
  12.         }   
  13.         return   0 ;
  14. }


经过编译后形成的文件是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,程序清单如下:

  1.  # include < linux / module . h >                                                                                     
  2. #include < linux/init.h >
  3. #include < linux/interrupt.h >
  4. #include < linux/sched.h >
  5. static int pid ;
  6. module_param ( pid , int , 0644 ) ;
  7. static   int __init memtest_init ( void )
  8. {
  9.         struct   task_struct * p ;
  10.         struct   vm_area_struct * temp ;
  11.         printk ( " My module worked! / n " ) ;
  12.         p = find_task_by_vpid ( pid ) ;
  13.         temp = p -> mm -> mmap ;
  14.         while ( temp )   {
  15.                 printk ( " start:%p / tend:%p / n " , ( unsigned   long * ) temp -> vm_start ,
  16. ( unsigned   long * ) temp -> vm_end ) ;
  17.                 temp = temp -> vm_next ;
  18.          }   
  19.         return   0 ;
  20. }
  21. static   void __exit memtest_exit ( void )
  22. {
  23.         printk ( " Unloading my module. / n " ) ;
  24.         return ;
  25. }
  26. module_init ( memtest_init ) ;
  27. module_exit ( memtest_exit ) ;
  28. 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值