【linux学习笔记】内存管理

本部分是本人学习linux的学习笔记,本人系菜鸟一枚,如有错误欢迎指正。个人邮箱:yaoxitong123@hotmail.com

ARM架构中的MMU

MMU是内存管理单元,主要负责物理地址与虚拟地址之间相互转化、并管理内存访问的管理权限。
这样一个场景:内核想要对一个地址进行访问,并读取其中数据。

  1. MMU查找TLB,是否有虚拟地址和物理地址对应关系的缓存;如果有,查找cache中是否缓存了这个物理地址的内容;如果有直接返回,如果没有,再进行IO操作读取这个内存,读取完之后需要将这一页的内容缓存在cache中。
  2. 如果TLB中没有缓存这个对应关系怎么办?需要执行TTW进行三级页表转换找到虚拟地址对应的物理地址,并将这个对应关系缓存到TLB当中,随后的操作与上面一致。
  • 页(page)
    内存管理的基本单位。这和体系结构中MMU有关。32位的体系结构支持4K的页。struct page用于管理一个物理页,表示这个页是不是“脏”、被引用的次数、映射的虚拟地址等信息。

  • 区(zone)
    内核采用区对页进行分组。主要是解决以下两个问题:1.一些硬件只能采用特定的内存执行DMA操作。2.一些体系结构的物理内存寻址范围大于虚拟内存的寻址范围,所以并不是所有的页都能映射到内核空间。
    ZONE_DMA
    ZONE_NORMAL
    ZONE_HIGHMEM:这部分的映射就取决于体系结构。
    这里可以看出,在内存管理上ucosii果然与linux是相似的,都是采用这种内存池的模式:linux是区-页,而ucos采用的是区-块,这样做的一大好处显然是可以防止内存碎片。

内存申请函数接口:
  • 按照页申请alloc_page(flag,order)等函数接口。这个函数接口就和ucosii的内存申请接口存在很大的相似性。
  • 按照字节申请:kmalloc(字节数,flag) – kfree(ptr)
    关键就是这个flag,这个flag为int类型,有三类:行为修饰符,区修饰、类型修饰符,常用的是类型修饰符,它是前面二者的集合。
    GFP_KERNEL 用于内核中,可以休眠的上下文(所谓进程中不能休眠/阻塞的部分,指的是中断处理程序(不具备重新调度的可能)、持有自旋锁、软中断、tasklet等)。由于可以睡眠而且对内存申请方式没有过多的限制,所以比较容易申请到内存。
    GFP_ATOMIC 与上部分相反,申请过程中不能发生睡眠/阻塞。因此可以用于那些不允许休眠的位置,如上。因为不允许休眠,则获得内存的可能性较小。
  • vmalloc(字节数) – vfree
    与kmalloc不同的是,kmalloc申请的物理内存时连续的,虚拟内存也是连续的;但是vmalloc申请的虚拟内存保证连续,但是物理内存并不能保证连续。在内核中最常用的是是kmalloc,因为如果采用后者,需要将不连续的物理页通过修正页表的方式重新映射成连续的地址,浪费时间,所以只有在需要大块内存的时候才会使用这个函数。如动态的安装一个模块的时候。
slab层

大规模的分配和撤销很多大的数据结构。

linux中内存的分配和施放采用和ucosii类似的方式,可以理解为一种高级的空闲链表的模式。将使用过后的内存放入空闲链表中,下次申请内存中从空闲链表中已经有的块中获取,可以有效的避免内存碎片出现,并提高内存申请额速度。缺陷:空闲链表不能全局控制,内核无法通知每一个空闲链表。

linux内核中,将不同的对象划分成不同的高速缓存区(所谓的对象就是不同类型的数据结构,比如进程描述符task_struct这样的结构体),高速缓存区又被划分为多个slab(slab层的由来),每个slab都管理着至少一页内存。slab分为三种状态:满、部分满、空。当内核中需要一个新的对象的时候,优先从部分满的slab中获取空间,其次是空slab。如果这个对象不再需要的时候,就将这个对象标记为空闲,可以继续使用。可以发现,这样的做法可以有效地避免频繁的申请和施放空间。

slab层只有在既没有部分满也没有空的slab的情况下才会申请空间;只有在以下两种情况下才会施放内存:1)内核内存紧张,需要更多内存使用;2)高速缓存区显示销毁的时候。

高速缓存创建:kmem_cache_create()
撤销高速缓存:kmem_cache_destroy()

获取slab中的对象:kmem_cache_alloc()
释放对象回到原slab中:kmem_cache_free()

当然和ucos一样,linux内核还保留了内存池,结构体是mempool_t。

在栈上静态分配

32bit–2页–8k
64bit–2页–16k

  • 单页内核栈
    在2.6之后的内核中,出现了单页内核栈。每个进程可以选择栈为1页而不是两页。因为随着代码的运行,连续的两个页越来越难找。当选择单页栈之后,中断处理函数就不用再与被中断的进程使用一个栈了。

  • 节约的使用内核栈
    在栈空间中定义的局部变量尽可能不要超过百字节。因为栈溢出没有警报,这就可能淹没栈末端一些重要数据,引发严重问题。

IO内存与IO端口

linux内核可以运行于ARM架构芯片上也可以运用于X86架构芯片上。

  • 对于X86架构芯片会将寄存器映射到IO空间,此时称这些寄存器位于IO端口,就像之前汇编里学过的,操作这些寄存器需要IN/OUT指令。
  • 对于ARM架构,寄存器参与了地址的编排,所以这些寄存器被称为IO内存,可以直接通过指针进行访问。在使用IO内存的时候需要将其映射为虚拟地址,API:ioremap(原寄存器的物理地址,要映射地址的长度)。使用完之后需要free掉iounmap(映射的虚拟地址)

linux中有一组API用于申请IO内存,意义主要在于确认这部分寄存器是否可用,保证安全。

request_mem_region(start,n,name)
release_mem_region(start,n)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值