您的位置:首页 > 运维架构 > Linux

Linux驱动程序开发009 - 使用内核内存

2012-04-11 14:36 176 查看
序言

我们在编写用户空间程序的时候经常需要动态或静态(如静态数组)的使用系统内存资源,同样在内核空间也有类似的操作,但内核空间的操作要远比用户空间复杂的多,这一章就对如何在驱动程序中使用内存做个系统的介绍。

对于一个32位的系统来说,可访问的内存地址空间是4G。Linux系统将这4G的地址空间划分为两部分,以x86为例,0-3G (0 - PAGE_OFFSET, 0xC0000000)是用户地址空间,而3G-4G是内核地址空间,我们这里就讨论如何使用这部分地址空间。

静态数据区

Linux的驱动模块(Module)是ELF格式的,这里有必要先对Linux系统的内存段(Section)做个简单的介绍,如果你对此感兴趣,建议你阅读ELF文件格式,里面会有更加详尽的介绍。比较常见的就是.txt/.data/.bss段,它们就是静态分配的内存区域:

.text

text段,又叫代码段,它是程序代码所在的区域,这个区域是只读的。

.data

data段,静态初始化数据段,所有有初始值的全局变量和静态变量(static)都位于data段。

.bss

bss段,未初始化的全局变量区,由于存放未初始化的全局变量。

由于静态数据会持续占有内存,如果你不是特别需要这样的一块内存,尤其对内存相对紧张的系统,建议你采用动态分配方式来分配大块内存。

这里要强调的一点就是,与用户空间不同,进程内核栈的大小是有限制的,一般来说它是8K(32位系统)或16K(64位系统)。在<linux/sched.h>文件中有如下的定义:

union thread_union {

struct thread_info thread_info;

unsigned long stack[THREAD_SIZE/sizeof(long)];

};
THREAD_SIZE在不同的平台下有不同的定义,对于ARM平台,它就是8K,对于X86平台,如果配置栈大小位4K,那么它就是4K,否则,它就是8K。由于Linux进程内核栈不是很大,因此一定不能在你的驱动程序中使用大的局部变量(如数组),也不能使用递归这样消耗栈的函数,否则很容易导致进程内核栈的溢出。

有兴趣的读者可以参考一下Linux2.4的内核代码,它的栈的定义与Linux2.6有些不同,但进程内核栈的大小还是接近的。

动态内存分配

你已经使用过了内存分配函数了,它们主要是kmalloc和vmalloc。

Kmalloc

kmalloc是最常用的内核动态内存分配函数,有点类似于用户空间的malloc函数。kmaloc同Linux内核分配器(Allocator)是紧 密相关的,通常内核支持SLAB,SLOB和SLUB内核分配器,默认的也是最常用的是SLAB,因此这里我们假设内存分配器是SLAB的。

对于SLAB内存分配器,kmalloc的定义及实现在/include/linux/slab_def.h头文件中:

static inline void *kmalloc(size_t size, gfp_t flags)
kmalloc是快速的内存分配函数,kmalloc并不清除(初始化)分配的内存区域内容,因此在使用kmalloc分配的内存时要十分小心,必要时需要你显示的进行初始化。另外,kmalloc分配的内存在物理地址和虚拟地址上都是连续的,因此内存的访问操作也就会更快。

kmalloc分配的内存位于物理内存映射区,那么什么是物理内存映射区呢?我们知道3G到4G的物理空间是Linux内存的内核逻辑地址空间,而物理内存会映射到从3G开始的一段空间,大小就是物理内存的大小,因此这个区域就是物理内存映射区,内核使用 high_memory来标识这个空间。对于x86来说,它的大小就是:(arch/x86/kernel/setup.c)

high_memory = (void *)__va(max_pfn * PAGE_SIZE - 1) + 1;
使用kmalloc时要注意,由于kmalloc总是要从连续的物理内存分配,因此如果分配大块内存区域时有可能分配失败,因为系统不能保证有连续可用的物理内存。因此对于大块内存区域,我们不建议使用kmalloc来分配,除非必须这样做。

Vmalloc
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: