内存映射相关学习
以前看过一些内存映射相关的文章,最近看到相关的知识,发现一时没办法全部提起来。这个忘性!!!
看样子不写点东西出来,是没办法克服忘性了。
内存映射,其实就是建立物理内存与虚拟内存之间的关系。
物理内存就不用说了,就是实实在在的东东,看得见,摸得着。
虚拟内存是嘛呢?
看名字就知道,是“虚”的,看不见,摸不着。
其实就是操作系统给进程的一个空间,告诉进程有这么多“内存”,需要的话可以分配。
当然,这些内存是“虚”的,不一定真正存在这么多内存,但是确实存在这么多的内存地址空间。
在存在MMU的系统中,无论用户态还是内核态,在访问内存时都必须使用虚拟地址。
因为存在MMU的话,访问内存都会经过MMU,MMU认为告诉它的地址都是虚拟地址。
如果没有操作系统呢?更细一点,如果没有MMU呢?
那也就没有东东来把内存做“虚”了,进程看到的就是实实在在的物理内存,能访问到的内存空间也就是实实在在的物理内存空间。
物理内存空间有多大,这个比较好知道,你装了多大的内存,地址空间就有多大。
那么,虚拟地址空间有多大呢?
这跟cpu的指令宽度有关。
32位cup的话,虚拟地址空间为2^32 bytes,也就是4G Bytes。
64位cpu的话,虚拟地址空间为2^64 bytes,也就是...个天文数字,呵呵,不管它了.
32位系统中,4G的虚拟地址空间怎么分配呢?
所谓的分配,就是划一部分给内核用,剩下的给用户空间用,让两者别打架。
内核空间与用户空间的虚拟地址空间比例一般为1:3或者2:2,这个是可以配置的。
我们以1:3的模式来分析,看物理内存是怎么映射的。
说的内存映射,经常会看到一个图。本来想贴过来,可惜我是在文本文件中编辑的。
没办法,只能依图画“图”,将就着看吧。
___________________________________________________________
| 用户空间 | 直接映射区 |...| vmalloc 区 |...| 永久映射区 | 固定映射区 |
|_________|___________|__|__________|__|___________|___________|
虚拟内存的组成部分,基本如上所示。
不过,图中省略了一些gap,即为了防止内存越界访问,留下的一些空洞。
下面看看各个部分都是干嘛的。
用户空间:
就不多说了,这部分地址不会与内存建立直接映射关系。
直接映射区:
看名字就知道,是直接映射过来的。
也就是说,是物理内存一整块,啪!映射到了虚拟地址空间,1对应1,2对应2,当然,要加上开始的偏移量。
例如,物理地址0x123,对应过来就是 PAGE_OFFSET + 0x123,PAGE_OFFSET 就是直接映射区起始点的虚拟地址。
这部分区域的内存可以通过kmalloc分配,通过kfree释放。
通过kmalloc分配得到的内存,在虚拟地址空间连续,在物理内存空间也连续。
注意,kmalloc分配得到的也是虚拟地址,代码中可以访问的只有虚拟地址,因为有MMU存在。
这部分区间有多大呢?
以32位系统为例,在1:3的情况下,即内核区有1G的虚拟空间,减去128M用于高端映射的区域,剩下896M.
也就是说这种情况下,直接映射区的为896M.
如果实际物理内存小于896M,都可以映射到直接映射区。
如果大于896M呢?那也不能浪费,将剩下的内存划到HIGH_MEMORY zone,通过高端映射区,即vmalloc 区、永久映射区、固定映射区,可以访问HIGH_MEMORY zone。
如果32位系统2:2的情况下,内核空间就有2G的虚拟空间,此时直接映射区的大小为:2G - 128M = 1920M。
这种情况下,物理内存小于1920M时,都可以直接映射,只有大于1920M时,才需要使用HIGH_MEMORY。
这部分内存的特点是访问快,因为映射方法简单,只是加上个偏移;缺点是资源有限,大家都想用,久而久之,就被瓜分的乱七八糟,剩下的就不多了,连续的更少。
这个时候,如何还需要虚拟地址空间连续的内存怎么办呢?
vmalloc就派上了用场。
vmalloc 区:
在需要虚拟地址连续,并不要求物理地址连续时,vmalloc 区就派上用场了。
通过函数vmalloc,可以从该区域分配内存。
由于该区域并不直接映射到物理内存,当收到vmalloc请求时,会依次尝试从HIGH, NORNAL, DMA中分配物理内存,并与vmalloc区的虚拟地址进行映射。
也就是说,vmalloc不仅会利用HIGH Memory,也会利用NORMAL Memory和DMA。
但vmalloc要求不高,在kmalloc失败的情况下,vmalloc是有可能成功的,因为它不要求物理内存连续。
这个区域内存的分配与访问比直接映射区要慢。
永久映射区:
这儿的永久并不是真正的永久,而是指在调用kunmap之前,映射是一直存在的。
这一部分内存的使用方法是,先通过alloc_page获取一个内存页,再通过kmap将这个内存页映射到该区域,并得到映射的虚拟地址,之后通过访问虚拟地址即可以访问物理内存页。
注意,kmap是可能会被卡住的,在该区域的虚拟内存全部被映射晚的话,再调用kmap就会被放置到一个等待队列。
当有虚拟内存空出来了,会被唤醒,并完成映射。
所以,中断中是万万用不得kmap的。
那么中断中该用什么呢?固定映射区。
固定映射区:
也叫临时映射区。怎么感觉固定和临时联系不起来?
原来所指的不是一个意思。
固定,是指这个区域已经划分好了,名字都起好了(enum km_type)。
临时,是指不会阻塞,需要映射即映射,有可能会把之前的映射覆盖掉。
原来临时的意思是只能临时用用,之后想再用,就要再映射,因为原来的映射可能已经被覆盖了。
相关函数:kmap_atomic/kunmap_atomic