Linux的分段管理

概述

本文主要描述的是基于X86平台的分段管理机制,本参考Linux-2.6.11进行说明。其中的基本概念可以参考《深入理解Linux内核》第二章。

分段的概念

在了解清楚分段之前,我们需要知道以下三种地址之间的关系:逻辑地址,线性地址,物理地址。

分段单元
分页单元
逻辑地址
线性地址
物理地址
  • 逻辑地址
    包含在机器语言指令中用来指定一个操作数或一条指令的地址,它由段部分和偏移部分组成。
  • 线性地址
    我理解的是在X86平台中因为使能分页管理之前需要先使能分段管理,所以才需要转换成线性地址再进行分页管理,我们在后面会发现在Linux中,逻辑地址的偏移部分与线性地址是一样的。
  • 物理地址
    用于内存芯片单元的寻址。

逻辑地址到线性地址的转换过程

其实转换过程中主要是由MMU的分段单元来负责的,但是对于操作系统来说,其需要分配好段描述表,以及设置好段选择符(其为段寄存器的2-15位)。
转换操作主要由以下过程完成:

  1. 先检查段选择符的TI字段,以决定段描述符在哪一个描述符表中,TI=0为全局描述符表(gdt), TI=1为局部描述符表(LDT).
  2. 以段描述符的index字段为索引,从GDT/LDT中获取对应的描述符,由于一个描述符的大小为8bytes,所以index*8 + GDT/LDT基地址就得到了描述符的地址。
  3. 把逻辑地址的偏移量与段描述符Base字段的值相加就得到了线性地址。
    在这里插入图片描述

Linux中的分段

分段机制主要是由80x86引入的,而且大多数的平台并不支持分段机制,所以在Linux内核中是以非常有限的方式在使用分段。2.6版的Linux只有在80x86结构下才使用了分段。
在Linux中,所有用户态进程都使用相同的数据段和代码端,并且所有进程的内核态代码段和数据段也相同,这四个主要的段描述符如表1所示。
表1:

段名BaseGLimiitSTypeDPLD/BP
用户代码段0x0000000010xfffff110311
用户数据段0x0000000010xfffff12311
用户代码段0x0000000010xfffff110011
用户数据段0x0000000010xfffff12011

相应的段选择符由宏__USER_CS, __USER_DS,__KERNEL_CS,和__KERNEL_DS分别定义。例如,为了对内核代码段寻址,内核只需要把__KERNEL_CS宏产生的值装进cs段寄存器即可。
从表1中可知,所有段的基地址都为0x00000000,并且寻址范围为(0-0xfffff)*4kbytes, 乘以4kbytes是因为G=1。所以虚拟地址(逻辑地址的偏移量)与线性地址相同。

Linux GDT

在单处理器系统中只有一个GDT,而在多处理器中每个CPU对应一个GDT。所有的GDT都存放在cpu_gdt_table数组中,而所有GDT的地址和它们的大小(初始化dgtr寄存器时使用)都被存放在cpu_gdt_descr数组中。这些符号定义在arch/i386/kernel/head.S中。

cpu_gdt_descr:
	.word GDT_ENTRIES*8-1
	.long cpu_gdt_table

	.fill NR_CPUS-1,8,0		# space for the other GDT descriptors

/*
 * The boot_gdt_table must mirror the equivalent in setup.S and is
 * used only for booting.
 */
	.align L1_CACHE_BYTES
ENTRY(boot_gdt_table)
	.fill GDT_ENTRY_BOOT_CS,8,0
	.quad 0x00cf9a000000ffff	/* kernel 4GB code at 0x00000000 */
	.quad 0x00cf92000000ffff	/* kernel 4GB data at 0x00000000 */

/*
 * The Global Descriptor Table contains 28 quadwords, per-CPU.
 */
	.align PAGE_SIZE_asm
ENTRY(cpu_gdt_table)
	.quad 0x0000000000000000	/* NULL descriptor */
	.quad 0x0000000000000000	/* 0x0b reserved */
	.quad 0x0000000000000000	/* 0x13 reserved */
	.quad 0x0000000000000000	/* 0x1b reserved */
	.quad 0x0000000000000000	/* 0x20 unused */
	.quad 0x0000000000000000	/* 0x28 unused */
	.quad 0x0000000000000000	/* 0x33 TLS entry 1 */
	.quad 0x0000000000000000	/* 0x3b TLS entry 2 */
	.quad 0x0000000000000000	/* 0x43 TLS entry 3 */
	.quad 0x0000000000000000	/* 0x4b reserved */
	.quad 0x0000000000000000	/* 0x53 reserved */
	.quad 0x0000000000000000	/* 0x5b reserved */

	.quad 0x00cf9a000000ffff	/* 0x60 kernel 4GB code at 0x00000000 */
	.quad 0x00cf92000000ffff	/* 0x68 kernel 4GB data at 0x00000000 */
	.quad 0x00cffa000000ffff	/* 0x73 user 4GB code at 0x00000000 */
	.quad 0x00cff2000000ffff	/* 0x7b user 4GB data at 0x00000000 */

	.quad 0x0000000000000000	/* 0x80 TSS descriptor */
	.quad 0x0000000000000000	/* 0x88 LDT descriptor */

	/* Segments used for calling PnP BIOS */
	.quad 0x00c09a0000000000	/* 0x90 32-bit code */
	.quad 0x00809a0000000000	/* 0x98 16-bit code */
	.quad 0x0080920000000000	/* 0xa0 16-bit data */
	.quad 0x0080920000000000	/* 0xa8 16-bit data */
	.quad 0x0080920000000000	/* 0xb0 16-bit data */
	/*
	 * The APM segments have byte granularity and their bases
	 * and limits are set at run time.
	 */
	.quad 0x00409a0000000000	/* 0xb8 APM CS    code */
	.quad 0x00009a0000000000	/* 0xc0 APM CS 16 code (16 bit) */
	.quad 0x0040920000000000	/* 0xc8 APM DS    data */

	.quad 0x0000000000000000	/* 0xd0 - unused */
	.quad 0x0000000000000000	/* 0xd8 - unused */
	.quad 0x0000000000000000	/* 0xe0 - unused */
	.quad 0x0000000000000000	/* 0xe8 - unused */
	.quad 0x0000000000000000	/* 0xf0 - unused */
	.quad 0x0000000000000000	/* 0xf8 - GDT entry 31: double-fault TSS */

对于表1所对应的段选择符定义在include/asm-i386/segment.h,我们可以参照cpu_gdt_table来求出对应的索引值。

#define GDT_ENTRY_DEFAULT_USER_CS	14
#define __USER_CS (GDT_ENTRY_DEFAULT_USER_CS * 8 + 3)

#define GDT_ENTRY_DEFAULT_USER_DS	15
#define __USER_DS (GDT_ENTRY_DEFAULT_USER_DS * 8 + 3)

#define GDT_ENTRY_KERNEL_BASE	12

#define GDT_ENTRY_KERNEL_CS		(GDT_ENTRY_KERNEL_BASE + 0)
#define __KERNEL_CS (GDT_ENTRY_KERNEL_CS * 8)

#define GDT_ENTRY_KERNEL_DS		(GDT_ENTRY_KERNEL_BASE + 1)
#define __KERNEL_DS (GDT_ENTRY_KERNEL_DS * 8)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值