内存管理
12.7
12.7.1
中断栈
12.7.2 栈上正大光明工作
尽量动态分配,内核栈有限
12.8 高端内存的映射
12.8.1 永久映射
映射page到内核地址空间
void *kmap(struct page *page)
低端内存:返回虚拟地址
高端内存:建立永久映射,并返回地址
kmap可睡眠
永久映射有限,高端内存不需要时候应该解除映射
void kunmap(struct page *page)
12.8.2 临时映射(原子映射)
创建映射且上下文不能睡眠
void *kmap_atomic(struct page*page, enum km_type type)
type描述了临时映射的目的
不会阻塞
禁止内核抢占,每个映射对每个处理器唯一
void kunmap_atomic(void *kvaddr, enum_type type)
12.9 每个CPU的分配
每个CPU的数据存在一个数组里
按当前处理器号决定这个数组的当前元素
unsigned long my_percpu[NR_CPUS];
int cpu;
cpu = get_cpu();
my_percpu[cpu]++; //具体执行的代码
put_cpu();
不存在并发问题
内核抢占问题:
- 代码被其它处理器抢占,重新调度,CPU变量无效
- 同一个处理器抢占了两个,造成竞争
get_cpu()时禁止内核抢占,put_cpu()时重新激活处理器
12.10 新的每个CPU接口
2.6内核为了方便操作每个CPU数据,引入percpu
<linux/percpu.h>声明了所有接口操作例程
12.10.1 编译时每个CPU数据
DEFINE_PER_CPU(type, name);
为每个处理器创建类型为type,名字为name的变量实例
在别处声明变量:
DECLARE_PER_CPU(type, name);
get_cpu, put_cpu操作变量
get_cpu_var(name)++;
put_cpu_var(name);
也可以
per_cpu(name, cpu)++;
此方法不会禁止内核抢占,也不会提供锁保护
链接程序将他们创捷在一个唯一的可执行段中.data.percpu
12.10.2 运行时的每个CPU数据
内核实现CPU数据动态分配类似kmalloc
void *alloc_percpu(type);
void *__alloc_percpu(size_t size, size_t align);
void free_percpu(const void *);
alloc_percpu:单字节对齐,按照给定类型的自然边界对齐
__alloc_percpu:指定对齐
返回一个指针,用来引用CPU数据
get_cpu_var(ptr);
put_cpu_var(prt);
12.11 使用每个CPU数据的原因
- 减少了数据锁定,不再需要锁(纯粹的编程约定,不存在强制措施)
- 大大减少缓存失效,percpu接口缓存对齐(cache align)所有数据
唯一的安全要求:禁止内核抢占(put, get),不能睡眠
12.12 分配函数的选择
- 常用:kmalloc() GFP_ATOMIC 或 GFP_KERNEL
- 高端内存:alloc_pages()+kmap()
- vmalloc():分配大段内存
- slab:针对创建撤销很多大的数据结构