Linux内存管理 -- malloc,kmalloc,vmalloc区别

1. Linux内存管理

高性能处理器一般会提供一个内存管理单元(MMU),该单元辅助操作系统进行内存管理,提供虚拟地址和物理地址的映射、内存访问权限保护和 Cache 缓存控制等硬件支持。操作系统内核借助 MMU,可以让用户感觉到好像程序可以使用非常大的内存空间,从而使得编程人员在写程序时不用考虑计算机中的物理内存的实际容量。

MMU 具有虚拟地址和物理地址转换、内存访问权限保护等功能, 这将使得 Linux操作系统能单独为系统的每个用户进程分配独立的内存空间并保证用户空间不能访问内核空间的地址,为操作系统的虚拟内存管理模块提供硬件基础。

但是,MMU 并非对所有处理器都是必须的,例如常用的 SAMSUNG 基于ARM7TDMI 系列的 S3C44B0X 不附带 MMU,其上无法运行老版的 Linux,而只能运行改版的μcLinux,但是新版的 Linux 2.6 则支持不带 MMU 的处理器。在嵌入式系统中,仍存在大量无 MMU 的处理器,Linux 2.6 为了更广泛地应用于嵌入式系统,融合了μcLinux,以支持 MMU-less 系统,如 Dragonball、ColdFire、Hitachi H8/300 等。

对于包含 MMU 的处理器而言,Linux 系统提供了复杂的存储管理系统,使得进程所能访问的内存达到 4GB。
在 Linux 系统中,进程的 4GB 内存空间被分为两个部分——用户空间与内核空间。用户空间地址一般分布为 0~3GB(即 PAGE_OFFSET,在 0x86 中它等于 0xC0000000),
这样,剩下的 3~4GB 为内核空间,如图 11.5 所示。用户进程通常情况下只能访问用户空间的虚拟地址,不能访问内核空间虚拟地址。用户进程只有通过系统调用(代表用户进程在内核态执行)等方式才可以访问到内核空间。

2. 用户空间内存动态申请 -- malloc

在用户空间动态申请内存的函数为 malloc(),这个函数在各种操作系统上的使用是一致的,malloc()申请的内存的释放函数为 free()。malloc()的内存一定要被 free(), 否则会造成内存泄漏。理想情况下, malloc()和 free()应成对出现,即谁申请,就由谁释放。完全让 malloc()和 free()成对出现有时候很难做到, 即便如此, 也应尽力将 malloc()申请内存的释放限制在本模块范围之内。如果在 A 模块申请的内存需在 B 模块释放,一般而言,软件结构的设计可能存在问题。

3. 内核空间内存动态申请

在 Linux 内核空间申请内存涉及的函数主要包括 kmalloc()、_ _get_free_pages()和vmalloc()等。kmalloc()和_ _get_free_pages()(及其类似函数)申请的内存位于物理内存映射区域, 而且在物理上也是连续的, 它们与真实的物理地址只有一个固定的偏移,因此存在较简单的转换关系。而 vmalloc()在虚拟内存空间给出一块连续的内存区,实质上,这片连续的虚拟内存在物理内存中并不一定连续,而 vmalloc()申请的虚拟内存和物理内存之间也没有简单的换算关系。

3.1 kmalloc()

最常用的分配标志是 GFP_KERNEL,其含义是在内核空间的进程中申请内存。kmalloc()的底层依赖_ _get_free_pages()实现, 分配标志的前缀 GFP 正好是这个底层函数的缩写。使用 GFP_ KERNEL 标志申请内存时,若暂时不能满足,则进程会睡眠 等 待 页 , 即会 引 起 阻塞 , 因 此不 能在中 断 上 下 文 或 持有自 旋锁 的 时 候 使用GFP_KERNE 申请内存。在中断处理函数、tasklet 和内核定时器等非进程上下文中不能阻塞,此时驱动应当使用 GFP_ATOMIC 标志来申请内存。当使用 GFP_ATOMIC 标志申请内存时,若不存在空闲页,则不等待,直接返回。

3.2 _ _get_free_pages ()

_ _get_free_pages()系列函数/宏是 kmalloc()实现的基础,_ _get_free_pages()系列函数/宏包括 get_zeroed_page()、_ _get_free_page()和_ _get_free_pages()。
get_zeroed_page(unsigned int flags);
该函数返回一个指向新页的指针并且将该页清零。
_ _get_free_page(unsigned int flags);
该宏返回一个指向新页的指针但是该页不清零,它实际上为:
#define _ _get_free_page(gfp_mask) \
_ _get_free_pages((gfp_mask),0)
就是调用了下面的_ _get_free_pages()申请 1 页。
_ _get_free_pages(unsigned int flags, unsigned int order);
该函数可分配多个页并返回分配内存的首地址,分配的页数为 2 order ,分配的页也不清零。order 允许的最大值是 10(即 1024 页)或者 11(即 2048 页),依赖于具体的硬件平台。
_ _get_free_pages ()和 get_zeroed_page ()的实现中调用了 alloc_pages()函数,alloc_pages()既可以在内核空间分配,也可以在用户空间分配,其原型为:
struct page * alloc_pages(int gfp_mask, unsigned long order);
参数含义与_ _get_free_pages()类似,但它返回分配的第一个页的描述符而非首地址。使用_ _get_free_pages()系列函数/宏申请的内存应使用下列函数释放:
void free_page(unsigned long addr);
void free_pages(unsigned long addr, unsigned long order);
如果申请和释放的 order 不一样,则会引起内存的混乱。
_ _get_free_pages 等函数在使用时, 其申请标志的值与 kmalloc()完全一样, 各标志的含义也与 kmalloc()完全一致,最常用的是 GFP_KERNEL 和 GFP_ATOMIC。

3.3 vmalloc()

vmalloc()一般用在为只存在于软件中(没有对应的硬件意义)的较大的顺序缓冲区分配内存,vmalloc()远大于_ _get_free_pages()的开销,为了完成 vmalloc(),新的页表需要被建立。因此,只是调用 vmalloc()来分配少量的内存(如 1 页)是不妥的。vmalloc()申请的内存应使用 vfree()释放,vmalloc()和 vfree()的函数原型如下:
void *vmalloc(unsigned long size);
void vfree(void * addr);
vmalloc()不能用在原子上下文中, 因为它的内部实现使用了标志为 GFP_KERNEL 的kmalloc()。使用 vmalloc 函数的一个例子函数是 creat。e_module()系统调用,它利用 vmalloc()函数来获取被创建模块需要的内存空间。

你可能感兴趣的:(linux,编码)