免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 3048 | 回复: 9

[内存管理] 虚拟地址 逻辑地址 线性地址 物理地址 虚拟存储器 物理存储器 [复制链接]

论坛徽章:
0
发表于 2014-06-20 15:42 |显示全部楼层
有关于上述几类的地址,最近有做学习和整理,基本上是明白其含义,但是还不够火候

在此一来作为学习的标记,二来还希望大家可以不吝赐教

下面篇幅有点长,内容有点杂乱,问题也有点小多;望各位耐心看完 :wink:

相关帖子,有看过一些。比如: http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=2083672

理解上

由于intel(32位)处理器的分段机制,因此有了 逻辑地址的概念,其组成是 段选择符(16位) + 段内偏移量(32位)。该逻辑地址由cpu的分段单元解析后,得到32位线性地址。

如果没有分页部件的存在,该线性地址就对应于物理地址

分页部件把该线性地址转化成物理地址。根据查找页表来进行转化



在微机原理书上有说明,对于32位微处理器,可以访问2^32字节的物理存储器,但它支持多任务时,每个任务又能得到最大为2^46字节

物理存储器对应于物理上的内存,是cpu可以访问的存储器空间
虚拟存储器是程序占有的空间,其容量由cpu的内部结构所决定

对于8086cpu来说,程序占有的虚拟存储器和cpu能访问的存储器是一致的,都是1M。(8086的地址总线是20位)
对于 32位的cpu来说,两者是不同的。cpu最大可以访问的存储器是 4G, 而对于存储在磁盘的程序而言,最大可以写 2^46字节的程序

对于以上的理解,有
问题一: 为何在支持多任务时候,每个任务会有2^46字节。同时逻辑地址到底是48位还是46位?

看起来说和 cpu的内部结构有关。对于这个 46字节,根据后续的介绍有: 14位的段选择符 + 32位的段内偏移
因为段选择符的最低两位是相关的权限标志位。不知以上理解是否正确

以上都是有关理论上的知识,有
问题二:对于逻辑地址,在哪可以比较直白的看到该地址的存在?或者说通过什么方式我们可以看到这 48/46位的逻辑地址

我们可以通过使用 readelf -S a.out(参考下面的问题三中的讨论)可以来查看程序的虚拟内存空间。理论上我们在此可以看到最大 2^46大小的虚拟内存空间。

我理解上,其中列出的就是虚拟地址,也就等同于上述所说的 逻辑地址的偏移量。

对于上述的逻辑地址,后来在翻书时候有注意到,对于汇编语言中的直接寻址方式。(以下是8086cpu,也就是20位地址总线,16位数据总线结构的)

比如 “ mov AX, [1070H]“ 该操作是将 DS段的 1070H和 1071H两单元的内容放置到 AX中。因为直接寻址的默认段寄存器是 DS

如果要指定其他段寄存器,应该指令前用前缀指定段寄存器名称,比如 ” CS:mov BX, [3000H]“

以上两个指令,假设 DS为2000H, CS为5100H
指令一就是将 21070H和 21071H两单元内容存放到 AX中(DS向左偏移4位后加上后续的偏移量组成)
指令二是将 54000H和 54001H两单元内容存放到 BX中

21070H是逻辑地址还是线性地址??
如果是逻辑地址,是否说在汇编语言中可以查看到 逻辑地址的踪迹?


对于 程序,参考某网络上相关程序内存布局的文章
  1. #include <stdio.h>
  2. #include <stdlib.h>

  3. int global_init_a=1;
  4. int global_uninit_a;

  5. int main()
  6. {
  7.     int local_init_a=1;
  8.     int local_uninit_a;

  9.     int * malloc_p_a;
  10.     malloc_p_a=malloc(sizeof(int));

  11.     printf("\n         &global_init_a=%p \t          "
  12.          "global_init_a=%d\n",&global_init_a,global_init_a);

  13.     printf("       &global_uninit_a=%p \t       "
  14.         "global_uninit_a=%d\n",&global_uninit_a,global_uninit_a);   


  15.     printf("\n          &local_init_a=%p \t          "
  16.         "local_init_a=%d\n",&local_init_a,local_init_a);

  17.     printf("        &local_uninit_a=%p \t        "
  18.         "local_uninit_a=%d\n",&local_uninit_a,local_uninit_a);
  19.    
  20.     printf("             malloc_p_a=%p \t           "
  21.         "*malloc_p_a=%d\n",malloc_p_a,*malloc_p_a);
  22.    
  23.     while(1);
  24.     return 0;
  25. }
复制代码
运行结果:
  1. [martin@stack]$ gcc process_mem.c
  2. [martin@stack]$ ./a.out &
  3. [1] 7418
  4. [martin@stack]$
  5.          &global_init_a=0x804a024                   global_init_a=1
  6.        &global_uninit_a=0x804a02c                global_uninit_a=0

  7.           &local_init_a=0xbfb1f5a4                   local_init_a=1
  8.         &local_uninit_a=0xbfb1f5a8                 local_uninit_a=134513931
  9.              malloc_p_a=0x8758008                    *malloc_p_a=0

  10. [martin@stack]$
复制代码
查看进程的maps
  1. [martin@stack]$ sudo cat /proc/7418/maps
  2. 08048000-08049000 r-xp 00000000 08:08 1700899    /home/martin/debug/work/test/stack/a.out
  3. 08049000-0804a000 r--p 00000000 08:08 1700899    /home/martin/debug/work/test/stack/a.out
  4. 0804a000-0804b000 rw-p 00001000 08:08 1700899    /home/martin/debug/work/test/stack/a.out
  5. 08758000-08779000 rw-p 00000000 00:00 0          [heap]
  6. b7542000-b7543000 rw-p 00000000 00:00 0
  7. b7543000-b76ec000 r-xp 00000000 08:0a 1570780    /lib/i386-linux-gnu/libc-2.19.so
  8. b76ec000-b76ee000 r--p 001a9000 08:0a 1570780    /lib/i386-linux-gnu/libc-2.19.so
  9. b76ee000-b76ef000 rw-p 001ab000 08:0a 1570780    /lib/i386-linux-gnu/libc-2.19.so
  10. b76ef000-b76f2000 rw-p 00000000 00:00 0
  11. b7709000-b770c000 rw-p 00000000 00:00 0
  12. b770c000-b770d000 r-xp 00000000 00:00 0          [vdso]
  13. b770d000-b772d000 r-xp 00000000 08:0a 1570756    /lib/i386-linux-gnu/ld-2.19.so
  14. b772d000-b772e000 r--p 0001f000 08:0a 1570756    /lib/i386-linux-gnu/ld-2.19.so
  15. b772e000-b772f000 rw-p 00020000 08:0a 1570756    /lib/i386-linux-gnu/ld-2.19.so
  16. bfb00000-bfb21000 rw-p 00000000 00:00 0          [stack]
  17. [martin@stack]$
复制代码
使用 readelf查看 a.out程序的Stack
  1. [martin@stack]$ readelf -S a.out
  2. There are 30 section headers, starting at offset 0x1154:

  3. Section Headers:
  4.   [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  5.   [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  6.   [ 1] .interp           PROGBITS        08048154 000154 000013 00   A  0   0  1
  7.   [ 2] .note.ABI-tag     NOTE            08048168 000168 000020 00   A  0   0  4
  8.   [ 3] .note.gnu.build-i NOTE            08048188 000188 000024 00   A  0   0  4
  9.   [ 4] .gnu.hash         GNU_HASH        080481ac 0001ac 000020 04   A  5   0  4
  10.   [ 5] .dynsym           DYNSYM          080481cc 0001cc 000060 10   A  6   1  4
  11.   [ 6] .dynstr           STRTAB          0804822c 00022c 000053 00   A  0   0  1
  12.   [ 7] .gnu.version      VERSYM          08048280 000280 00000c 02   A  5   0  2
  13.   [ 8] .gnu.version_r    VERNEED         0804828c 00028c 000020 00   A  6   1  4
  14.   [ 9] .rel.dyn          REL             080482ac 0002ac 000008 08   A  5   0  4
  15.   [10] .rel.plt          REL             080482b4 0002b4 000020 08   A  5  12  4
  16.   [11] .init             PROGBITS        080482d4 0002d4 000023 00  AX  0   0  4
  17.   [12] .plt              PROGBITS        08048300 000300 000050 04  AX  0   0 16
  18.   [13] .text             PROGBITS        08048350 000350 000222 00  AX  0   0 16
  19.   [14] .fini             PROGBITS        08048574 000574 000014 00  AX  0   0  4
  20.   [15] .rodata           PROGBITS        08048588 000588 000123 00   A  0   0  4
  21.   [16] .eh_frame_hdr     PROGBITS        080486ac 0006ac 00002c 00   A  0   0  4
  22.   [17] .eh_frame         PROGBITS        080486d8 0006d8 0000ac 00   A  0   0  4
  23.   [18] .init_array       INIT_ARRAY      08049f08 000f08 000004 00  WA  0   0  4
  24.   [19] .fini_array       FINI_ARRAY      08049f0c 000f0c 000004 00  WA  0   0  4
  25.   [20] .jcr              PROGBITS        08049f10 000f10 000004 00  WA  0   0  4
  26.   [21] .dynamic          DYNAMIC         08049f14 000f14 0000e8 08  WA  6   0  4
  27.   [22] .got              PROGBITS        08049ffc 000ffc 000004 04  WA  0   0  4
  28.   [23] .got.plt          PROGBITS        0804a000 001000 00001c 04  WA  0   0  4
  29.   [24] .data             PROGBITS        0804a01c 00101c 00000c 00  WA  0   0  4
  30.   [25] .bss              NOBITS          0804a028 001028 000008 00  WA  0   0  4
  31.   [26] .comment          PROGBITS        00000000 001028 000024 01  MS  0   0  1
  32.   [27] .shstrtab         STRTAB          00000000 00104c 000106 00      0   0  1
  33.   [28] .symtab           SYMTAB          00000000 001604 000460 10     29  45  4
  34.   [29] .strtab           STRTAB          00000000 001a64 000288 00      0   0  1
  35. Key to Flags:
  36.   W (write), A (alloc), X (execute), M (merge), S (strings)
  37.   I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
  38.   O (extra OS processing required) o (OS specific), p (processor specific)
  39. [martin@stack]$
复制代码
重点在于进程的 maps表,从 readelf可以看出,程序的 bss和 data数据段的虚拟地址是  0804a01c,这个应该是程序的虚拟内存空间。根据以上讨论,该虚拟内存空间可以大于 4G
该地址也可以在进程的maps中看到。

问题三:对于进程的 maps中,stack的地址 bfb00000应该是该进程占用的实际物理内存空间的物理地址,那为何还会有类似 08049000地址,这个不是程序的虚拟地址么?
这个有可能是针对 linux进程的内存管理方面的知识,不是特别清楚,所以也在此咨询下各位大婶

论坛徽章:
0
发表于 2014-06-20 16:08 |显示全部楼层
ULK中在分段一节有说到:x86中的分段鼓励程序员将程序划分成逻辑上相关的实体,例如子程序或者全局与局部数据区

是否是指代常说的 代码段,数据段,堆栈段等一段段的实体?

同时代码段是否也有必要分成不同的逻辑段??

论坛徽章:
7
2015年亚洲杯之约旦
日期:2015-03-05 17:03:522015亚冠之山东鲁能
日期:2015-09-29 13:01:2115-16赛季CBA联赛之四川
日期:2016-01-18 15:47:0215-16赛季CBA联赛之广夏
日期:2016-02-24 11:47:1515-16赛季CBA联赛之辽宁
日期:2016-11-01 09:45:4115-16赛季CBA联赛之青岛
日期:2017-02-15 10:02:182016科比退役纪念章
日期:2017-02-16 17:25:35
发表于 2014-06-20 17:14 |显示全部楼层
看内核有用处不?
我看了一年内核,可出来找工作却找不到做内核的

论坛徽章:
0
发表于 2014-06-20 17:20 |显示全部楼层
回复 3# linggang_123


    这个真不好说。
之前linux的项目倒是会设计内核的一些修改
现在在 ecos下面,基本上不太会涉及 linux的东西

现在再看linux的kernel,主要也就是:
1. 真心想了解其kernel的内容,增加知识积累了
2. 为以后可能的用途做准备,书到用时方恨少么。了解了,有些也就通了

论坛徽章:
15
射手座
日期:2014-02-26 13:45:082015年迎新春徽章
日期:2015-03-04 09:54:452015年辞旧岁徽章
日期:2015-03-03 16:54:15羊年新春福章
日期:2015-02-26 08:47:552015年亚洲杯之卡塔尔
日期:2015-02-03 08:33:45射手座
日期:2014-12-31 08:36:51水瓶座
日期:2014-06-04 08:33:52天蝎座
日期:2014-05-14 14:30:41天秤座
日期:2014-04-21 08:37:08处女座
日期:2014-04-18 16:57:05戌狗
日期:2014-04-04 12:21:33技术图书徽章
日期:2014-03-25 09:00:29
发表于 2014-06-23 08:52 |显示全部楼层
Linux内核没有使用实际分段机制,所以,逻辑地址相当于没用~
“对于进程的 maps中,stack的地址 bfb00000应该是该进程占用的实际物理内存空间的物理地址”----这个应该是虚拟地址。

论坛徽章:
0
发表于 2014-06-23 18:06 |显示全部楼层
回复 5# humjb_1983


    查看代码来看,确实是打印程序的线性地址空间
  1. static int nommu_vma_list_show(struct seq_file *m, void *v)
  2. {
  3.     struct vm_area_struct *vma;
  4. ...
  5.     seq_printf(m,
  6.            "%08lx-%08lx %c%c%c%c %08lx %02x:%02x %lu %n",
  7.            vma->vm_start,                                                                                                                             
  8.            vma->vm_end,
  9.            flags & VM_READ ? 'r' : '-',
  10.            flags & VM_WRITE ? 'w' : '-',
  11.            flags & VM_EXEC ? 'x' : '-',
  12.            flags & VM_MAYSHARE ? flags & VM_SHARED ? 'S' : 's' : 'p',
  13.            vma->vm_pgoff << PAGE_SHIFT,
  14.            MAJOR(dev), MINOR(dev), ino, &len);
  15. ...
  16. }
复制代码

论坛徽章:
0
发表于 2014-06-23 18:22 |显示全部楼层
对于
问题二:对于逻辑地址,在哪可以比较直白的看到该地址的存在?或者说通过什么方式我们可以看到这 48/46位的逻辑地址

mov AX, [1070H]
其中 逻辑地址是 DS:偏移量 1070
通过加法器得到的 21070是线性地址,也是物理地址

因此,逻辑地址是可以在汇编中看得到的

比如对于代码段的寻址,其逻辑地址应该是 CS:IP,其中IP会作为其偏移量存在,
同样对于堆栈段,逻辑地址就是 SS:ESP,其中 ESP是偏移量

论坛徽章:
15
射手座
日期:2014-02-26 13:45:082015年迎新春徽章
日期:2015-03-04 09:54:452015年辞旧岁徽章
日期:2015-03-03 16:54:15羊年新春福章
日期:2015-02-26 08:47:552015年亚洲杯之卡塔尔
日期:2015-02-03 08:33:45射手座
日期:2014-12-31 08:36:51水瓶座
日期:2014-06-04 08:33:52天蝎座
日期:2014-05-14 14:30:41天秤座
日期:2014-04-21 08:37:08处女座
日期:2014-04-18 16:57:05戌狗
日期:2014-04-04 12:21:33技术图书徽章
日期:2014-03-25 09:00:29
发表于 2014-06-23 22:07 |显示全部楼层
abin9630 发表于 2014-06-23 18:22
对于
问题二:对于逻辑地址,在哪可以比较直白的看到该地址的存在?或者说通过什么方式我们可以看到这 48/ ...

呵呵,这个差不多,但之前说了linux其实没有用分段机制,CS、DS、内核和用户态都是0.

论坛徽章:
0
发表于 2014-06-23 22:12 |显示全部楼层
回复 8# humjb_1983


注意段寄存器中并不是0,因为里面是段选择符。它们指向的段中基址是0   

论坛徽章:
15
射手座
日期:2014-02-26 13:45:082015年迎新春徽章
日期:2015-03-04 09:54:452015年辞旧岁徽章
日期:2015-03-03 16:54:15羊年新春福章
日期:2015-02-26 08:47:552015年亚洲杯之卡塔尔
日期:2015-02-03 08:33:45射手座
日期:2014-12-31 08:36:51水瓶座
日期:2014-06-04 08:33:52天蝎座
日期:2014-05-14 14:30:41天秤座
日期:2014-04-21 08:37:08处女座
日期:2014-04-18 16:57:05戌狗
日期:2014-04-04 12:21:33技术图书徽章
日期:2014-03-25 09:00:29
发表于 2014-06-23 22:15 |显示全部楼层
njuzhyf 发表于 2014-06-23 22:12
回复 8# humjb_1983

呵呵,我的意思就是段基址是0.。。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP