Chinaunix

标题: [X86]关于 逻辑地址 线性地址 物理地址 的讨论 [打印本页]

作者: niezhongbao    时间: 2013-08-21 10:00
标题: [X86]关于 逻辑地址 线性地址 物理地址 的讨论
本帖最后由 niezhongbao 于 2013-08-27 16:48 编辑

是否可以这样理解,求高手指正:

printf("%p\n", p);打印出来的地址是一个段偏移地址么?我们程序操作的都是一个段偏移地址,
因为GDT表中的base都是0,所以这个偏移与线性地址相同,
所以linux管理的线性地址实际上是管理段偏移地址,
CPU每次寻址都要经过 以下这个过程么?
段选择符:段偏移--(查GDT表)-->线性地址--(查进程中pgd)-->物理地址




作者: bensenq    时间: 2013-08-21 11:12
跟我的理解一样,base设为0后段式,就相当于只有页式管理。
作者: 瀚海书香    时间: 2013-08-21 13:08
回复 1# niezhongbao
printf("%p\n", p);打印出来的地址是一个段偏移地址么?我们程序操作的都是一个段偏移地址,
因为GDT表中的base都是0,所以这个偏移与线性地址相同,
所以linux管理的线性地址实际上是管理段偏移地址,
CPU每次寻址都要经过 以下这个过程么?
段选择符:段偏移--(查GDT表)-->线性地址--(查进程中pgd)-->物理地址


理解基本正确

printf打印的值确切说应该叫逻辑地址。
由于linux下的段基地址都是0,所有线性地址=逻辑地址+0
然后根据页表,查找到线性地址对应的物理地址。


作者: niezhongbao    时间: 2013-08-21 13:54
回复 3# 瀚海书香

我的理解是:
一个逻辑地址由两部分组成,段标示符(即segment selector)和段内偏移组成的,
segment selector是保存在段寄存器里面的(cs, ds, gs等),
逻辑地址的表示是  segment selector : offset, 根据segment selector去查找GDT,
GDT中找到的base是0, 加上offset就是线性地址,所以感觉应该是这个offset与线性地址相等,
有的说linux的线性地址等于逻辑地址,
但是从这个关系逆向推理的话就让人糊涂了,或者这里理解的不对?


   
作者: 瀚海书香    时间: 2013-08-21 14:38
回复 4# niezhongbao

GDT中找到的base是0, 加上offset就是线性地址,所以感觉应该是这个offset与线性地址相等,
有的说linux的线性地址等于逻辑地址,
但是从这个关系逆向推理的话就让人糊涂了,或者这里理解的不对?

   
因为base=0,所有linux下线性地址就等于逻辑地址啊
作者: niezhongbao    时间: 2013-08-21 14:48
本帖最后由 niezhongbao 于 2013-08-21 14:50 编辑

回复 5# 瀚海书香

但是线性地址=base + offset,offset只是逻辑地址组成的一部分那,如果base 为0,应该是offset = 线性地址才对呀。


   
作者: stuman    时间: 2013-08-21 20:46
逻辑地址的偏移量的值与相应的线性地址的值总是一致的
作者: 魔鬼的惊叹    时间: 2013-08-22 20:25
回复 3# 瀚海书香
我的理解和你稍有不同,我认为打印的是虚拟地址。

在编译链接时才回你用到逻辑地址,在重定位的过程中,逻辑地址会转化为相应的虚拟地址,这个转变可能由编译器完成或者装载器完成。

说的不对的地方还望多多指正。

   
作者: 瀚海书香    时间: 2013-08-23 07:39
回复 8# 魔鬼的惊叹
我的理解和你稍有不同,我认为打印的是虚拟地址。

在编译链接时才回你用到逻辑地址,在重定位的过程中,逻辑地址会转化为相应的虚拟地址,这个转变可能由编译器完成或者装载器完成。


编译程序出来后,里面用到的地址应该是逻辑地址,有段的概念(比如代码段、数据段等等),这个应该没有异议吧。
机器语言指令中出现的内存地址,都是逻辑地址,需要转换成线性地址,再经过MMU(CPU中的内存管理单元)转换成物理地址才能够被访问到。所以说这里打印的应该是逻辑地址。

其实只要记住你在进行C语言指针编程中,可以读取指针变量本身值(&操作),实际上这个值就是逻辑地址,它是相对于你当前进程数据段的地址,不和绝对物理地址相干。只有在Intel实模式下,逻辑地址才和物理地址相等(因为实模式没有分段或分页机制,Cpu不进行自动地址转换)。

虚拟地址(线性地址)=段地址+段内偏移(逻辑地址),由于linux的段地址为0,所以“虚拟地址(线性地址)=逻辑地址”

   
作者: niezhongbao    时间: 2013-08-23 08:36
本帖最后由 niezhongbao 于 2013-08-23 09:21 编辑

回复 9# 瀚海书香
回复 8# 魔鬼的惊叹

个人理解是,因为这个段偏移计算之后与线性地址相同,所以操作这个段偏移实际上就是在操作线性地址,
但是说逻辑地址等于线性地址这个理解不上去,
逻辑地址是CPU根据段寄存器中的段选择符和段偏移计算出来的,是运行过程中的地址。

要不然这个图应该怎么理解,ULK3 中的。



作者: 瀚海书香    时间: 2013-08-23 10:21
回复 10# niezhongbao
个人理解是,因为这个段偏移计算之后与线性地址相同,所以操作这个段偏移实际上就是在操作线性地址,
但是说逻辑地址等于线性地址这个理解不上去,
逻辑地址是CPU根据段寄存器中的段选择符和段偏移计算出来的,是运行过程中的地址。


1. 代码中的地址都是逻辑地址,也就是段内偏移
2. 线性地址=段基地址+段内偏移(逻辑地址)
3. Linux下段基地址为0


   
作者: 魔鬼的惊叹    时间: 2013-08-23 10:27
回复 9# 瀚海书香

在程序运行前,会有一个装载的过程,每个进程的虚拟内存空间都是4G,装载地址在win下和linux有所不同,装载后从装载地址开始运行,这个地址我觉得就是 虚拟地址,从这里开始所

有变量所用到的地址都是虚拟地址,所以我觉得打印的还是虚拟地址,毕竟打印的时候程序已经在运行了。
   
另外在实模式下是通过基地址和偏移地址来寻找物理地址。在保护模式下是通过段选择子(由段选择子可确定基地址)和偏移地址来确定虚拟地址(线性地址),再由页表得到物理地址。

内存这块比较难理解,我也不知道我理解的对不对,只是把我理解的写出来了,还请不要见笑,错误之处多多指正。


作者: 瀚海书香    时间: 2013-08-23 10:42
回复 12# 魔鬼的惊叹
在程序运行前,会有一个装载的过程,每个进程的虚拟内存空间都是4G,装载地址在win下和linux有所不同,装载后从装载地址开始运行,这个地址我觉得就是 虚拟地址,从这里开始所

有变量所用到的地址都是虚拟地址,所以我觉得打印的还是虚拟地址,毕竟打印的时候程序已经在运行了。


段机制是CPU的机制,如果编译器直接将所有的逻辑转换成线性地址,那CPU还要那些段寄存器干什么?

   
作者: niezhongbao    时间: 2013-08-23 11:47
本帖最后由 niezhongbao 于 2013-08-23 11:49 编辑

回复 11# 瀚海书香


1. 代码中的地址都是逻辑地址,也就是段内偏移
-------其他的我们俩个人的意见好像一致,
对于逻辑地址也就是段内偏移我感觉不是这么回事,
段内偏移只是逻辑地址的一部分,
我感觉逻辑地址应该是段选择符和段内偏移组成的。
作者: 瀚海书香    时间: 2013-08-23 13:04
回复 14# niezhongbao
对于逻辑地址也就是段内偏移我感觉不是这么回事,
段内偏移只是逻辑地址的一部分,
我感觉逻辑地址应该是段选择符和段内偏移组成的。


是这样的。由于linux下,由于所有的段基地址都是0,所有在linux下 逻辑地址=段内偏移
   
作者: niezhongbao    时间: 2013-08-23 13:40
回复 15# 瀚海书香

你说的段基址是指哪个,Linux在什么位置设置的呀,
不是Segment Descriptor中的base吧,
这个是由逻辑地址得到的,计算线性地址用的。

   
作者: cjdao    时间: 2013-08-23 14:08
本帖最后由 cjdao 于 2013-08-23 14:09 编辑

我的理解,应该分两个层面去看这些地址:
1.从硬件层面讲:
在程序执行的过程中,CPU需要从物理内存取指令时,pc寄存器上存放的值就是下一条指令的内存地址;在x86系列上,pc寄存器里的值就是一个逻辑地址,然后经过段式转换(即段基地址+段内偏移(逻辑地址))后会得到线性地址(虚拟地址),如果没有开启页式管理,则线性地址当成物理地址直接送上地址总线;否则线性地址经过页式转换成为物理地址再送上地址总线。
所以,从硬件层面讲,程序中的地址一律都是逻辑地址,哪怕段基地址为0!
2.从linux操作系统层面讲:
个人理解,linux对于地址只分成虚拟地址和物理地址两类:虚拟地址空间分成用户空间和内核空间;物理地址空间以page为最小单位进行管理,然后是zone,最后是node。程序中的地址是虚拟地址,虚拟地址转换成物理地址的过程就是页式转换。这种设计显然与x86上的设计不吻合的,而linux想要在x86上运行就必须符合x86对于地址转换的要求--程序中的地址是逻辑地址,需要经过段式转换后才能成为虚拟地址。linux将所有段基址设置为0,成功的解决了这个问题,使得从硬件上看程序中的地址是逻辑地址,而从操作系统层面上看程序中的地址是虚拟地址而觉察不到逻辑地址的存在。

所以,从硬件层面讲,程序中的地址一律都是逻辑地址,从操作系统层面看程序中的地址是虚拟地址!各位( @瀚海书香 @魔鬼的惊叹 @niezhongbao )不知我的理解对不对

另外,操作系统对于用户空间的管理,会将虚拟地址空间分成一些域(或者说段),不过这里这个段的概念跟硬件段的概念不是同一个概念来的。
作者: niezhongbao    时间: 2013-08-23 15:27
本帖最后由 niezhongbao 于 2013-08-23 15:32 编辑

回复 17# cjdao

cjdao 发表于 2013-08-23 14:08
pc寄存器里的值就是一个逻辑地址,然后经过段式转换(即段基地址+段内偏移(逻辑地址))后会得到线性地址(虚拟地址)


基本认同你的观点,说的特别细特别好,不过就是对PC寄存器里面的值我有一些想不通,
感觉是段内偏移,我感觉逻辑地址是分开存放的,分别在段内偏移和段寄存器(cs等)内的,
而且逻辑地址也可以表示为segment selector : offset,
我的理解是在linux中,逻辑地址是没有显示存在的。
作者: niezhongbao    时间: 2013-08-23 16:23
回复 17# cjdao
你说的是IP或者EIP寄存器吧,存放的是段内偏移,需要和CS寄存器一起使用。

   
作者: kiongf    时间: 2013-08-24 00:56
从左到右依次转换

intel-protected-mode-memory-management.jpg (48.42 KB, 下载次数: 50)

intel-protected-mode-memory-management.jpg

作者: bensenq    时间: 2013-08-24 00:57
回复 12# 魔鬼的惊叹


1.    因为应用程序编译的时候根本不知道自己会加载到那个页面、使用那个段,也就是说程序内的地址都是最原始的、未经过硬件变换的地址,也就是段内偏移喽。
2.    从机制上来说,X86下Linux也可以使用段机制,把段机制置为非0值,这样在页面映射的时候无非多个偏移而已。但是Linux选择不使用段式管理(通过将段基设为0)是因为CPU有了页式管理以后,段式就没什么优势了。现代的通用CPU的MMU基本上都是基于页来影射的。
作者: 魔鬼的惊叹    时间: 2013-08-24 11:18
回复 21# bensenq
你说的我不是很理解,看来还需要看些这方面的书。

   
作者: niezhongbao    时间: 2013-08-26 17:39
回复 20# kiongf
很明了~


   
作者: cnlostain    时间: 2013-09-03 23:27
额 能说说x64和ia32的区别吗




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2