【Linux】在内核中申请内存
2011-05-22 16:04
609 查看
<!--
@page { margin: 0.79in }
P { margin-bottom: 0.08in }
TD P { margin-bottom: 0in }
TH P { margin-bottom: 0in }
A:link { so-language: zxx }
-->
Allocating
Memory in the Kernel
http://www.linuxjournal.com/article/6930
Dec
01, 2003 By Robert
Love in
SysAdmin
在内核中申请内存和在用户空间中申请内存不同,有以下因素引起了复杂性,包括:
1,内核的虚拟和物理地址被限制到1GB。
2,内核的内存不能pageable。
3,内核通常需要连续的物理地址。
4,通常内核申请内存是不能睡眠。
5,内核中的错误比其他地方的错误有更多的代价。
尽管在内核中访问大量内存不再奢侈,但是一点错误就要耗费很长的时间来解决。
A General-Purpose Allocator
在内核中申请内存的常用方法是:
#include <linux/slab.h>
void *kmalloc(size_t size, int flags);
kmalloc和用户空间的malloc几乎一样,除了第二个参数flags。size和malloc一样,指定了需要申请的按照字节计算的内存数量。当申请成功后,kmalloc()返回一个指向size大小的内存。返回的内存可以赋予任何类型的对象。和malloc一样,kmalloc也可以失败,如果失败,返回一个NULL。
Struct falcon *p;
p=kmalloc(sizeof(struct falcon),
GFP_KERNEL);
if(!p)
/*申请失败,错误处理*/
Flags
flags控制了申请内存的行为。flags分为三类:action
modifiers,zone
modifiers,types。Action
modifiers告诉内如以怎么的行为去申请内存。比如kernel能否睡眠。Zone
modifiers告诉内核从什么地方去申请内存,比如一些请求可能需要申请的内存能够让硬件以DMA的方式方式。type指定了申请的类型。把这写flags可以合成一个变量,传给kmalloc。
Table 1是action
modifier。Table 2是zone
modifiers。最常用的是GFP_ATOMIC,
GFP_KERNEL。几乎所有的申请都需要同时指定这两个标记的一个。
Table 1. Action
Modifiers
Table 2. Zone
Modifiers
GFP_ATOMIC限制内核申请内存不能被堵塞。在不能睡眠的环境中使用这个标记,必须以原子的方式申请内存,比如中断处理。因为内核不能堵塞并且需要去满足申请的内存大小,因此指定这个标记的成功率会低一些。尽管如此,如果申请内存的环境不能睡眠,这个标记是唯一的选择:
struct wolf *p;
p=kmalloc(sizeof(struct wolf),
GFP_ATOMIC);
if(!p)
/*error*/
GFP_KERNEL指定了一个通常的内核申请。GFP_KERNEL指定了申请上下文不能被锁,但是可以睡眠。内核可以在睡眠的时候释放内存,所以指定这个标记,成功的可能性更大些。比如内核可以锁住申请的请求,然后交换一些非活动状态的内存页到硬盘上,压缩内存中的caches等等。
有时候,比如在写一个ISA设备驱动,我们需要让内存也可以在DMA的区域上申请。对于ISA设备来说,这部分内存是物理地址的开始的16M(0x
1000000)。为了保证内存申请在这个16M的区域,使用GFP_DMA标记。通常,需要GFP_DMA要和GFP_KERNEL或GFP_ATOMIC一起指定。比如:
char *buf;
buf=kmalloc(BUF)LEN,
GFP_DMA|GFP_KERNEL);
Table 3是type列表。Table
4表示了那些action,
zone的flag标记是一致的。在<linux/gfp.h>中定义了这些标记:
Table 3. Types
Table 4.
Composition of the Type Flags
Returning Memory
当使用完内存之后,需要kfree()注销内存:
#include <linux/slab.h>
void kfree(const void* objp);
kfree的使用和free一样。不同的是free()操作在一个NULL或者非法地址上,会造成内存的错误,而kfree是安全的。
Allocating from Virtual Memory
kmalloc返回物理的地址,也就是虚拟连续的地址。与用户空间的malloc对比,malloc返回虚拟的地址,但是物理上不一定是连续的。物理上连续的地址有两个好处,一是,需要硬件不能访问虚拟地址,二是物理上连续的地址可以使用一个大的页面来映射,着意味着TLB(translation
lookaside buffer,传输后备区)对于寻址这些内存只需要一个TLB
engry。
申请物理连续地址有一个问题,就是常常比较难找到物理连续的内存,特别是大的内存块。而申请大的连续的虚拟地址则成功可能性更大。如果不一定需要物理连续地址时,可以使用vmalloc():
#include <linux/vmalloc.h>
void * vmalloc(unsigned long size);
void vfree(void * addr);
vmalloc和vfree的使用和用户空间的malloc(),free()一样。vmalloc()一致。
尽管kmalloc()的性能更好的,但是如果不是特殊需要,还是推荐使用vmalloc。
A Small Fixed-Size Stack
和用户空间的进行不一样的是,在kernel中代码没有大的动态的stack。相反的,在内核的进程有一个小的固定大小的stack。这个大小不同的体系不一样。许多体系总申请两个页给stack,在32位的机器上,一般就是8k。
因此申请大的stack上的数据是不鼓励的。
#define BUF_LEN 2048
void rabbit_function(void)
{
char buf[BUF_LEN];
/* ... */
}
相比较以上的例子,更倾向于下边的做法:
#define BUF_LEN 2048
void rabbit_function(void)
{
char *buf;
buf = kmalloc(BUF_LEN,
GFP_KERNEL);
if (!buf)
/* error! */
/* ... */
}
Conclusion
内核中申请内存有一些简单的规则:
1,判断申请内存的时候可否睡眠,也就是调用kmalloc的时候能否被阻塞。如果在一个中断处理,在中断处理的下半部分,或者有一个锁的时候,就不能被阻塞。如果在一个进程上下文,也没有锁,则一般可以睡眠。
2,如果可以睡眠,指定GFP_KERNEL。
3,如果不能睡眠,就指定GFP_ATOMIC。
4,如果需要DMA可以访问的内存,比如ISA或者有些PCI设备,就需要指定GFP_DMA。
5,需要对kmalloc返回的值检查NULL。
6,为了没有内存泄漏,需要用kfree()来释放内存。
Resources
For more information, check out these
files in your kernel source tree.
include/linux/gfp.h: home of the
allocation flags.
include/linux/slab.h: definitions of
kmalloc(), et al.
mm/page_alloc.c: page allocation
functions.
mm/slab.c: implementation of kmalloc(),
et al.
Robert Love (rml@tech9.net) is a kernel
hacker at MontaVista Software and a student at the University of
Florida. He is the author of Linux Kernel Development. Robert enjoys
fine wine and lives in Gainesville, Florida.
@page { margin: 0.79in }
P { margin-bottom: 0.08in }
TD P { margin-bottom: 0in }
TH P { margin-bottom: 0in }
A:link { so-language: zxx }
-->
Allocating
Memory in the Kernel
http://www.linuxjournal.com/article/6930
Dec
01, 2003 By Robert
Love in
SysAdmin
在内核中申请内存和在用户空间中申请内存不同,有以下因素引起了复杂性,包括:
1,内核的虚拟和物理地址被限制到1GB。
2,内核的内存不能pageable。
3,内核通常需要连续的物理地址。
4,通常内核申请内存是不能睡眠。
5,内核中的错误比其他地方的错误有更多的代价。
尽管在内核中访问大量内存不再奢侈,但是一点错误就要耗费很长的时间来解决。
A General-Purpose Allocator
在内核中申请内存的常用方法是:
#include <linux/slab.h>
void *kmalloc(size_t size, int flags);
kmalloc和用户空间的malloc几乎一样,除了第二个参数flags。size和malloc一样,指定了需要申请的按照字节计算的内存数量。当申请成功后,kmalloc()返回一个指向size大小的内存。返回的内存可以赋予任何类型的对象。和malloc一样,kmalloc也可以失败,如果失败,返回一个NULL。
Struct falcon *p;
p=kmalloc(sizeof(struct falcon),
GFP_KERNEL);
if(!p)
/*申请失败,错误处理*/
Flags
flags控制了申请内存的行为。flags分为三类:action
modifiers,zone
modifiers,types。Action
modifiers告诉内如以怎么的行为去申请内存。比如kernel能否睡眠。Zone
modifiers告诉内核从什么地方去申请内存,比如一些请求可能需要申请的内存能够让硬件以DMA的方式方式。type指定了申请的类型。把这写flags可以合成一个变量,传给kmalloc。
Table 1是action
modifier。Table 2是zone
modifiers。最常用的是GFP_ATOMIC,
GFP_KERNEL。几乎所有的申请都需要同时指定这两个标记的一个。
Table 1. Action
Modifiers
Flag | Description |
---|---|
__GFP_COLD | The kernel should use cache cold pages. |
__GFP_FS | The kernel can start filesystem I/O. |
__GFP_HIGH | The kernel can access emergency pools. |
__GFP_IO | The kernel can start disk I/O. |
__GFP_NOFAIL | The kernel can repeat the allocation. |
__GFP_NORETRY | The kernel does not retry if the allocation fails. |
__GFP_NOWARN | The kernel does not print failure warnings. |
__GFP_REPEAT | The kernel repeats the allocation if it fails. |
__GFP_WAIT | The kernel can sleep. |
Modifiers
Flag | Description |
---|---|
__GFP_DMA | Allocate only DMA-capable memory. |
No flag | Allocate from wherever available. |
GFP_ATOMIC限制内核申请内存不能被堵塞。在不能睡眠的环境中使用这个标记,必须以原子的方式申请内存,比如中断处理。因为内核不能堵塞并且需要去满足申请的内存大小,因此指定这个标记的成功率会低一些。尽管如此,如果申请内存的环境不能睡眠,这个标记是唯一的选择:
struct wolf *p;
p=kmalloc(sizeof(struct wolf),
GFP_ATOMIC);
if(!p)
/*error*/
GFP_KERNEL指定了一个通常的内核申请。GFP_KERNEL指定了申请上下文不能被锁,但是可以睡眠。内核可以在睡眠的时候释放内存,所以指定这个标记,成功的可能性更大些。比如内核可以锁住申请的请求,然后交换一些非活动状态的内存页到硬盘上,压缩内存中的caches等等。
有时候,比如在写一个ISA设备驱动,我们需要让内存也可以在DMA的区域上申请。对于ISA设备来说,这部分内存是物理地址的开始的16M(0x
1000000)。为了保证内存申请在这个16M的区域,使用GFP_DMA标记。通常,需要GFP_DMA要和GFP_KERNEL或GFP_ATOMIC一起指定。比如:
char *buf;
buf=kmalloc(BUF)LEN,
GFP_DMA|GFP_KERNEL);
Table 3是type列表。Table
4表示了那些action,
zone的flag标记是一致的。在<linux/gfp.h>中定义了这些标记:
Table 3. Types
Flag | Description |
---|---|
GFP_ATOMIC | The allocation is high-priority and does not sleep. This is the flag to use in interrupt handlers, bottom halves and other situations where you cannot sleep. |
GFP_DMA | This is an allocation of DMA-capable memory. Device drivers that need DMA-capable memory use this flag. |
GFP_KERNEL | This is a normal allocation and might block. This is the flag to use in process context code when it is safe to sleep. |
GFP_NOFS | This allocation might block and might initiate disk I/O, but it does not initiate a filesystem operation. This is the flag to use in filesystem code when you cannot start another filesystem operation. |
GFP_NOIO | This allocation might block, but it does not initiate block I/O. This is the flag to use in block layer code when you cannot start b4ac more block I/O. |
GFP_USER | This is a normal allocation and might block. This flag is used to allocate memory for user-space processes. |
Composition of the Type Flags
Flag | Value |
---|---|
GFP_ATOMIC | __GFP_HIGH |
GFP_NOIO | __GFP_WAIT |
GFP_NOFS | (__GFP_WAIT | __GFP_IO) |
GFP_KERNEL | (__GFP_WAIT | __GFP_IO | __GFP_FS) |
GFP_USER | (__GFP_WAIT | __GFP_IO | __GFP_FS) |
GFP_DMA | __GFP_DMA |
Returning Memory
当使用完内存之后,需要kfree()注销内存:
#include <linux/slab.h>
void kfree(const void* objp);
kfree的使用和free一样。不同的是free()操作在一个NULL或者非法地址上,会造成内存的错误,而kfree是安全的。
Allocating from Virtual Memory
kmalloc返回物理的地址,也就是虚拟连续的地址。与用户空间的malloc对比,malloc返回虚拟的地址,但是物理上不一定是连续的。物理上连续的地址有两个好处,一是,需要硬件不能访问虚拟地址,二是物理上连续的地址可以使用一个大的页面来映射,着意味着TLB(translation
lookaside buffer,传输后备区)对于寻址这些内存只需要一个TLB
engry。
申请物理连续地址有一个问题,就是常常比较难找到物理连续的内存,特别是大的内存块。而申请大的连续的虚拟地址则成功可能性更大。如果不一定需要物理连续地址时,可以使用vmalloc():
#include <linux/vmalloc.h>
void * vmalloc(unsigned long size);
void vfree(void * addr);
vmalloc和vfree的使用和用户空间的malloc(),free()一样。vmalloc()一致。
尽管kmalloc()的性能更好的,但是如果不是特殊需要,还是推荐使用vmalloc。
A Small Fixed-Size Stack
和用户空间的进行不一样的是,在kernel中代码没有大的动态的stack。相反的,在内核的进程有一个小的固定大小的stack。这个大小不同的体系不一样。许多体系总申请两个页给stack,在32位的机器上,一般就是8k。
因此申请大的stack上的数据是不鼓励的。
#define BUF_LEN 2048
void rabbit_function(void)
{
char buf[BUF_LEN];
/* ... */
}
相比较以上的例子,更倾向于下边的做法:
#define BUF_LEN 2048
void rabbit_function(void)
{
char *buf;
buf = kmalloc(BUF_LEN,
GFP_KERNEL);
if (!buf)
/* error! */
/* ... */
}
Conclusion
内核中申请内存有一些简单的规则:
1,判断申请内存的时候可否睡眠,也就是调用kmalloc的时候能否被阻塞。如果在一个中断处理,在中断处理的下半部分,或者有一个锁的时候,就不能被阻塞。如果在一个进程上下文,也没有锁,则一般可以睡眠。
2,如果可以睡眠,指定GFP_KERNEL。
3,如果不能睡眠,就指定GFP_ATOMIC。
4,如果需要DMA可以访问的内存,比如ISA或者有些PCI设备,就需要指定GFP_DMA。
5,需要对kmalloc返回的值检查NULL。
6,为了没有内存泄漏,需要用kfree()来释放内存。
Resources
For more information, check out these
files in your kernel source tree.
include/linux/gfp.h: home of the
allocation flags.
include/linux/slab.h: definitions of
kmalloc(), et al.
mm/page_alloc.c: page allocation
functions.
mm/slab.c: implementation of kmalloc(),
et al.
Robert Love (rml@tech9.net) is a kernel
hacker at MontaVista Software and a student at the University of
Florida. He is the author of Linux Kernel Development. Robert enjoys
fine wine and lives in Gainesville, Florida.
相关文章推荐
- [初级知识]linux内核开发中基本内存分配与申请
- linux 内核 内存管理 bootmem alloctor 申请内存
- linux驱动开发--内核空间中内存的申请与释放
- 【Linux开发】linux设备驱动归纳总结(五):1.在内核空间分配内存
- Linux 查看内核,CPU,内存信息命令
- 从 Linux 内核访问用户空间内存
- 2014年首个Linux内核更新发布 Linux 3.13提升了网络、内存性能
- linux内存源码分析 - 伙伴系统(初始化和申请页框)
- 通过slab分配器申请内核内存的函数有?
- Linux内核开发之内存与I/O访问(二)
- 宋宝华:Linux内核的连续内存分配器(CMA)——避免预留大块内存
- linux 用户空间与内核空间——高端内存详解
- Linux内存管理: Linux Kernel Shared Memory 剖析 Linux 内核中的内存去耦合
- linux0.11内核main.c中的内存初始化 /*非常详解*/
- linux 系统信息查询——系统版本,内核信息,cpu信息,内存信息,硬盘信息
- 深入理解Linux内核之内存管理
- Linux的内存结构以及动态申请的几个函数
- Linux内核开发之内存与I/O访问(二)
- Linux下如何查看CPU型号、个数、核数、逻辑CPU数、位数、发行版本、内核信息、内存、服务器生产厂家
- Linux 内核内存泄露工具使用