Linux内存管理之地址映射
写在前面:由于地址映射涉及到各种寄存器的设置访问,Linux对于不同体系结构处理器的地址映射采用不同的方法,例如对于i386及后来的32位的Intel的处理器在页式映射时采用的是2级页表映射,而对于IA64的处理器则采用3级分页。对于其他类型的处理器,例如MK68000等其他许多处理器,在地址映射时则忽略了段式映射,只是因为Intel的X86系列需要兼容早期的段式映射,才在后来的设计中即使用了段式映射,也采用了页式映射。以后关于Linux的笔记,除特别说明外,均是在i386体系结构之上,笔记中所有源码除特别说明外均摘自linux-2.4.0源码树。
现代操作系统在内存管理上均使用高效的页式管理,Linux也不例外。对于i386处理器则有些例外,为了兼容早期的处理器,Intel强制要求必须先经过段式映射。在地址映射时,虚拟地址被划分成固定的页面大小,由MMU将虚拟地址映射到实际的物理地址。在访问一个虚拟地址表示的内存空间中,CPU必须经过若干次的内存访问才能完成映射,具体访问次数为N+1(N为页表级数),同时还需要N次加法运算。
在Linux进行段式映射和页式映射之前,需要搞清楚X86系列的地址描述方式:
- 逻辑地址:出现在机器指令中,用来制定操作数的地址。
- 线性地址:逻辑地址经过分段单元处理后得到线性地址,这是一个32位的无符号整数,可用于定位4G个存储单元。
- 物理地址:线性地址经过页表查找后得出物理地址,这个地址将被送到地址总线上指示所要访问的物理内存单元。
include/asm-i386/processor.h
#define start_thread(regs, new_eip, new_esp) do { \
__asm__("movl %0,%%fs ; movl %0,%%gs": :"r" (0));\
set_fs(USER_DS); \
regs->xds = __USER_DS; \
regs->xes = __USER_DS; \
regs->xss = __USER_DS; \
regs->xcs = __USER_CS; \
regs->eip = new_eip; \
regs->esp = new_esp; \
} while (0)
从代码可以看出,Linux将i386处理器的DS,ES,SS寄存器均设置为USER_DS,这表示在Linux中对于进程的代码段,数据段和堆栈段是不区分的。__USER_CS和__USER_DS的设置位于include/asm-i386/segment.h
include/asm-i386/segment.h #ifndef _ASM_SEGMENT_H
#define _ASM_SEGMENT_H #define __KERNEL_CS 0x10
#define __KERNEL_DS 0x18 #define __USER_CS 0x23
#define __USER_DS 0x2B #endif
由以上代码可以看出,CS寄存器中的内容是0x23,通过段寄存器各位的含义可知,CPU以4作为下标,从全局描述符表GDT中寻找段描述选项,GDT的内容在arch/i386/kernel/head.S中定义
arch/i386/kernel/head.S ENTRY(gdt_table)
.quad 0x0000000000000000 /* NULL descriptor */
.quad 0x0000000000000000 /* not used */
.quad 0x00cf9a000000ffff /* 0x10 kernel 4GB code at 0x00000000 */
.quad 0x00cf92000000ffff /* 0x18 kernel 4GB data at 0x00000000 */
.quad 0x00cffa000000ffff /* 0x23 user 4GB code at 0x00000000 */
.quad 0x00cff2000000ffff /* 0x2b user 4GB data at 0x00000000 */
.quad 0x0000000000000000 /* not used */
.quad 0x0000000000000000 /* not used */
/*
* The APM segments have byte granularity and their bases
* and limits are set at run time.
*/
.quad 0x0040920000000000 /* 0x40 APM set up for bad BIOS's */
.quad 0x00409a0000000000 /* 0x48 APM CS code */
.quad 0x00009a0000000000 /* 0x50 APM CS 16 code (16 bit) */
.quad 0x0040920000000000 /* 0x58 APM DS data */
.fill NR_CPUS*4,8,0 /* space for TSS's and LDT's */
include/asm-i386/mmu_context.h static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, struct task_struct *tsk, unsigned cpu)
{
if (prev != next) {
/* stop flush ipis for the previous mm */
clear_bit(cpu, &prev->cpu_vm_mask);
/*
* Re-load LDT if necessary
*/
if (prev->context.segments != next->context.segments)
load_LDT(next);
#ifdef CONFIG_SMP
cpu_tlbstate[cpu].state = TLBSTATE_OK;
cpu_tlbstate[cpu].active_mm = next;
#endif
set_bit(cpu, &next->cpu_vm_mask);
/* Re-load page tables */
asm volatile("movl %0,%%cr3": :"r" (__pa(next->pgd)));
}
#ifdef CONFIG_SMP
else {
cpu_tlbstate[cpu].state = TLBSTATE_OK;
if(cpu_tlbstate[cpu].active_mm != next)
BUG();
if(!test_and_set_bit(cpu, &next->cpu_vm_mask)) {
/* We were in lazy tlb mode and leave_mm disabled
* tlb flush IPI delivery. We must flush our tlb.
*/
local_flush_tlb();
}
}
#endif
} #define activate_mm(prev, next) \
switch_mm((prev),(next),NULL,smp_processor_id()) #endif
重点关注其中的asm volatile("movl %0,%%cr3": :"r" (__pa(next->pgd)));它实现的功能即为将页目录指针读入CR3寄存器中。通过线性地址的最高10位可以从页面目录中知道具体的目录项,在找到进程的目录项之后,在目录项中,高20位指向页面表,在得到页面表之后,CPU再从线性地址的中间10位得到页表中的表项。在32位处理器上页表中的高20位指向物理内存的初始地址,在其后添加12个0,然后加上线性地址中的低12位(即为线性地址中的偏移量),这样就得到了一个具体的物理地址了。
在地址映射这个问题上,内核只提供页表,实际的转换是由硬件去完成的。那么内核如何生成这些页表呢?这就有两方面的内容,虚拟地址空间的管理和物理内存的管理。实际上只有用户态的地址映射才需要管理,内核态的地址映射是写死的即为[0xC000 0000] (3 GB)到[0xFFFF FFFF] (4 GB)。在这一部分中,内核要实现的一个重要功能就是通过高速缓存来提高查找速度。
参考资料:
Linux内存管理之地址映射的更多相关文章
- linux内存管理
一.Linux 进程在内存中的数据结构 一个可执行程序在存储(没有调入内存)时分为代码段,数据段,未初始化数据段三部分: 1) 代码段:存放CPU执行的机器指令.通常代码区是共享的,即其它执行程 ...
- Linux内存管理原理
本文以32位机器为准,串讲一些内存管理的知识点. 1. 虚拟地址.物理地址.逻辑地址.线性地址 虚拟地址又叫线性地址.linux没有采用分段机制,所以逻辑地址和虚拟地址(线性地址)(在用户态,内核态逻 ...
- Linux内存管理原理【转】
转自:http://www.cnblogs.com/zhaoyl/p/3695517.html 本文以32位机器为准,串讲一些内存管理的知识点. 1. 虚拟地址.物理地址.逻辑地址.线性地址 虚拟地址 ...
- Windows内存管理和linux内存管理
windows内存管理 windows 内存管理方式主要分为:页式管理,段式管理,段页式管理. 页式管理的基本原理是将各进程的虚拟空间划分为若干个长度相等的页:页式管理把内存空间按照页的大小划分成片或 ...
- Linux内核分析(三)----初识linux内存管理子系统
原文:Linux内核分析(三)----初识linux内存管理子系统 Linux内核分析(三) 昨天我们对内核模块进行了简单的分析,今天为了让我们今后的分析没有太多障碍,我们今天先简单的分析一下linu ...
- Linux内存管理 (1)物理内存初始化
专题:Linux内存管理专题 关键词:用户内核空间划分.Node/Zone/Page.memblock.PGD/PUD/PMD/PTE.lowmem/highmem.ZONE_DMA/ZONE_NOR ...
- Linux内存管理 (2)页表的映射过程
专题:Linux内存管理专题 关键词:swapper_pd_dir.ARM PGD/PTE.Linux PGD/PTE.pgd_offset_k. Linux下的页表映射分为两种,一是Linux自身的 ...
- Linux内存管理 (3)内核内存的布局图
专题:Linux内存管理专题 关键词:内核内存布局图.lowmem线性映射区.kernel image.ZONE_NORMAL.ZONE_HIGHMEM.swapper_pg_dir.fixmap.v ...
- Linux内存管理 (9)mmap(补充)
之前写过一篇简单的介绍mmap()/munmap()的文章<Linux内存管理 (9)mmap>,比较单薄,这里详细的梳理一下. 从常用的使用者角度介绍两个函数的使用:然后重点是分析内核的 ...
随机推荐
- selenium+python笔记4
#!/usr/bin/env python # -*- coding: utf-8 -*- """ @desc: 使用unittest组织用例 ""& ...
- 看懂UML图
看懂UML类图和时序图 这里不会将UML的各种元素都提到,我只想讲讲类图中各个类之间的关系: 能看懂类图中各个类之间的线条.箭头代表什么意思后,也就足够应对 日常的工作和交流: 同时,我们应该能将类图 ...
- 如何处理PHP和MYSQL的并发以及优化
sql优化,数据缓存和页面静态化首先各种优化程序逻辑优化数据库优化硬件横向扩展数据hash.服务器提升性能.表hash.出钱找oraclec出解决方案页面静态化:Php页面静态化有两种,第一,php模 ...
- hashtable 实现
#include <stdlib.h> #include <stdio.h> #include <string.h> typedef struct _hashnod ...
- 循环语句for
循环语句for 格式: for(int i=1/*初始条件*/;i<=100/*循环条件*/;i++/*状态改变*/) { //循环体,执行代码:(break:跳出循环体) } 给出初始条件,先 ...
- mysql 添加字段、删除字段、调整字段顺序 转
ALTER TABLE — 更改表属性添加字段: alter table `user_movement_log`Add column GatewayId int not null default 0 ...
- CentOS命令登录MySQL时,报错ERROR 1045 (28000):
CentOS命令登录MySQL时,报错ERROR 1045 (28000): Access denied for user root@localhost (using password: NO)错误解 ...
- Octopus系列之更新历史记录
更新笔记历史 2015.2.3 更新了产品价格的计算方法 --采用了通用化的一个处理[支持各个国家货币]更新产品价格,增加两组价格:一组用来进行前台的展示:一组用来进行后台的计算更新了产品分类 ...
- 一步一步配置NLB(续)之深入测试
在这里http://blog.csdn.net/haoxiaozigang1/article/details/12198679跟大家分享了NLB配置的过程,下面写一些对NLB不同情况的下测试的结果: ...
- PHP+mysql常用类库
<?php /** * @title: Ekcms mysql类库 * @version: 1.0 * @author: perry <perry@1kyou.com> * @pub ...