zoukankan      html  css  js  c++  java
  • 内存分段机制

    我们可以写一段简单的c代码(code/memory/segment_1.c):

    #include <stdio.h>
    int main()
    {
       int a = 1;
       printf("Hello, World!");
       return 0;
    }
    

    然后将其转为汇编,运行:

    gcc -S segment_1.c
    

    之后会生成一个.s 文件(code/memory/segment_1.s),不用细看内容。

    我们发现,这个汇编代码中,有 .string main .text,反正分为不同的块。

    	.file	"segment_1.c"
    	.text
    	.section	.rodata
    .LC0:
    	.string	"Hello, World!"
    	.text
    	.globl	main
    	.type	main, @function
    main:
    .LFB0:
    	.cfi_startproc
    	pushq	%rbp
    	.cfi_def_cfa_offset 16
    	.cfi_offset 6, -16
    	movq	%rsp, %rbp
    	.cfi_def_cfa_register 6
    	subq	$16, %rsp
    	movl	$1, -4(%rbp)
    	leaq	.LC0(%rip), %rdi
    	movl	$0, %eax
    	call	printf@PLT
    	movl	$0, %eax
    	leave
    	.cfi_def_cfa 7, 8
    	ret
    	.cfi_endproc
    .LFE0:
    	.size	main, .-main
    	.ident	"GCC: (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0"
    	.section	.note.GNU-stack,"",@progbits
    
    

    进程在内存中,主要是按照这种形式进行存储的。

    为什么要分段呢?

    我们看上面那张图片,分为不同段,每个段的读写属性是不同的。比如正文段存储代码,就像我们上面的.s文件中的main部分。它在内存中是始终不变的,我们CPU就是从main中去取指执行。所以,它是只读的。

    而其他部分,也许是可读可写的。

    通过分段,可以对不同的属性代码、数据进行更方便的管理。如果是打乱的放在内存中,那么读写属性就很难控制。

    所以通过分段机制,我们可以更好得控制不同段的属性,这有利于内存的组织安排。更多的好处,可以后面慢慢体会。

    分段机制与间接寻址

    因为现在引入了分段机制,所以如果我们想要访问某个地方的数据,指令,我们会引入间接寻址。

    比如说,程序现在运行在代码段(CS),然后呢,他想要访问数据段的某个数据(DS),那么就可以使用 DS的基地址+偏移量,就可以访问到数据段中的数据了。

    就像我们下图一样,如果我们运行到某一条指令,他需要 DS:1 的数据,那么我们可以通过数据段开始的地址0x100 加上偏移量1,就可以直接访问到 int a = 1 了。

    所以,我们只需要知道不同段的开始地址加上偏移量,就可以访问不同段的内容。

    分段机制与LDT表

    我们前面说到,因为分段机制,所以,如果我们想要去访问某个段中的内容,我们需要获得每个段的基址。所以,我们需要一个结构去储存每个段的基址。这就是 LDT (本地描述表/local descriptor table) 表。

    (名字,有点。。。,至少本地两字说明,这个表是局部的,对于每个进程,都有一个LDT表)。

    LDT 表

    现在来说说 LDT 表,之前说了分段的好处,那么我们应该怎么设计 LDT 表呢?

    我们需要用一些位来表示:可读/可写状态,不同段的基址以及段的长度。

    现在,我们可以通过每个进程的 LDT 表找到每个段所在的位置。

    GDT 表

    那么我们如何找到每个进程的 LDT 表呢?

    这个,就轮到 GDT表了,与之前的 LDT 表,就差一个字母。G的意思,就是 global 全局的意思。

    那意味着这张表,所有的进程都可以看到,进行访问。

    在 GDT 表中,有着不同 LDT 表的地址。这里涉及到 LDTR寄存器,这个寄存器被赋值为 GDT表中的一个 LDT 表的位置,然后CPU 可以通过这个寄存器,找到对应的进程的 LDT表。

    可以看下下图。

    关于更多的 GDT 和 LDT 的介绍,可以看最后的参考部分。

    分段机制与多进程

    我们知道,CPU会采用某种策略,运行多个进程。这个时候,我们就需要保证不同的进程的内容,会存在不同的内存中,不能相互干扰。

    这就要求,我们之前说的,每个进程都有一个 LDT表。可以将不同的进程内容映射到不同的内存地址。

    总结

    最后,就拿这张图来总结下吧,大家可以用这张图来回顾下前面的内容。

    参考

    《深入理解计算机系统》

    • 7.9 加载可执行目标文件

    《Linux 内核0.11完全注释》

    • 4.3 分页机制(进阶)

    操作系统(哈工大李治军老师)32讲(全)超清 P20 L20 内存使用与分段

    GDT(全居描述符表)和LDT(局部描述符表)

  • 相关阅读:
    Hbase常用Shell命令
    Hbase的安装与基本操作
    Unity3d 游戏设计(一)井字棋
    Unity3d 二、离散仿真引擎基础
    Unity3D 一、游戏
    String StringBuffer StringBulider
    Java日志记录log4j最简明教程
    使用PinYin4j.jar将汉字转换为拼音
    线性素数筛
    求一个数的约数个数 d(n)
  • 原文地址:https://www.cnblogs.com/zhouzhiyao/p/12844485.html
Copyright © 2011-2022 走看看