内存管理
- 内存管理
- 页
- 区
- 获得页
- 获得填充为0的页
- 释放页
- kmalloc()
- kfree()
- vmalloc()
- slab层
- 在栈上的静态分配
- 单页内核栈
- 在栈上光明正大的工作
本系列博客追寻《Linux内核设计与实现-Robert Love》,各个Linux机中的内核源代码不一,因此直接下载官网内核源码
参考书籍:《Linux内核设计与实现-Robert Love》
内存管理
在内核中分配内存可不像在其他地方分配内存那么容易,从根本上来讲,是因为内核本身不能像用户空间那样奢侈的使用内存
自古以来,得内存者得终极
1、用户空间
应用程序使用malloc()申请内存,使用free()释放内存。malloc(和free()是glibc库的内存分配器ptmalloc是供的接口,ptmalloc使用系统调用brk或mmap向内核以页为单位申请内存,然后划分成很小内存块分配给应用程序。
2、内核空间
内核空间它的基本功能:虚拟内存管理主要负责从进程的虚拟地址空间分配虚拟页,sys_ brk用来扩大或收缩堆,sys_ mmap用来在内存映射区域分配虚拟页, sys_ munmap用来释放虚拟页。
内核空间提供把页划分成小内存块分配的块分配器,提供分配内存的接口,支持3个块分配器: slab分配器、slub分配器和slob分配器。
页
内核把物理页作为内存管理的基本单位,内存管理单元(MMU,管理内存并把虚拟地址转换成物理地址的硬件)通常以页为单位进行处理。从虚拟内存的角度来看,页就是最小单位。
内核用struct page结构表示系统中的每个物理页
struct page {
/* flag用来存放页的状态
状态:脏,是否锁定在内存中等等
*/
unsigned long flags;
/* _count存放页的引用计数,当为-1时说明没有引用
*/
atomic_t _count;
atomic_t _mapcount;
unsigned long private;
struct address_space *mapping;
pgoff_t index;
struct list_head lru;
/* virtual页的虚拟地址
*/
void *virtual;
}
区
由于硬件的限制,内核并不能对所有的页一视同仁。有些页位于内存中特定的物理地址上,所以不能将其用于一些特定的任务,所以内核把页划分为不同的区(zone)。
Linux必须处理如下两种由于硬件存在缺陷而引起的内存寻址问题:
- 一些硬件只能用某些特定的内存地址来执行DMA(直接内存访问)
- 一些体系结构的内存的物理寻址范围比虚拟寻址范围大得多,这样,就有一些内存不能永久的映射到内核空间上
因此,Linux使用了四种区
- ZONE_DMA:只能用来执行DMA操作
- ZONE_DMA32:这些页面用来执行32位设备的DMA操作
- ZONE_BOEMAL:这个区包含的都是能正常映射的页
- ZONE_HIGHHEM:这个区包含“高端内存”,其中的页并不能永久的映射到内核地址空间
在x86体系结构上,高于896MB的所有物理内存的范围大都是高端内存,他并不会永久的或自动的映射到内核地址空间
获得页
内核提供了一种请求内存的底层机制,并提供了访问他们的几个接口
获得填充为0的页
让返回的页的内容全为0
unsigned long get_zeroed_page(unsigned int gfp_mask);
释放页
当不在需要页时候释放他们
free_page(unsigned long addr, unsigned int order);
kmalloc()
互动二以字节为单位的一块内核内存
malloc()相比多了一个flags参数
kfree()
释放由kmalloc()分配的内存块
vmalloc()
kmalloc和vmalloc
vmalloc()分配的内存虚拟地址是连续的,而物理地址无需连续
kmalloc()函数确保页在物理地址上也是连续的(虚拟地址上也是连续的)
slab层
分配和释放数据结构是所有内核中最普遍的操作之一,为了百年与数据的频繁分配和回收,编程人员常常会使用到空闲链表。
空闲链表包含可供使用的、已经分配好的数据结构块
当代码需要一个新的数据结构实例时,就可以从空闲链表中抓取一个,而不需要分配内存,再把数据放进去
当不需要这个数据结构的实例时,就把他放回空闲链表,而不是释放他
空闲链表面临的问题之一是不能全局控制,当可用内存变的紧缺时,内核无法通知整个链表,让其收缩缓存的大小以便释放出一些内存来。实际上,内核根本不知道存在任何空闲链表
因此,Linux内核提供了slab层(页也就是所谓的slab分配器)
我们在此不做深入
在栈上的静态分配
在用户空间,不少都可用在栈上发生,用户空间可用负担很大的栈,而且栈空间还可用增长。
但是,内核栈小而且固定
当给每个进程分配一个固定大小的小栈后,不但可用减少内存的消耗,而且内核也无需负担太重的栈管理任务
每个进程的内核栈大小通产是两个页的大小
单页内核栈
单页内核栈,即每个进程的内核栈只有一页大小
原因:
- 让每个进程减少内存消耗
- 随着机器运行时间的增加,寻找两个未分配的、连续的页变得更加困难,物理内存渐渐变成碎片,因此,给一个新进程分配虚拟内存(VM)的压力页在增大
但是中断处理程序页放进内核栈明显怪怪的,因此中断处理程序程序自己的栈了!中断栈!
中断栈为每个进程提供了一个用于处理中断处理程序的栈,有了这个选项
中断处理程序不用再和被中断进程共享一个内核栈,他们可以使用自己的栈
在栈上光明正大的工作
进行动态分配或者选择宕机