linux 内核地址空间

Linux中的内核地址空间

一、内核镜像

在3GB~(3GB+896MB)这段直接/线性映射区域,包含了内核初始化页表swapper_pg_dir,内核镜像等。内核也是由一个elf文件(比如vmlinux)加载启动的,加载后也有text段,data段,bss段等。

 

二、内存分配

kmalloc和vmalloc

在虚拟内存空间的normal memory区域,内核使用kmalloc()来分配内存,kmalloc()返回的也是虚拟地址,但是分到的内存在物理地址上是连续的(因为是直接映射,在虚拟地址上自然也是连续的)。

在VMALLOC_START和VMALLOC_END之间的区域为vmolloc area,它和normal memory中有8MB的间隔。这部分间隔不作任何地址映射,相当于一个空洞,主要用做安全保护,防止不正确的越界内存访问,因为此处没有进行任何形式的映射,如果进入到空洞地带,将会触发处理器产生一个异常。

preview

在vmolloc area中使用vmalloc()分配内存,具体的分配过程是:

  1. 根据要分配的内存大小,调用get_vm_area( ),获取vmlist_lock锁以扫描vmlist链表,在vmolloc area中找到一块大小满足要求的空闲内存;
  2. 调用__vmalloc_area_pages() --> alloc_page(),通过内核的buddy系统获得相应大小的物理页面,关于物理页面的分配请参考这篇文章
  3. vmalloc area中的地址映射不再是简单的3GB偏移,因此需要调用map_vm_area(),建立虚拟地址和物理页面的映射关系,并添加到内核页表中。

同kmalloc()相比,vmalloc()分配的内存只能保证在虚拟地址上连续,不能保证在物理地址上连续。在物理地址上连续有什么好处呢?

  • 可以更好的根据空间局部性原理利用cache,增加数据访问的速度。
  • 由于kmalloc()基于的是直接映射,其虚拟地址和物理地址之间是一个固定的偏移,因此可以利用既有的内核页表,而不需要为新的地址增加新的page table entries,因此其分配速度也比vmalloc()更快
  • 因为物理地址不连续,通过vmalloc()获得的每个page需要单独映射,而TLB资源很有限,因此这将比直接映射造成更严重的TLB thrashing问题。

连续的物理内存和简单的直接映射关系谁不想要啊,可是如果系统运行久了,内存碎片就多起来,想要找到一块物理上连续的大块内存就越来越困难,这时就只能靠vmalloc()出马了。因为有一些应用场景是需要物理上连续的内存的(比如硬件设备),那是不是如果是没有这个要求的,就用vmalloc()就好了,把宝贵的normal area的地址资源留给那些真正需要的同志呢?

这种做法也有问题,如果大家都谦让着不用kmalloc(),那可能normal area就不能被充分利用起来。公平和效率始终是需要兼顾和平衡的,在内核的实际应用中,如果不是分配大块内存,还是推荐使用更高效的kmalloc()。

vmalloc区域

还记得上篇文章举的那个房间和钥匙的例子么,用户空间的进程通过malloc()分配内存时,获得的只是虚拟地址的使用权,要等到真正往这块内存写数据了,才会获得对应的物理页面,而且是用多少给多少,而不是要多少给多少。内核空间自己的vmalloc()就不一样了,申请的物理内存立刻满足,房间钥匙一起给,在上级单位干活就是不一样啊。

分配到的每个内存区域(以下称vmalloc区域)都用一个vm_struct结构体表示,对这个名字有没有一点眼熟?跟进程地址空间里的vm_area_struct很像是吧,事实上,它们不光是名字相似,组织方式也有类似的地方,vm_struct也是通过一个叫vmlist的单项链表串起来的。

同样都是为了描述一段内存区域,包括这段区域的地址,大小,属性等,那这里可以直接用vm_area_struct吗?可以倒是可以,但是vmalloc区域相对要简单一些,用vm_area_struct来表达就显得复杂了,所以单独有了一个vm_struct,来看下这个数据结构的定义:

二、GFP 掩码

  1. GFP_ATOMIC:用来从中断处理和进程上下文之外的其他代码中分配内存。从不睡眠。可以用在具有原子性的地方,分配函数。
  2. GFP_KERNEL:内核内存的正常分配。可能睡眠。在具有原子性的地方,不能用。
  3. GFP_USER:用来为用户空间页来分配内存; 它可能睡眠。在具有原子性的地方,不能用。
  4. GFP_NOIO、GFP_NOFS:这个标志功能如同 GFP_KERNEL, 但是它们增加限制到内核能做的来满足请求. 一个 GFP_NOFS 分配不允许进行任何文件系统调用, 而 GFP_NOIO 根本不允许任何I/O 初始化. 它们主要地用在文件系统和虚拟内存代码, 那里允许一个分配睡眠,但是递归的文件系统调用会是一个坏注意.

一般用得比较多的是GFP_KERNEL,GFP_ATOMIC

三、V区虚拟地址与物理地址的关系

 

 动态内存映射区:动态内存映射区的起始地址要根据物理内存的大小决定,
        如果物理内存大于896m,起始地址就是0xC0000000+896,

        如果小于896M,起始地址就是0xC0000000 + 物理内存的大小

        虚拟上连续,物理上不一定连续!

低于896M的物理内存与虚拟内存在内核空间里是一一对应的,这句话是有问题的,需要分情况说。

如果总得物理内存低于896M,假设为A,对于内核中的一个地址,如果小于A那么这个地址与物理地址只差一个偏移量,如果大于A,那么肯定位于VMALLOC区。如果一个物理地址为X的页被内存使用,即使X小于896,它对应的虚拟地址也有可能不是3G+896。

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值