有关段式映射的问题

发布于 2022-10-15 08:23:21 字数 640 浏览 32 评论 0

各位,请教一下,有关《linux情景分析》里有关对 i386CPU 的内存管理方面的内容,具体如下:

1、里面提到了一个例子,如下:

========================================
greeting()
{
        printf("hello world\n");
}

main()
{
        greeting();
}

其中反编译后(objdump),发现 greeting()的入口地址为 0x8048368.

========================================

2、当执行 “call 0x8048368” 这条指令时,按上面所说的是,(由于是 i386CPU 的缘故)CPU分析 0x8048368 这个地址时,是先进行段式映射,再进行页式映射的。对于页式映射部分,没什么问题。但对于段式映射部分,还是不太清楚,想请教一下大家(换句话说,对于0x8048368这个地址,在linux中是如何进行段式映射的?第一步干什么、第二步干什么....),谢谢。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

画尸师 2022-10-22 08:23:22

本帖最后由 captivated 于 2011-04-27 23:04 编辑

回复 1# sherf

   
80386+ 增加了一个GDT和一个LDT寄存器,这个GDT和LDT指向一个数组,数组中每项是一个八个字节的称之为“段描述符”的数据结构 -- 这在《情景分析》中一开篇就讲了的事情吧?

然后cs ds ss(在INTEL 32位CPU上仍然是16位)等等中的13位用做“段选择子”也就是选定数组下标,用以选择“段描述符”,然后再使用“段描述符”里面的段基址 + 指令中的偏移量得到线性地址。 -- 希望我没说错吧?

上面的段式管理在Intel 32 bits CPU上是一直存在的 -- 不像页式管理一样可以决定是否关闭它 -- 所以,CPU发出的指令,一定先经过了“段式管理”映射成线性地址,CPU再把这个线性地址发送出去,要不要经过页式管理由系统程序决定。

Linux中似乎为了方便管理,没有设立许多段。所有的cs ds ss等等都是同一个值 -- 这意味着,所有的段寄存器都选择了同一个“段描述符”,因此所有的段都具有同一个段基址 -- 再假设这个段基址就是0的话 -- 因此线性地址就是4G的虚拟地址,就是完全平坦的地址。这种手段使得段式管理根本就是透明的,只有一个4G虚拟空间的平坦地址的概念。所以地址0x8048368就是0x8048368。虽然它实际上经过了段式映射,但是看起来跟没有映射一样。一句话,你不必考虑段式管理。 -- 本人也是菜鸟一个,根本不知道有没有说错 -- 请大虾们指教,同时轻点拍砖~

迷离° 2022-10-22 08:23:22

本帖最后由 sherf 于 2011-04-28 00:04 编辑

回复 5# captivated

谢谢 nmf 和 captivated 的回复,我重新再考虑了一下,觉得 i386CPU 对 0x8048368 的寻址过程应该如下:

1、段式映射

因为main() 和 greeting() 是属于同一个段,因此根据当前CS就可找到对应的 GDT/LDT 描述符,从而可以获得 greeting() 对应的基址,假定为 BASE_G(且假定其值为 0x1000000)。

2、页式映射

这个主要是针对 0x8048368,经过高10位、中间10位、低12位的对应转换等找到在一个段(一般是 0 ~ 4G)内的实际偏移,计算的过程如下:

1)经过高10位和中间10位找到对应页面“相对于段的‘虚拟首址‘(即0,假设段的虚拟空间为 0 ~ 4G)“的偏移为 0x740000

2)根据最后12位得到 0x8048368 这个线性地址相对于该段的‘虚拟首址‘(即0)的偏移为 0x740368,假设其名字叫 OFFSET_G

3、最终的 0x8048368 对应的"物理地址"为:

Addr        = 段基址  + 段内偏移
        = BASE_G + OFFSET_G
        = 0x1000000 + 0x740368
        = 0x1740368

不知道我的理解是否正确?如果正确的话,那可以推出如下(还是之前的那个例子):

========================================
greeting()
{
        printf("hello world\n");
}

main()
{
        greeting();
}

其中反编译后(objdump),发现 greeting()的入口地址为 0x8048368,假设 main() 的地址为 0x8048000

========================================

这个文件编译链接后为一个 elf 格式的 greeting 程序。现在将greeting当作一个新进程进行加载,过程如下:

1、当执行 call main() 时,

1)系统构造LDT描述符,包括基址、界限、权限等(基址要根据内存状况定)。假设段地址为 BASE_G(假设其值为 0x2000000)
2)构造页目录和页表,并将页目录首址赋予CR3
3)根据页目录和页表,得页面相对于该段的‘虚拟首址‘(即0)的偏移为 0x740000。
4)则,main()对应的物理地址为 0x2000000 + 0x740000 = 0x2740000。
5)cpu执行跳转指令(执行 call main())

2、当执行 call greeting() 时,

跟 call main() 类似,但更简单,只需 3)、4)、5)这3步,最后其物理地址为 0x2740368

我的理解对吗?

陌伤浅笑 2022-10-22 08:23:22

回复 4# mnf

LZ在于20:07在内核版发了同样的帖子,大概怕那边人气不够,20:09又在这边发了一帖。

镜花水月 2022-10-22 08:23:22

回复 6# sherf

上面几位都说了好多了,Linux下分段对地址转换没有任何影响。Linux只用GDT,用户的数据段代码段与内核的数据段代码的base都是0,limit都是4G-1。CPU确实要经过分段这一环节,但此举没有任何影响。

混吃等死 2022-10-22 08:23:22

本帖最后由 sherf 于 2011-04-28 09:26 编辑

回复  sherf

上面几位都说了好多了,Linux下分段对地址转换没有任何影响。Linux只用GDT,用户的数据段代 ...
tempname3 发表于 2011-04-28 07:20

谢谢你的回复。我知道linux使用了分段,但我不太清楚它是如何使用分段的(步骤)。另,你所提到的linux只使用了GDT,这个我在《情景分析》里也看到了,也不太理解为何。原因是,当fork出几个新的进程时,难道它们的段基址都是一样的(即你所提到的为 0 )?个人感觉对于fork出来的用户进程,应使用LDT,即使不使用LDT,那使用GDT也应保证每个新的段的段基址不一样才行,否则段的内容会相互覆盖......个人浅见而已,还在学习中.........

另,如果可以的话,希望以我在6楼所举的例子为例,说明i386cpu 是如何进行段式映射的(包括段式映射的目的又是什么?个人认为是取得段基址、段大小、权限等信息)。

情丝乱 2022-10-22 08:23:22

回复 6# sherf

    你说的很混乱。

1. 对于段式管理,你想要原理,还是要实现?
原理你说的也没什么问题,实现是:OS 一般都不使用 LDT,而且,CS 不会改变的,除非是在 user/kernel 之间切换才改变,也就是说,它不会去在 GDT 查表。

2. 对于页式管理,你就有太错了。

>> 这个主要是针对 0x8048368,经过高10位、中间10位、低12位的对应转换等找到在一个段(一般是 0 ~ 4G)内的实际偏移
段就段,页就页,不能混在一起谈,什么找到在一段内的实际偏移量,这句话是什么意思?

>> 2)根据最后12位得到 0x8048368 这个线性地址相对于该段的‘虚拟首址‘(即0)的偏移为 0x740368,假设其名字叫 OFFSET_G
大错特错,首先,你在段式和页式混乱的基础上说的,就大错了。其次,page 是物理地址,不是虚拟地址

>>3、最终的 0x8048368 对应的"物理地址"为:

Addr        = 段基址  + 段内偏移
        = BASE_G + OFFSET_G
        = 0x1000000 + 0x740368
        = 0x1740368
--------------------------------------------
物理地址= page + offset,  你一直在和 segment 混在一起,太错了

下面的例子就不想说了

握住我的手 2022-10-22 08:23:21

回复 1# sherf

先顶一下

驱逐舰岛风号 2022-10-22 08:23:21

回复 2# sherf

有人知道吗?

若有似无的小暗淡 2022-10-22 08:23:21

这个问题你跑到这里来问
1.如果,目标的代码在同一段中,就直接 CS.base+0x8048368 就行了,
2.不在同一段,则在GDT 中找到Code segment descriptor
3.经过相关的权限检查之后,转入CS : offset 中

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文