📄 linux设备驱动程序学习(8)-分配内存 - linux设备驱动程序 - tekkaman ninja.htm
字号:
<P><FONT color=#0000ff><FONT color=#000000>kmalloc
是一个功能强大且高速(除非被阻塞)的工具,所分配到的内存在物理内存中连续且保持原有的数据(不清零)。原型:</FONT></FONT></P>
<TABLE style="BORDER-COLLAPSE: collapse"
borderColor=#999999 cellSpacing=0 cellPadding=0
width="95%" bgColor=#f1f1f1 border=1>
<TBODY>
<TR>
<TD>
<P
style="MARGIN: 5px; LINE-HEIGHT: 150%"><CODE><SPAN
style="COLOR: #000000">#<SPAN
style="COLOR: #ff0000">include</SPAN> <SPAN
style="COLOR: #0000cc"><</SPAN>linux/slab<SPAN
style="COLOR: #0000cc">.</SPAN>h<SPAN
style="COLOR: #0000cc">></SPAN> <BR>void
<SPAN
style="COLOR: #0000cc">*</SPAN>kmalloc<SPAN
style="COLOR: #0000cc">(</SPAN>size_t <SPAN
style="COLOR: #0000ff">size</SPAN><SPAN
style="COLOR: #0000cc">,</SPAN> <SPAN
style="COLOR: #0000ff">int</SPAN> flags<SPAN
style="COLOR: #0000cc">)</SPAN><SPAN
style="COLOR: #0000cc">;</SPAN>
</SPAN></CODE></P></TD></TR></TBODY></TABLE>
<P><FONT color=#0000ff><STRONG>size
参数</STRONG></FONT></P>
<P>内核管理系统的物理内存,物理内存只能按页面进行分配。kmalloc 和典型的用户空间
malloc 在实际上有很大的差别,内核使用特殊的基于页的分配技术,以最佳的方式利用系统
RAM。Linux
处理内存分配的方法:创建一系列内存对象集合,每个集合内的内存块大小是固定。处理分配请求时,就直接在包含有足够大内存块的集合中传递一个整块给请求者。</P>
<P>必须注意的是:内核只能分配一些预定义的、固定大小的字节数组。kmalloc
能够处理的最小内存块是 32 或 64
字节(体系结构依赖),而内存块大小的上限随着体系和内核配置而变化。考虑到移植性,不应分配大于 128
KB的内存。若需多于几个 KB的内存块,最好使用其他方法。</P>
<P><STRONG><FONT
color=#0000ff>flags 参数</FONT></STRONG></P>
<P>内存分配最终总是调用 __get_free_pages 来进行实际的分配,这就是 GFP_
前缀的由来。</P>
<P>所有标志都定义在 <FONT
color=#0000ff><linux/gfp.h></FONT>
,有符号代表常常使用的标志组合。</P>
<P>主要的标志常被称为分配优先级,包括:</P>
<DIV class=variablelist>
<DL><SPAN class=term>
<DT><SPAN class=term><FONT
color=#0000ff>GFP_KERNEL</FONT>
<SPAN></SPAN></SPAN>
<DD>
<P>最常用的标志,意思是这个分配代表运行在内核空间的进程进行。内核正常分配内存。当空闲内存较少时,可能进入休眠来等待一个页面。当前进程休眠时,内核会采取适当的动作来获取空闲页。所以使用
GFP_KERNEL 来分配内存的函数必须是可重入,且不能在原子上下文中运行。</P>
<DT><FONT color=#0000ff>GFP_ATOMIC</FONT>
<SPAN></SPAN></SPAN>
<DD>
<P>内核通常会为原子性的分配预留一些空闲页。当当前进程不能被置为睡眠时,应使用
GFP_ATOMIC,这样kmalloc
甚至能够使用最后一个空闲页。如果连这最后一个空闲页也不存在,则分配返回失败。常用来从中断处理和进程上下文之外的其他代码中分配内存,从不睡眠。</P>
<DT><SPAN class=term><FONT
color=#0000ff>GFP_USER</FONT>
<SPAN></SPAN></SPAN>
<DD>
<P>用来为用户空间分配内存页,可能睡眠。</P>
<DT><SPAN class=term><FONT
color=#0000ff>GFP_HIGHUSER</FONT>
<SPAN></SPAN></SPAN>
<DD>
<P>类似 GFP_USER,如果有高端内存,就从高端内存分配。</P>
<DT><SPAN class=term><FONT
color=#0000ff>GFP_NOIO
<SPAN></SPAN></FONT></SPAN>
<DD><FONT color=#0000ff></FONT>
<DT><SPAN class=term><FONT
color=#0000ff>GFP_NOFS</FONT>
<SPAN></SPAN></SPAN>
<DD>
<P>功能类似 GFP_KERNEL,但是为内核分配内存的工作增加了限制。具有GFP_NOFS
的分配不允许执行任何文件系统调用,而 GFP_NOIO 禁止任何 I/O
初始化。它们主要用在文件系统和虚拟内存代码。那里允许分配休眠,但不应发生递归的文件系统调。</P></DD></DL></DIV>
<P><FONT color=#660099>
<TABLE
style="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 94.38%; BORDER-BOTTOM: #999 1px solid; HEIGHT: 237px"
align=center>
<TBODY>
<TR>
<TD>
<P>Linux 内核把内存分为 3 个区段:
可用于DMA的内存(位于一个特别的地址范围的内存, 外设可以在这里进行 DMA
存取)、常规内存和高端内存(为了访问(相对)大量的内存而存在的一种机制)。目的是使每中计算机平台都必须知道如何将自己特定的内存范围归类到这三个区段中,而不是所有RAM都一样。</P>
<P>当要分配一个满足kmalloc要求的新页时,
内核会建立一个内存区段的列表以供搜索。若指定了 __GFP_DMA,
只有可用于DMA的内存区段被搜索;若没有指定特别的标志, 常规和
可用于DMA的内存区段都被搜索; 若 设置了 __GFP_HIGHMEM,所有的 3
个区段都被搜索(注意:kmalloc 不能分配高端内存)。</P>
<P>内存区段背后的机制在 mm/page_alloc.c 中实现,
且区段的初始化时平台相关的, 通常在 arch 目录树的
mm/init.c中。</P></TD></TR></TBODY></TABLE></FONT></P>
<P><FONT
color=#660099>有的标志用双下划线做前缀,他们可与上面标志“或”起来使用,以控制分配方式:</FONT></P>
<DIV class=variablelist>
<DL>
<DT><SPAN class=term><FONT
color=#0000ff>__GFP_DMA</FONT>
<SPAN></SPAN></SPAN>
<DD>
<P>要求分配可用于DMA的内存。</P>
<DT><SPAN class=term><FONT
color=#0000ff>__GFP_HIGHMEM</FONT>
<SPAN></SPAN></SPAN>
<DD>
<P>分配的内存可以位于高端内存.</P>
<DT><SPAN class=term><FONT
color=#0000ff>__GFP_COLD</FONT>
<SPAN></SPAN></SPAN>
<DD>
<P>通常,分配器试图返回“缓存热(cache warm)”页面(可在处理器缓存中找到的页面)。
而这个标志请求一个尚未使用的“冷”页面。对于用作 DMA
读取的页面分配,可使用此标志。因为此时页面在处理器缓存中没多大帮助。</P>
<DT><SPAN class=term><FONT
color=#0000ff>__GFP_NOWARN</FONT>
<SPAN></SPAN></SPAN>
<DD>
<P>当一个分配无法满足,阻止内核发出警告(使用 printk )。</P>
<DT><SPAN class=term><FONT
color=#0000ff>__GFP_HIGH</FONT>
<SPAN></SPAN></SPAN>
<DD>
<P>高优先级请求,允许为紧急状况消耗被内核保留的最后一些内存页。</P>
<DT><FONT color=#0000ff><SPAN
class=term>__GFP_REPEAT<SPAN></SPAN></SPAN>
</FONT>
<DD><FONT color=#0000ff></FONT>
<DT><FONT color=#0000ff><SPAN
class=term>__GFP_NOFAIL<SPAN></SPAN></SPAN>
</FONT>
<DD><FONT color=#0000ff></FONT>
<DT><SPAN class=term><FONT
color=#0000ff>__GFP_NORETRY</FONT>
<SPAN></SPAN></SPAN>
<DD>
<P>告诉分配器当满足一个分配有困难时,如何动作。__GFP_REPEAT 表示努力再尝试一次,仍然可能失败;
__GFP_NOFAIL 告诉分配器尽最大努力来满足要求,始终不返回失败,不推荐使用;
__GFP_NORETRY 告知分配器如果无法满足请求,立即返回。</P>
<DD><FONT
color=#0000ff></FONT></DD></DL></DIV><FONT
color=#0000ff><SPAN class=term>
<P>
<HR id=null>
<P></P></SPAN></FONT>
<DL>
<DT><FONT color=#0000ff size=4><SPAN
class=term><STRONG>后备高速缓存</STRONG></SPAN></FONT>
<DT><FONT color=#0000ff><SPAN class=term>
<P><FONT
color=#000000>内核为驱动程序常常需要反复分配许多相同大小内存块的情况,增加了一些特殊的内存池,称为后备高速缓存(lookaside
cache)。 设备驱动程序通常不会涉及后备高速缓存,但是也有例外:在 Linux 2.6 中
USB 和 SCSI 驱动。</FONT></SPAN></FONT><FONT
color=#0000ff><SPAN class=term><FONT
color=#000000>Linux 内核的高速缓存管理器有时称为“slab
分配器”,相关函数和类型在 <FONT
color=#0000ff><linux/slab.h></FONT>
中声明。slab 分配器实现的高速缓存具有 kmem_cache_t
类型。实现过程如下:</FONT></SPAN></FONT></P>
<DT>
<P><FONT color=#0000ff><SPAN class=term><FONT
color=#000000>(1)创建一个新的后备高速缓存
<TABLE style="BORDER-COLLAPSE: collapse"
borderColor=#999999 cellSpacing=0 cellPadding=0
width="95%" bgColor=#f1f1f1 border=1>
<TBODY>
<TR>
<TD>
<P
style="MARGIN: 5px; LINE-HEIGHT: 150%"><CODE><SPAN
style="COLOR: #000000"><FONT
face=新宋体>kmem_cache_t <SPAN
style="COLOR: #0000cc">*</SPAN>kmem_cache_create<SPAN
style="COLOR: #0000cc">(</SPAN><SPAN
style="COLOR: #0000ff">const</SPAN> <SPAN
style="COLOR: #0000ff">char</SPAN> <SPAN
style="COLOR: #0000cc">*</SPAN>name<SPAN
style="COLOR: #0000cc">,</SPAN> </FONT></SPAN></CODE><CODE><SPAN
style="COLOR: #000000"><FONT face=新宋体><SPAN
style="COLOR: #ff0000">size_t</SPAN> size<SPAN
style="COLOR: #0000cc">,</SPAN><SPAN
style="COLOR: #ff0000">size_t</SPAN> offset<SPAN
style="COLOR: #0000cc">,</SPAN><BR> <SPAN
style="COLOR: #0000ff"> unsigned</SPAN>
<SPAN style="COLOR: #0000ff">long</SPAN>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -