32位Linux系统的虚拟地址映射 <一>
2017-11-22 17:41
197 查看
在分析地址映射过程前,先描述几个概念:
逻辑地址: 在机器语言指令中,说明操作数和指令的地址;每个逻辑地址包含段(Segment)和偏移量(Offset)两部分。
线性地址:也称为虚拟地址,在32位系统中,它是32位的无符号整型,最大可达到4G。
物理地址:就是真正物理内存上的地址
CPU位数:指CPU的ALU的宽度(ALU从寄存器取数据,寄存器从总线取数据)
也是数据总线的条数
寄存器:暂存指令,数据和位址。
CS:代码段寄存器
DS:数据段寄存器
SS:堆栈段寄存器
ES:附加段寄存器
总线包括:
地址总线:内存中定位地址
数据总线:内存中获取数据
控制总线:发0/1信号
物理内存:一般是16的倍数,范围是:16~2^16,(16~64K)
DS<<4 + IP = 数据的地址
段基址 ==》 偏移量/偏移地址/逻辑地址(一个内存段上的偏移量)
一,实模式(实地址模式)
实模式下最大物理内存:2^20 = 1M,会强制进入实模式1M。
Linux内核image(镜像)一般会从0x100000(1M)地址处开始加载,虚拟地址在0xC010000开始加载。为什么呢?
因为实模式下占用内存是1M,在这1M中包含显卡缓存,软件驱动代码等。
当我们访问一块代码时
CPU从80386开始,增加了寄存器
GDTR(全局的段描述符表寄存器)
所有进程所共享的
LDTR (局部的段描述符表寄存器)
每个进程所私有的
首先,先从GDTR来看,
在Linux内核,gdt_table段描述表中有12项被系统使用,所以剩下8192-12 = 8180留给用户使用。
下边是段寄存器定义,这里是在80386下讨论的。
Linux内核给每一个进程的运行都会分配一个虚拟地址空间4G(2^32),其中包含3G的用户空间和1G的内核空间。
接下来是段描述表项的定义
CR0 最高PG位, 0 / 1 (PG内存是否开启分页)0开启,1未开启
CR2
保存发生缺页异常的虚拟地址
CR3
保存当前进程页目录的起始地址
CR4 PAE位是否开启物理地址扩展 0 / 1
二,保护模式
1.保护模式下内存分段的地址映射 IP/PC
举个例子,有一个线性地址( 虚拟地址)为0x0018ff44,可以先尝试进行转换
在32位下,一般分为10 ,10,12
物理页面的起始地址在页表中以下标存储,低12位为0.因为一个物理页面的大小是4K,物理地址是4K的倍数,所以低12位是0,我们一般表示物理地址省略低12位,类似于下标。如0x00002000,一般写作0x00002。
物理页面在内存中以数组方式管理 (动态数组)
刚才提到,在CR3寄存器中可以找到当前进程页目录的起始地址。
这里提个问题,页目录和页表是每个进程都有一份还是所有进程共享呢?
每个进程都有自己一份的页目录和页表,有独立的地址空间。如果共享的话,就访问同一个物理页面,是不能的。(如下图,Linux进程的虚拟存储器)
在虚拟地址空间上,地址连续;经映射后,物理地址不一定连续。
2.在Linux源代码中,进程调度函数schedule()函数中,有一个进程切换函数switch_mm()函数,每一个进程有自己的地址空间,所以进程切换会进行切换地址空间。
在switch_mm()函数中有一个执行语句
load_cr3(next->pgd);
pgd存放的是当前页目录地址,这里是将页目录地址放在CR3寄存器中。CPU在执行某一个进程时,进行地址映射时默认CR3寄存器放的是当前页目录地址。所以在进程切换时,要将下一个要调度进程的页目录地址放在CR3寄存器中。
在Page Table Entry中,高20位是value(value是物理页面在磁盘swap分区中的位置),最低一位是present,
引起缺页异常的原因如下:
当value==0,present==0时,说明PTE对应的物理页面没有分配过;
当value!=0,present==0时,说明PTE对应的物理页面在交换分区当中;
当value!=0,present!=0时,说明PTE对应的物理页面是一个活动的页面。
3.缺页
缺页:DRAM缓存不命中。
缺页分三种情况:
(1)正常缺页:缺页异常会调用内核中的缺页异常处理程序,该程序会选择一个牺牲页。如果这个牺牲页被修改过,那么就将它交换出去,换入新的页面并更新页表。当缺页处理程序返回时,CPU重新引起缺页的指令,这条指令再次将虚拟地址发送到MMU。MMU就能正常翻译该虚拟地址,而不会再产生缺页中断。
(2)段错误:访问一个不存在的页面。
(3)保护异常:违反许可。例如,写一个只读的页面;进程是否由读写或执行这个区域内页面的权限;
或者是一个运行在用户模式中进程试图从内核虚拟存储器中读取字。
4.虚拟寻址
地址翻译(地址映射)需要CPU硬件和操作系统之间紧密合作。
硬件是MMU(内存管理单元),利用存放在主存中的查询表来动态翻译虚拟地址,该表的内容由OS管理;
软件有进程页目录,页表,CR3寄存器,全局段描述符表;
映射由内核提供。
补充:
交换分区,是指磁盘空间,根据LRU最近最久未使用算法,选择一个页面交换出去,只有脏页才会被交换
脏页,就是被修改过数据的页。在PTE中低12位中,会有一个duty位,标记是否被修改过。 没有被修改过的页面不会交换,会被新的页面覆盖。
DRAM缓存(动态随机存储器):虚拟存储器的缓存,再主存中缓存虚拟页。
SRAM缓存(静态随机存储器):位于CPU和主存之间的f1,f2,f3高速缓存。
SRAM比DRAM快大约10倍,而DRAM比磁盘快100 000多倍。 DRAM对磁盘的访问时间较长,所以总是使用写回,而不是直写。
虚拟内存:<1>将主存看成是一个存储在磁盘上的地址空间的高速缓存;
<2>为每一个进程提供一致的地址空间;
<3>保护了每个进程的地址空间不被其他进程破坏。
虚拟地址空间:在一个带虚拟存储器的系统中,CPU从一个由2^n个地址地址空间中生成虚拟地址。
参考资料:《深入理解计算机系统》
逻辑地址: 在机器语言指令中,说明操作数和指令的地址;每个逻辑地址包含段(Segment)和偏移量(Offset)两部分。
线性地址:也称为虚拟地址,在32位系统中,它是32位的无符号整型,最大可达到4G。
物理地址:就是真正物理内存上的地址
CPU位数:指CPU的ALU的宽度(ALU从寄存器取数据,寄存器从总线取数据)
也是数据总线的条数
寄存器:暂存指令,数据和位址。
CS:代码段寄存器
DS:数据段寄存器
SS:堆栈段寄存器
ES:附加段寄存器
总线包括:
地址总线:内存中定位地址
数据总线:内存中获取数据
控制总线:发0/1信号
物理内存:一般是16的倍数,范围是:16~2^16,(16~64K)
DS<<4 + IP = 数据的地址
段基址 ==》 偏移量/偏移地址/逻辑地址(一个内存段上的偏移量)
一,实模式(实地址模式)
实模式下最大物理内存:2^20 = 1M,会强制进入实模式1M。
Linux内核image(镜像)一般会从0x100000(1M)地址处开始加载,虚拟地址在0xC010000开始加载。为什么呢?
因为实模式下占用内存是1M,在这1M中包含显卡缓存,软件驱动代码等。
当我们访问一块代码时
CPU从80386开始,增加了寄存器
GDTR(全局的段描述符表寄存器)
所有进程所共享的
LDTR (局部的段描述符表寄存器)
每个进程所私有的
首先,先从GDTR来看,
在Linux内核,gdt_table段描述表中有12项被系统使用,所以剩下8192-12 = 8180留给用户使用。
下边是段寄存器定义,这里是在80386下讨论的。
Linux内核给每一个进程的运行都会分配一个虚拟地址空间4G(2^32),其中包含3G的用户空间和1G的内核空间。
接下来是段描述表项的定义
CR0 最高PG位, 0 / 1 (PG内存是否开启分页)0开启,1未开启
CR2
保存发生缺页异常的虚拟地址
CR3
保存当前进程页目录的起始地址
CR4 PAE位是否开启物理地址扩展 0 / 1
二,保护模式
1.保护模式下内存分段的地址映射 IP/PC
举个例子,有一个线性地址( 虚拟地址)为0x0018ff44,可以先尝试进行转换
在32位下,一般分为10 ,10,12
物理页面的起始地址在页表中以下标存储,低12位为0.因为一个物理页面的大小是4K,物理地址是4K的倍数,所以低12位是0,我们一般表示物理地址省略低12位,类似于下标。如0x00002000,一般写作0x00002。
物理页面在内存中以数组方式管理 (动态数组)
struct page { page_flags_t flags; /*一组标志,也对页框所在的管理区进行编号*/ atomic_t _count; /* Usage count, see below. */ /** 页框中的页表项数目(没有则为-1) * -1: 表示没有页表项引用该页框。 * 0: 表明页是非共享的。 * >0: 表示而是共享共享的。 */ atomic_t _mapcount; /* Count of ptes mapped in mms, to show when page is mapped * & limit reverse map searches.*/ unsigned long private; /* Mapping-private opaque data */ struct address_space *mapping; pgoff_t index; /* Our offset within mapping. */ struct list_head lru; /**如果进行了内存映射,就是虚拟地址。对存在高端内存的系统来说有意义。*/ void *virtual; /* Kernel virtual address */ };
刚才提到,在CR3寄存器中可以找到当前进程页目录的起始地址。
这里提个问题,页目录和页表是每个进程都有一份还是所有进程共享呢?
每个进程都有自己一份的页目录和页表,有独立的地址空间。如果共享的话,就访问同一个物理页面,是不能的。(如下图,Linux进程的虚拟存储器)
在虚拟地址空间上,地址连续;经映射后,物理地址不一定连续。
2.在Linux源代码中,进程调度函数schedule()函数中,有一个进程切换函数switch_mm()函数,每一个进程有自己的地址空间,所以进程切换会进行切换地址空间。
static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, struct task_struct *tsk) { int cpu = smp_processor_id(); if (likely(prev != next)) { /* stop flush ipis for the previous mm */ cpu_clear(cpu, prev->cpu_vm_mask); #ifdef CONFIG_SMP per_cpu(cpu_tlbstate, cpu).state = TLBSTATE_OK; per_cpu(cpu_tlbstate, cpu).active_mm = next; #endif cpu_set(cpu, next->cpu_vm_mask); /* Re-load page tables */ load_cr3(next->pgd); //将下一个要调度进程的页目录地址放在cr3中 /* * load the LDT, if the LDT is different: */ if (unlikely(prev->context.ldt != next->context.ldt)) load_LDT_nolock(&next->context, cpu); }
在switch_mm()函数中有一个执行语句
load_cr3(next->pgd);
pgd存放的是当前页目录地址,这里是将页目录地址放在CR3寄存器中。CPU在执行某一个进程时,进行地址映射时默认CR3寄存器放的是当前页目录地址。所以在进程切换时,要将下一个要调度进程的页目录地址放在CR3寄存器中。
在Page Table Entry中,高20位是value(value是物理页面在磁盘swap分区中的位置),最低一位是present,
引起缺页异常的原因如下:
当value==0,present==0时,说明PTE对应的物理页面没有分配过;
当value!=0,present==0时,说明PTE对应的物理页面在交换分区当中;
当value!=0,present!=0时,说明PTE对应的物理页面是一个活动的页面。
3.缺页
缺页:DRAM缓存不命中。
缺页分三种情况:
(1)正常缺页:缺页异常会调用内核中的缺页异常处理程序,该程序会选择一个牺牲页。如果这个牺牲页被修改过,那么就将它交换出去,换入新的页面并更新页表。当缺页处理程序返回时,CPU重新引起缺页的指令,这条指令再次将虚拟地址发送到MMU。MMU就能正常翻译该虚拟地址,而不会再产生缺页中断。
(2)段错误:访问一个不存在的页面。
(3)保护异常:违反许可。例如,写一个只读的页面;进程是否由读写或执行这个区域内页面的权限;
或者是一个运行在用户模式中进程试图从内核虚拟存储器中读取字。
4.虚拟寻址
地址翻译(地址映射)需要CPU硬件和操作系统之间紧密合作。
硬件是MMU(内存管理单元),利用存放在主存中的查询表来动态翻译虚拟地址,该表的内容由OS管理;
软件有进程页目录,页表,CR3寄存器,全局段描述符表;
映射由内核提供。
补充:
交换分区,是指磁盘空间,根据LRU最近最久未使用算法,选择一个页面交换出去,只有脏页才会被交换
脏页,就是被修改过数据的页。在PTE中低12位中,会有一个duty位,标记是否被修改过。 没有被修改过的页面不会交换,会被新的页面覆盖。
DRAM缓存(动态随机存储器):虚拟存储器的缓存,再主存中缓存虚拟页。
SRAM缓存(静态随机存储器):位于CPU和主存之间的f1,f2,f3高速缓存。
SRAM比DRAM快大约10倍,而DRAM比磁盘快100 000多倍。 DRAM对磁盘的访问时间较长,所以总是使用写回,而不是直写。
虚拟内存:<1>将主存看成是一个存储在磁盘上的地址空间的高速缓存;
<2>为每一个进程提供一致的地址空间;
<3>保护了每个进程的地址空间不被其他进程破坏。
虚拟地址空间:在一个带虚拟存储器的系统中,CPU从一个由2^n个地址地址空间中生成虚拟地址。
参考资料:《深入理解计算机系统》
相关文章推荐
- 32位Linux系统的虚拟地址映射 <二>
- Linux虚拟文件系统(内核初始化<一>)
- IA32体系32位Linux系统虚拟地址映射上
- IA32体系32位Linux系统虚拟地址映射中
- IA32体系32位Linux系统虚拟地址映射下
- C++_IA32体系linux系统虚拟地址映射
- Linux虚拟文件系统(内核初始化<二>)
- linux启动流程导读(arm为例)<一>
- linux系统学习第十三天-<<工程师技术>>
- Linux下的lds链接脚本<一>
- 计算机系统学习笔记<一>
- linux设备驱动之led子系统<一>
- Linux系统--进程地址空间之虚拟地址空间
- Linux 入门常用命令<一>
- Linux下多播编程<一>【十全十美】
- 关于Linux系统的硬链接<百科>
- js获取系统时间的几种方法<一> 《网摘学习》
- linux启动流程导读(arm为例)<一>
- <一> Linux是什么
- <<Linux内核设计与实现>>读书笔际(五)-系统调用