内存和I/O的访问
这一篇内容的起始,和之前写的汇编专辑里的硬盘访问部分联动了,之前我们访问硬盘的时候,就是通过汇编语言的in和out指令
去从配置硬盘的I/O端口中的某些端口寄存器,再从一些寄存器中读取硬盘里的内容。
一些计算机系统中I/O端口号是映射到内存地址空间的,另一些计算机系统中端口是独立编址的,比如X86处理器中存在着I/O空间的概念,I/O空间是相对于内存空间而言,I/O空间和内存空间并行地接到处理器的地址线上,通过一根M/IO#控制引脚去指定控制访问mem space or I/O space.
I/O空间独立编址的处理器上,一般需要特殊的指令去访问他们,如Intel8086:
;目的寄存器必须使用ax或al 源端口号放在dx中 或者用立即数表示出来
in al,dx ;访问8位端口
in ax,dx ;访问16位端口
out dx,al
out dx,ax
在I/O空间是被映射到内存空间的处理器上,可以直接通过C语言指针操作
typedef void (*lpFunction) ();
lpFunction lpReset = (lpFunction)0xF000FFF0;
lpReset();
内存管理单元
Memory Map Unit简称MMU,他有如下功能:
辅助操作系统进行内存管理
提供虚拟地址和物理地址的映射
内存访问权限保护
Cache缓存控制(关于Cache又是一个很大的话题,以后再聊,DMA也可以做到CPU的 Cache中去做事)
MMU工作的基本过程
在启动了MMU的Linux内核中,CPU是通过虚拟地址来访问物理内存的。虚拟地址和物理地址之间的关系kernel会预先构建在一张表中,放在内存中,这张表有很多页面,每页有若干行,每一行都记录了类似"虚拟地址0x10000000对应的物理地址是0x21112001"这样的信息。
每一页都有其页号,每一行有其行号。
处理器在访问内存时访问的是虚拟地址,这个虚拟地址拿到MMU那里,先告诉它这个页表的起始地址被存在内存的哪里,然后它会拿着你给他的虚拟地址去查询页表,找到物理地址。
页表并不是每一页都是可访问的,都是有权限的,有些页是只读的,如果访问到他们就会产生page fault错误,导致应用程序段错误(segment default),也就是访问权限冲突了(access violation)
每个进程都有自己的虚拟地址空间,也就有了自己独立的页表。
进程切换时就要通知MMU这个进程的页表起始地址变了,要做更换。
比如,ARM中页表的起始地址存放在Translation table base(TTB)寄存器中。
进程切换时通过$(CPU_NAME)_switch_mm(),如cpu_v7_switch_mm来更新页表的base pointer,实际上就是协处理器的操作。
MMU每次访问内存都要查表寻址是很慢的,页表也是有高速缓存的,来改善虚拟地址到物理地址的转换速度。
这个高速缓存就是Translation Look-Aside Buffers(TLB),它是一个硬件单元,查找虚拟地址到物理地址的映射关系的时候会首先尝试在TLB中命中 ,如果命中那就省去了到内存中的页表中查表的步骤了。(如果能找到就是命中,没找到就是缺失)
TLB存放频繁地被查询的页表项,算是种类似Cache的机制。
MMU在TLB中命中不了那就只能去内存中查找,这个页表的名字也可以做一个命名,叫做Translation Table Walk(TTW)
即转换表漫游, TTW查询到后会记录到TLB中,后面再访问相同地址就可以到TLB中快速查找
ARM内TLB条目中的控制信息用于控制对对应地址的访问权限以及Cache的操作。
·C(高速缓存) 和B(缓冲) 位被用来控制对应地址的高速缓存和写缓冲, 并决定是否进行高速缓
存。
·访问权限和域位用来控制读写访问是否被允许。 如果不允许, MMU则向ARM处理器发送一个存储
器异常, 否则访问将被允许进行。
Linux系统中进程的4GB空间被分为两个部分 — 用户空间和内核空间
用户空间地址一般再0~3GB,而3 ~ 4GB为内核空间,这个部分无法被用户直接访问,除非System Call进入系统调用进入内核态运行
内核空间是由内核负责映射,不跟着进程改变。用户空间会随着进程的切换而变换。
虽然只有这一个G,但还是分了好几个区域:内存映射区,虚拟内存分配区,高端页面映射区,专用页面映射区和系统保留区