[ZLG-ARM] Linux设备驱动程序学习-分配内存

[复制链接]
1672|2
 楼主| reeper 发表于 2009-4-6 17:26 | 显示全部楼层 |阅读模式
内核为设备驱动提供了一个统一的内存管理接口,所以模块无需涉及分段和分页等问题。&nbsp;我已经在第一个scull模块中使用了&nbsp;kmalloc&nbsp;和&nbsp;kfree&nbsp;来分配和释放内存空间。&nbsp;<br />--------------------------------------------------------------------------------<br /><br />kmalloc&nbsp;函数内幕<br /><br />kmalloc&nbsp;是一个功能强大且高速(除非被阻塞)的工具,所分配到的内存在物理内存中连续且保持原有的数据(不清零)。原型:<br /><br />#include&nbsp;<br />void&nbsp;*kmalloc(size_t&nbsp;size,&nbsp;int&nbsp;flags);&nbsp;<br /><br /><br />size&nbsp;参数<br /><br />内核管理系统的物理内存,物理内存只能按页面进行分配。kmalloc&nbsp;和典型的用户空间&nbsp;malloc&nbsp;在实际上有很大的差别,内核使用特殊的基于页的分配技术,以最佳的方式利用系统&nbsp;RAM。Linux&nbsp;处理内存分配的方法:创建一系列内存对象集合,每个集合内的内存块大小是固定。处理分配请求时,就直接在包含有足够大内存块的集合中传递一个整块给请求者。<br /><br />必须注意的是:内核只能分配一些预定义的、固定大小的字节数组。kmalloc&nbsp;能够处理的最小内存块是&nbsp;32&nbsp;或&nbsp;64&nbsp;字节(体系结构依赖),而内存块大小的上限随着体系和内核配置而变化。考虑到移植性,不应分配大于&nbsp;128&nbsp;KB的内存。若需多于几个&nbsp;KB的内存块,最好使用其他方法。<br /><br />flags&nbsp;参数<br /><br />内存分配最终总是调用&nbsp;__get_free_pages&nbsp;来进行实际的分配,这就是&nbsp;GFP_&nbsp;前缀的由来。<br /><br />所有标志都定义在&nbsp;,有符号代表常常使用的标志组合。<br /><br />主要的标志常被称为分配优先级,包括:<br /><br />GFP_KERNEL&nbsp;<br />最常用的标志,意思是这个分配代表运行在内核空间的进程进行。内核正常分配内存。当空闲内存较少时,可能进入休眠来等待一个页面。当前进程休眠时,内核会采取适当的动作来获取空闲页。所以使用&nbsp;GFP_KERNEL&nbsp;来分配内存的函数必须是可重入,且不能在原子上下文中运行。<br /><br />GFP_ATOMIC&nbsp;<br />内核通常会为原子性的分配预留一些空闲页。当当前进程不能被置为睡眠时,应使用&nbsp;GFP_ATOMIC,这样kmalloc&nbsp;甚至能够使用最后一个空闲页。如果连这最后一个空闲页也不存在,则分配返回失败。常用来从中断处理和进程上下文之外的其他代码中分配内存,从不睡眠。<br /><br />GFP_USER&nbsp;<br />用来为用户空间分配内存页,可能睡眠。<br /><br />GFP_HIGHUSER&nbsp;<br />类似&nbsp;GFP_USER,如果有高端内存,就从高端内存分配。<br /><br />GFP_NOIO&nbsp;<br />GFP_NOFS&nbsp;<br />功能类似&nbsp;GFP_KERNEL,但是为内核分配内存的工作增加了限制。具有GFP_NOFS&nbsp;的分配不允许执行任何文件系统调用,而&nbsp;GFP_NOIO&nbsp;禁止任何&nbsp;I/O&nbsp;初始化。它们主要用在文件系统和虚拟内存代码。那里允许分配休眠,但不应发生递归的文件系统调。<br /><br />Linux&nbsp;内核把内存分为&nbsp;3&nbsp;个区段:&nbsp;可用于DMA的内存(位于一个特别的地址范围的内存,&nbsp;外设可以在这里进行&nbsp;DMA&nbsp;存取)、常规内存和高端内存(为了访问(相对)大量的内存而存在的一种机制)。目的是使每中计算机平台都必须知道如何将自己特定的内存范围归类到这三个区段中,而不是所有RAM都一样。<br /><br />当要分配一个满足kmalloc要求的新页时,&nbsp;内核会建立一个内存区段的列表以供搜索。若指定了&nbsp;__GFP_DMA,&nbsp;只有可用于DMA的内存区段被搜索;若没有指定特别的标志,&nbsp;常规和&nbsp;可用于DMA的内存区段都被搜索;&nbsp;若&nbsp;设置了&nbsp;__GFP_HIGHMEM,所有的&nbsp;3&nbsp;个区段都被搜索(注意:kmalloc&nbsp;不能分配高端内存)。<br /><br />内存区段背后的机制在&nbsp;mm/page_alloc.c&nbsp;中实现,&nbsp;且区段的初始化时平台相关的,&nbsp;通常在&nbsp;arch&nbsp;目录树的&nbsp;mm/init.c中。<br /><br /><br /><br />有的标志用双下划线做前缀,他们可与上面标志“或”起来使用,以控制分配方式:<br /><br />__GFP_DMA&nbsp;<br />要求分配可用于DMA的内存。<br /><br />__GFP_HIGHMEM&nbsp;<br />分配的内存可以位于高端内存.<br /><br />__GFP_COLD&nbsp;<br />通常,分配器试图返回“缓存热(cache&nbsp;warm)”页面(可在处理器缓存中找到的页面)。&nbsp;而这个标志请求一个尚未使用的“冷”页面。对于用作&nbsp;DMA&nbsp;读取的页面分配,可使用此标志。因为此时页面在处理器缓存中没多大帮助。<br /><br />__GFP_NOWARN&nbsp;<br />当一个分配无法满足,阻止内核发出警告(使用&nbsp;printk&nbsp;)。<br /><br />__GFP_HIGH&nbsp;<br />高优先级请求,允许为紧急状况消耗被内核保留的最后一些内存页。<br /><br />__GFP_REPEAT&nbsp;<br />__GFP_NOFAIL&nbsp;<br />__GFP_NORETRY&nbsp;<br />告诉分配器当满足一个分配有困难时,如何动作。__GFP_REPEAT&nbsp;表示努力再尝试一次,仍然可能失败;&nbsp;__GFP_NOFAIL&nbsp;告诉分配器尽最大努力来满足要求,始终不返回失败,不推荐使用;&nbsp;__GFP_NORETRY&nbsp;告知分配器如果无法满足请求,立即返回。<br /><br /><br /><br />--------------------------------------------------------------------------------<br /><br /><br />后备高速缓存&nbsp;<br />内核为驱动程序常常需要反复分配许多相同大小内存块的情况,增加了一些特殊的内存池,称为后备高速缓存(lookaside&nbsp;cache)。&nbsp;设备驱动程序通常不会涉及后备高速缓存,但是也有例外:在&nbsp;Linux&nbsp;2.6&nbsp;中&nbsp;USB&nbsp;和&nbsp;SCSI&nbsp;驱动。Linux&nbsp;内核的高速缓存管理器有时称为“slab&nbsp;分配器”,相关函数和类型在&nbsp;中声明。slab&nbsp;分配器实现的高速缓存具有&nbsp;kmem_cache_t&nbsp;类型。实现过程如下:<br /><br />(1)创建一个新的后备高速缓存&nbsp;kmem_cache_t&nbsp;*kmem_cache_create(const&nbsp;char&nbsp;*name,&nbsp;size_t&nbsp;size,size_t&nbsp;offset,<br />                 unsigned&nbsp;long&nbsp;flags,<br />      &nbsp;void&nbsp;(*constructor)(void&nbsp;*,&nbsp;kmem_cache_t&nbsp;*,unsigned&nbsp;long&nbsp;flags),&nbsp;<br /><br />      void&nbsp;(*destructor)(void&nbsp;*,&nbsp;kmem_cache_t&nbsp;*,&nbsp;unsigned&nbsp;long&nbsp;flags));<br />/*创建一个可以容纳任意数目内存区域的、大小都相同的高速缓存对象*/<br /><br /><br /><br />参数*name:&nbsp;一个指向&nbsp;name&nbsp;的指针,name和这个后备高速缓存相关联,功能是管理信息以便追踪问题;通常设置为被缓存的结构类型的名字,不能包含空格。<br /><br />参数size:每个内存区域的大小。<br /><br />参数offset:页内第一个对象的偏移量;用来确保被分配对象的特殊对齐,0&nbsp;表示缺省值。&nbsp;<br /><br />参数flags:控制分配方式的位掩码:<br /><br />SLAB_NO_REAP&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;保护缓存在系统查找内存时不被削减,不推荐。&nbsp;<br />SLAB_HWCACHE_ALIGN&nbsp;&nbsp;所有数据对象跟高速缓存行对齐,平台依赖,可能浪费内存。&nbsp;<br />SLAB_CACHE_DMA&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;每个数据对象在&nbsp;DMA&nbsp;内存区段分配.<br />其他标志详见&nbsp;mm/slab.c。但是,通常这些标志在只在开发系统中通过内核配置选项被全局性地设置。<br /><br />参数constructor&nbsp;和&nbsp;destructor&nbsp;是可选函数(不能只有destructor,而没有constructor&nbsp;),用来初始化新分配的对象和在内存被作为整体释放给系统之前“清理”对象。<br /><br />constructor&nbsp;函数在分配一组对象的内存时被调用,由于内存可能持有几个对象,所以可能被多次调用。同理,destructor不是立刻在一个对象被释放后调用,而可能在以后某个未知的时间中调用。&nbsp;根据它们是否被传递&nbsp;SLAB_CTOR_ATOMIC&nbsp;标志(&nbsp;CTOR&nbsp;是&nbsp;constructor&nbsp;的缩写),控制是否允许休眠。由于当被调用者是constructor函数时,slab&nbsp;分配器会传递&nbsp;SLAB_CTOR_CONSTRUCTOR&nbsp;标志。为了方便,它们可通过检测这个标志以使用同一函数。<br /><br />(2)通过调用&nbsp;kmem_cache_alloc&nbsp;从已创建的后备高速缓存中分配对象:&nbsp;void&nbsp;*kmem_cache_alloc(kmem_cache_t&nbsp;*cache,&nbsp;int&nbsp;flags);<br />/*cache&nbsp;参数是刚创建缓存,flags&nbsp;是和kmalloc&nbsp;的相同*/<br /><br /><br /><br />(3)使用&nbsp;kmem_cache_free释放一个对象:&nbsp;void&nbsp;kmem_cache_free(kmem_cache_t&nbsp;*cache,&nbsp;const&nbsp;void&nbsp;*obj);<br /><br /><br /><br />(4)当驱动用完这个后备高速缓存(通常在当模块被卸载时),释放缓存:<br /><br />int&nbsp;kmem_cache_destroy(kmem_cache_t&nbsp;*cache);&nbsp;<br />/*只在从这个缓存中分配的所有的对象都已返时才成功。因此,应检查&nbsp;kmem_cache_destroy&nbsp;的返回值:失败指示模块存在内存泄漏*/<br /><br /><br /><br />使用后备高速缓存的一个好处是内核会统计后备高速缓存的使用,统计情况可从&nbsp;/proc/slabinfo&nbsp;获得。<br /><br /><br /><br />--------------------------------------------------------------------------------<br />内存池&nbsp;<br /><br />为了确保在内存分配不允许失败情况下成功分配内存,内核提供了称为内存池(&nbsp;'mempool'&nbsp;)的抽象,它其实是某种后备高速缓存。它为了紧急情况下的使用,尽力一直保持空闲内存。所以使用时必须注意:&nbsp;mempool&nbsp;会分配一些内存块,使其空闲而不真正使用,所以容易消耗大量内存&nbsp;。而且不要使用&nbsp;mempool&nbsp;处理可能失败的分配。应避免在驱动代码中使用&nbsp;mempool。<br /><br />内存池的类型为&nbsp;mempool_t&nbsp;,在&nbsp;,使用方法如下:<br /><br />(1)创建&nbsp;mempool&nbsp;:<br /><br />mempool_t&nbsp;*mempool_create(int&nbsp;min_nr,<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mempool_alloc_t&nbsp;*alloc_fn,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mempool_free_t&nbsp;*free_fn,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;void&nbsp;*pool_data);&nbsp;<br />/*min_nr&nbsp;参数是内存池应当一直保留的最小数量的分配对象*/<br /><br />/*实际的分配和释放对象由&nbsp;alloc_fn&nbsp;和&nbsp;free_fn&nbsp;处理,原型:*/<br />typedef&nbsp;void&nbsp;*(mempool_alloc_t)(int&nbsp;gfp_mask,&nbsp;void&nbsp;*pool_data);<br />typedef&nbsp;void&nbsp;(mempool_free_t)(void&nbsp;*element,&nbsp;void&nbsp;*pool_data);<br />/*给&nbsp;mempool_create&nbsp;最后的参数&nbsp;*pool_data&nbsp;被传递给&nbsp;alloc_fn&nbsp;和&nbsp;free_fn&nbsp;*/<br /><br /><br />你可编写特殊用途的函数来处理&nbsp;mempool&nbsp;的内存分配,但通常只需使用&nbsp;slab&nbsp;分配器为你处理这个任务:mempool_alloc_slab&nbsp;和&nbsp;mempool_free_slab的原型和上述内存池分配原型匹配,并使用&nbsp;kmem_cache_alloc&nbsp;和&nbsp;kmem_cache_free&nbsp;处理内存的分配和释放。&nbsp;<br /><br />典型的设置内存池的代码如下:&nbsp;cache&nbsp;=&nbsp;kmem_cache_create(.&nbsp;.&nbsp;.);&nbsp;<br />pool&nbsp;=&nbsp;mempool_create(MY_POOL_MINIMUM,mempool_alloc_slab,&nbsp;mempool_free_slab,&nbsp;cache);&nbsp;<br /><br /><br /><br />(2)创建内存池后,分配和释放对象:&nbsp;void&nbsp;*mempool_alloc(mempool_t&nbsp;*pool,&nbsp;int&nbsp;gfp_mask);<br />void&nbsp;mempool_free(void&nbsp;*element,&nbsp;mempool_t&nbsp;*pool);<br /><br />在创建mempool时,分配函数将被调用多次来创建预先分配的对象。因此,对&nbsp;mempool_alloc&nbsp;的调用是试图用分配函数请求额外的对象,如果失败,则返回预先分配的对象(如果存在)。用&nbsp;mempool_free&nbsp;释放对象时,若预分配的对象数目小于最小量,就将它保留在池中,否则将它返回给系统。<br /><br />可用一下函数重定义mempool预分配对象的数量:&nbsp;int&nbsp;mempool_resize(mempool_t&nbsp;*pool,&nbsp;int&nbsp;new_min_nr,&nbsp;int&nbsp;gfp_mask);<br />/*若成功,内存池至少有&nbsp;new_min_nr&nbsp;个对象*/<br /><br /><br /><br />(3)若不再需要内存池,则返回给系统:&nbsp;void&nbsp;mempool_destroy(mempool_t&nbsp;*pool);&nbsp;<br />/*在销毁&nbsp;mempool&nbsp;之前,必须返回所有分配的对象,否则会产生&nbsp;oops*/<br /><br /><br /><br /><br />--------------------------------------------------------------------------------<br /><br /><br />get_free_page&nbsp;和相关函数<br />如果一个模块需要分配大块的内存,最好使用面向页的分配技术。<br />__get_free_page(unsigned&nbsp;int&nbsp;flags);&nbsp;<br />/*返回一个指向新页的指针,&nbsp;未清零该页*/<br /><br />get_zeroed_page(unsigned&nbsp;int&nbsp;flags);&nbsp;<br />/*类似于__get_free_page,但用零填充该页*/<br /><br />__get_free_pages(unsigned&nbsp;int&nbsp;flags,&nbsp;unsigned&nbsp;int&nbsp;order);&nbsp;<br />/*分配是若干(物理连续的)页面并返回指向该内存区域的第一个字节的指针,该内存区域未清零*/<br /><br />/*参数flags&nbsp;与&nbsp;kmalloc&nbsp;的用法相同;<br />参数order&nbsp;是请求或释放的页数以&nbsp;2&nbsp;为底的对数。若其值过大(没有这么大的连续区可用),&nbsp;则分配失败*/<br /><br /><br />get_order&nbsp;函数可以用来从一个整数参数&nbsp;size(必须是&nbsp;2&nbsp;的幂)&nbsp;中提取&nbsp;order,函数也很简单:<br /><br />/*&nbsp;Pure&nbsp;2^n&nbsp;version&nbsp;of&nbsp;get_order&nbsp;*/<br />static&nbsp;__inline__&nbsp;__attribute_const__&nbsp;int&nbsp;get_order(unsigned&nbsp;long&nbsp;size)<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;order;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;size&nbsp;=&nbsp;(size&nbsp;-&nbsp;1)&nbsp;&gt&gt&nbsp;(PAGE_SHIFT&nbsp;-&nbsp;1);<br />&nbsp;&nbsp;&nbsp;&nbsp;order&nbsp;=&nbsp;-1;<br />&nbsp;&nbsp;&nbsp;&nbsp;do&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;size&nbsp;&gt&gt=&nbsp;1;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;order++;<br />&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;while&nbsp;(size);<br />&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;order;<br />}<br /><br /><br /><br />使用方法举例在Linux设备驱动程序学习(7)-内核的数据类型&nbsp;中有。<br /><br />通过/proc/buddyinfo&nbsp;可以知道系统中每个内存区段上的每个&nbsp;order&nbsp;下可获得的数据块数目。<br /><br />当程序不需要页面时,它可用下列函数之一来释放它们。<br /><br />void&nbsp;free_page(unsigned&nbsp;long&nbsp;addr);<br />void&nbsp;free_pages(unsigned&nbsp;long&nbsp;addr,&nbsp;unsigned&nbsp;long&nbsp;order);<br /><br /><br /><br />它们的关系是:&nbsp;#define&nbsp;__get_free_page(gfp_mask)&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;__get_free_pages((gfp_mask),0)<br /><br /><br /><br />若试图释放和你分配的数目不等的页面,会破坏内存映射关系,系统会出错。<br /><br />注意:只要符合和&nbsp;kmalloc&nbsp;的相同规则,&nbsp;__get_free_pages&nbsp;和其他的函数可以在任何时候调用。这些函数可能失败(特别当使用&nbsp;GFP_ATOMIC&nbsp;时),因此调用这些函数的程序必须提供分配失败的处理。<br /><br />从用户的角度,可感觉到的区别主要是速度提高和更好的内存利用率(因为没有内部的内存碎片)。但主要优势实际不是速度,&nbsp;而是更有效的内存利用。&nbsp;__get_free_page&nbsp;函数的最大优势是获得的页完全属于调用者,&nbsp;且理论上可以适当的设置页表将起合并成一个线性的区域。<br /><br />alloc_pages&nbsp;接口&nbsp;<br />struct&nbsp;page&nbsp;是一个描述一个内存页的内部内核结构,定义在&nbsp;。<br /><br />Linux&nbsp;页分配器的核心是称为&nbsp;alloc_pages_node&nbsp;的函数:<br /><br />struct&nbsp;page&nbsp;*alloc_pages_node(int&nbsp;nid,&nbsp;unsigned&nbsp;int&nbsp;flags,<br />unsigned&nbsp;int&nbsp;order);<br /><br />/*以下是这个函数的&nbsp;2&nbsp;个变体(是简单的宏):*/<br />struct&nbsp;page&nbsp;*alloc_pages(unsigned&nbsp;int&nbsp;flags,&nbsp;unsigned&nbsp;int&nbsp;order);<br />struct&nbsp;page&nbsp;*alloc_page(unsigned&nbsp;int&nbsp;flags);<br /><br />/*他们的关系是:*/<br />#define&nbsp;alloc_pages(gfp_mask,&nbsp;order)&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;alloc_pages_node(numa_node_id(),&nbsp;gfp_mask,&nbsp;order)<br />#define&nbsp;alloc_page(gfp_mask)&nbsp;alloc_pages(gfp_mask,&nbsp;0)<br /><br /><br />参数nid&nbsp;是要分配内存的&nbsp;NUMA&nbsp;节点&nbsp;ID,<br />参数flags&nbsp;是&nbsp;GFP_&nbsp;分配标志,&nbsp;<br />参数order&nbsp;是分配内存的大小.&nbsp;<br />返回值是一个指向第一个(可能返回多个页)page结构的指针,&nbsp;失败时返回NULL。<br /><br />alloc_pages&nbsp;通过在当前&nbsp;NUMA&nbsp;节点分配内存(&nbsp;它使用&nbsp;numa_node_id&nbsp;的返回值作为&nbsp;nid&nbsp;参数调用&nbsp;alloc_pages_node)简化了alloc_pages_node调用。alloc_pages&nbsp;省略了&nbsp;order&nbsp;参数而只分配单个页面。&nbsp;<br /><br />释放分配的页:&nbsp;void&nbsp;__free_page(struct&nbsp;page&nbsp;*page);<br />void&nbsp;__free_pages(struct&nbsp;page&nbsp;*page,&nbsp;unsigned&nbsp;int&nbsp;order);<br />void&nbsp;free_hot_page(struct&nbsp;page&nbsp;*page);<br />void&nbsp;free_cold_page(struct&nbsp;page&nbsp;*page);<br />/*若知道某个页中的内容是否驻留在处理器高速缓存中,可以使用&nbsp;free_hot_page&nbsp;(对于驻留在缓存中的页)&nbsp;或&nbsp;free_cold_page(对于没有驻留在缓存中的页)&nbsp;通知内核,帮助分配器优化内存使用*/<br /><br /><br /><br /><br />--------------------------------------------------------------------------------<br /><br /><br />vmalloc&nbsp;和&nbsp;ioremap<br /><br />vmalloc&nbsp;是一个基本的&nbsp;Linux&nbsp;内存分配机制,它在虚拟内存空间分配一块连续的内存区,尽管这些页在物理内存中不连续&nbsp;(使用一个单独的&nbsp;alloc_page&nbsp;调用来获得每个页),但内核认为它们地址是连续的。&nbsp;应当注意的是:vmalloc&nbsp;在大部分情况下不推荐使用。因为在某些体系上留给&nbsp;vmalloc&nbsp;的地址空间相对小,且效率不高。函数原型如下:&nbsp;<br /><br />#include&nbsp;<br />void&nbsp;*vmalloc(unsigned&nbsp;long&nbsp;size);<br />void&nbsp;vfree(void&nbsp;*&nbsp;addr);<br />void&nbsp;*ioremap(unsigned&nbsp;long&nbsp;offset,&nbsp;unsigned&nbsp;long&nbsp;size);<br />void&nbsp;iounmap(void&nbsp;*&nbsp;addr);<br /><br /><br /><br /><br />kmalloc&nbsp;和&nbsp;_get_free_pages&nbsp;返回的内存地址也是虚拟地址,其实际值仍需&nbsp;MMU&nbsp;处理才能转为物理地址。vmalloc和它们在使用硬件上没有不同,不同是在内核如何执行分配任务上:kmalloc&nbsp;和&nbsp;__get_free_pages&nbsp;使用的(虚拟)地址范围和物理内存是一对一映射的,&nbsp;可能会偏移一个常量&nbsp;PAGE_OFFSET&nbsp;值,无需修改页表。<br /><br />而vmalloc&nbsp;和&nbsp;ioremap&nbsp;使用的地址范围完全是虚拟的,且每次分配都要通过适当地设置页表来建立(虚拟)内存区域。&nbsp;vmalloc&nbsp;可获得的地址在从&nbsp;VMALLOC_START&nbsp;到&nbsp;VAMLLOC_END&nbsp;的范围中,定义在&nbsp;中。vmalloc&nbsp;分配的地址只在处理器的&nbsp;MMU&nbsp;之上才有意义。当驱动需要真正的物理地址时,就不能使用&nbsp;vmalloc。&nbsp;调用&nbsp;vmalloc&nbsp;的正确场合是分配一个大的、只存在于软件中的、用于缓存的内存区域时。注意:vamlloc&nbsp;比&nbsp;__get_free_pages&nbsp;要更多开销,因为它必须即获取内存又建立页表。因此,&nbsp;调用&nbsp;vmalloc&nbsp;来分配仅仅一页是不值得的。vmalloc&nbsp;的一个小的缺点在于它无法在原子上下文中使用。因为它内部使用&nbsp;kmalloc(GFP_KERNEL)&nbsp;来获取页表的存储空间,因此可能休眠。<br /><br /><br />ioremap&nbsp;也要建立新页表,但它实际上不分配任何内存,其返回值是一个特殊的虚拟地址可用来访问特定的物理地址区域。<br />为了保持可移植性,不应当像访问内存指针一样直接访问由&nbsp;ioremap&nbsp;返回的地址,而应当始终使用&nbsp;readb&nbsp;和&nbsp;其他&nbsp;I/O&nbsp;函数。<br /><br />ioremap&nbsp;和&nbsp;vmalloc&nbsp;是面向页的(它们会修改页表),重定位的或分配的空间都会被上调到最近的页边界。ioremap&nbsp;通过将重映射的地址下调到页边界,并返回第一个重映射页内的偏移量来模拟一个非对齐的映射。&nbsp;<br /><br /><br />--------------------------------------------------------------------------------<br /><br /><br />per-CPU&nbsp;的变量<br /><br />per-CPU&nbsp;变量是一个有趣的&nbsp;2.6&nbsp;内核特性,定义在&nbsp;中。当创建一个per-CPU变量,系统中每个处理器都会获得该变量的副本。其优点是对per-CPU变量的访问(几乎)不需要加锁,因为每个处理器都使用自己的副本。per-CPU&nbsp;变量也可存在于它们各自的处理器缓存中,这就在频繁更新时带来了更好性能。<br /><br />在编译时间创建一个per-CPU变量使用如下宏定义:&nbsp;DEFINE_PER_CPU(type,&nbsp;name);<br />/*若变量(&nbsp;name)是一个数组,则必须包含类型的维数信息,例如一个有&nbsp;3&nbsp;个整数的per-CPU&nbsp;数组创建如下:&nbsp;*/<br />DEFINE_PER_CPU(int[3],&nbsp;my_percpu_array);&nbsp;<br /><br /><br /><br />虽然操作per-CPU变量几乎不必使用锁定机制。&nbsp;但是必须记住&nbsp;2.6&nbsp;内核是可抢占的,所以在修改一个per-CPU变量的临界区中可能被抢占。并且还要避免进程在对一个per-CPU变量访问时被移动到另一个处理器上运行。所以必须显式使用&nbsp;get_cpu_var&nbsp;宏来访问当前处理器的变量副本,&nbsp;并在结束后调用&nbsp;put_cpu_var。&nbsp;对&nbsp;get_cpu_var&nbsp;的调用返回一个当前处理器变量版本的&nbsp;lvalue&nbsp;,并且禁止抢占。又因为返回的是lvalue,所以可被直接赋值或操作。例如:&nbsp;get_cpu_var(sockets_in_use)++;<br />put_cpu_var(sockets_in_use);<br /><br /><br /><br />当要访问另一个处理器的变量副本时,&nbsp;使用:&nbsp;per_cpu(variable,&nbsp;int&nbsp;cpu_id);&nbsp;<br /><br /><br /><br />当代码涉及到多处理器的per-CPU变量,就必须实现一个加锁机制来保证访问安全。<br /><br />动态分配per-CPU变量方法如下:&nbsp;void&nbsp;*alloc_percpu(type);<br />void&nbsp;*__alloc_percpu(size_t&nbsp;size,&nbsp;size_t&nbsp;align);/*需要一个特定对齐的情况下调用*/<br />void&nbsp;free_percpu(void&nbsp;*per_cpu_var);&nbsp;/*&nbsp;将per-CPU&nbsp;变量返回给系统*/<br /><br />/*访问动态分配的per-CPU变量通过&nbsp;per_cpu_ptr&nbsp;来完成,这个宏返回一个指向给定&nbsp;cpu_id&nbsp;版本的per_cpu_var变量的指针。若操作当前处理器版本的per-CPU变量,必须保证不能被切换出那个处理器:*/<br />per_cpu_ptr(void&nbsp;*per_cpu_var,&nbsp;int&nbsp;cpu_id);<br /><br />/*通常使用&nbsp;get_cpu&nbsp;来阻止在使用per-CPU变量时被抢占,典型代码如下:*/<br /><br />int&nbsp;cpu;&nbsp;<br />cpu&nbsp;=&nbsp;get_cpu()<br />ptr&nbsp;=&nbsp;per_cpu_ptr(per_cpu_var,&nbsp;cpu);<br />/*&nbsp;work&nbsp;with&nbsp;ptr&nbsp;*/<br />put_cpu();<br /><br />/*当使用编译时的per-CPU&nbsp;变量,&nbsp;get_cpu_var&nbsp;和&nbsp;put_cpu_var&nbsp;宏将处理这些细节。动态per-CPU变量需要更明确的保护*/<br /><br /><br /><br />per-CPU变量可以导出给模块,&nbsp;但必须使用一个特殊的宏版本:&nbsp;EXPORT_PER_CPU_SYMBOL(per_cpu_var);<br />EXPORT_PER_CPU_SYMBOL_GPL(per_cpu_var);<br /><br /><br />/*要在模块中访问这样一个变量,声明如下:*/<br />DECLARE_PER_CPU(type,&nbsp;name);&nbsp;<br /><br /><br /><br />注意:在某些体系架构上,per-CPU变量的使用是受地址空间有限的。若在代码中创建per-CPU变量,&nbsp;应当尽量保持变量较小.<br /><br />--------------------------------------------------------------------------------<br /><br /><br />获得大的缓冲区<br />大量连续内存缓冲的分配是容易失败的。到目前止执行大&nbsp;I/O&nbsp;操作的最好方法是通过离散/聚集操作&nbsp;。<br /><br />在引导时获得专用缓冲区<br /><br />若真的需要大块连续的内存作缓冲区,最好的方法是在引导时来请求内存来分配。在引导时分配是获得大量连续内存页(避开&nbsp;__get_free_pages&nbsp;对缓冲大小和固定颗粒双重限制)的唯一方法。一个模块无法在引导时分配内存,只有直接连接到内核的驱动才可以。&nbsp;而且这对普通用户不是一个灵活的选择,因为这个机制只对连接到内核映象中的代码才可用。要安装或替换使用这种分配方法的设备驱动,只能通过重新编译内核并且重启计算机。<br /><br />当内核被引导,&nbsp;它可以访问系统种所有可用物理内存,接着通过调用子系统的初始化函数,&nbsp;允许初始化代码通过减少留给常规系统操作使用的&nbsp;RAM&nbsp;数量来分配私有内存缓冲给自己。<br /><br />在引导时获得专用缓冲区要通过调用下面函数进行:&nbsp;#include&nbsp;<br />/*分配不在页面边界上对齐的内存区*/<br />void&nbsp;*alloc_bootmem(unsigned&nbsp;long&nbsp;size);&nbsp;<br />void&nbsp;*alloc_bootmem_low(unsigned&nbsp;long&nbsp;size);&nbsp;/*分配非高端内存。希望分配到用于DMA操作的内存可能需要,因为高端内存不总是支持DMA*/<br /><br />/*分配整个页*/<br />void&nbsp;*alloc_bootmem_pages(unsigned&nbsp;long&nbsp;size);&nbsp;<br />void&nbsp;*alloc_bootmem_low_pages(unsigned&nbsp;long&nbsp;size);/*分配非高端内存*/<br /><br />/*很少在启动时释放分配的内存,但肯定不能在之后取回它。注意:以这个方式释放的部分页不返回给系统*/<br />void&nbsp;free_bootmem(unsigned&nbsp;long&nbsp;addr,&nbsp;unsigned&nbsp;long&nbsp;size);&nbsp;<br /><br /><br /><br />更多细节请看内核源码中&nbsp;Documentation/kbuild&nbsp;下的文件。&nbsp;<br />--------------------------------------------------------------------------------<br /><br /><br />ARM9开发板实验<br /><br />这几个实验我都没有用LDD3原配的代码,而是用以前的ioctl_and_lseek的代码改的。<br />(1)scullc&nbsp;模块试验<br />模块源码:scullc<br />测试代码:scullc_test<br /><br />(2)scullp&nbsp;模块试验<br />模块源码:scullp<br />测试代码:scullp_test<br /><br />(3)scullv&nbsp;模块试验&nbsp;<br />模块源码:scullv<br /><br />实验现象:&nbsp;[Tekkaman2440@SBC2440V4]#insmod&nbsp;/lib/modules/scullc.ko<br />[Tekkaman2440@SBC2440V4]#cat&nbsp;/proc/devices<br />Character&nbsp;devices:<br />&nbsp;&nbsp;1&nbsp;mem<br />&nbsp;&nbsp;2&nbsp;pty<br />&nbsp;&nbsp;3&nbsp;ttyp<br />&nbsp;&nbsp;4&nbsp;/dev/vc/0<br />&nbsp;&nbsp;4&nbsp;tty<br />&nbsp;&nbsp;4&nbsp;ttyS<br />&nbsp;&nbsp;5&nbsp;/dev/tty<br />&nbsp;&nbsp;5&nbsp;/dev/console<br />&nbsp;&nbsp;5&nbsp;/dev/ptmx<br />&nbsp;&nbsp;7&nbsp;vcs<br />10&nbsp;misc<br />13&nbsp;input<br />14&nbsp;sound<br />81&nbsp;video4linux<br />89&nbsp;i2c<br />90&nbsp;mtd<br />116&nbsp;alsa<br />128&nbsp;ptm<br />136&nbsp;pts<br />153&nbsp;spi<br />180&nbsp;usb<br />189&nbsp;usb_device<br />204&nbsp;s3c2410_serial<br />252&nbsp;scullc<br />253&nbsp;usb_endpoint<br />254&nbsp;rtc<br /><br />Block&nbsp;devices:<br />&nbsp;&nbsp;1&nbsp;ramdisk<br />256&nbsp;rfd<br />&nbsp;&nbsp;7&nbsp;loop<br />31&nbsp;mtdblock<br />93&nbsp;nftl<br />96&nbsp;inftl<br />179&nbsp;mmc<br />[Tekkaman2440@SBC2440V4]#mknod&nbsp;-m&nbsp;666&nbsp;scullc&nbsp;c&nbsp;252&nbsp;0<br />[Tekkaman2440@SBC2440V4]#ls&nbsp;-l&nbsp;/tmp/test&nbsp;(注:test是普通的2070B的文本文件)&nbsp;<br />-rw-r--r--&nbsp;1&nbsp;root&nbsp;root&nbsp;2070&nbsp;Nov&nbsp;24&nbsp;2007&nbsp;/<br /><br />[Tekkaman2440@SBC2440V4]#cat&nbsp;/tmp/test&nbsp;&gt&nbsp;/dev/scullc<br />[Tekkaman2440@SBC2440V4]#cat&nbsp;/proc/slabinfo<br />slabinfo&nbsp;-&nbsp;version:&nbsp;2.1&nbsp;(statistics)<br />#&nbsp;name&nbsp;:&nbsp;tunables&nbsp;:&nbsp;slabdata&nbsp;:&nbsp;globalstat&nbsp;:&nbsp;cpustat&nbsp;<br />scullc&nbsp;1&nbsp;1&nbsp;4020&nbsp;1&nbsp;1&nbsp;:&nbsp;tunables&nbsp;24&nbsp;12&nbsp;0&nbsp;:&nbsp;slabdata&nbsp;1&nbsp;1&nbsp;0&nbsp;:&nbsp;globalstat&nbsp;1&nbsp;1&nbsp;1&nbsp;0&nbsp;0&nbsp;0&nbsp;0&nbsp;0&nbsp;0&nbsp;:&nbsp;cpustat&nbsp;0&nbsp;1&nbsp;0&nbsp;0<br />......<br />[Tekkaman2440@SBC2440V4]#cat&nbsp;/proc/scullcseq<br /><br />Device&nbsp;0:&nbsp;qset&nbsp;1000,&nbsp;q&nbsp;4000,&nbsp;sz&nbsp;2070<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c3edca00,&nbsp;qset&nbsp;at&nbsp;c3efe000<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0:&nbsp;c3f56028<br />[Tekkaman2440@SBC2440V4]#/tmp/scullc_test<br />open&nbsp;scull&nbsp;!<br />scull_quantum=10&nbsp;scull_qset=4<br />close&nbsp;scull&nbsp;!<br /><br />[Tekkaman2440@SBC2440V4]#cat&nbsp;/tmp/test&nbsp;&gt&nbsp;/dev/scullc<br />[Tekkaman2440@SBC2440V4]#cat&nbsp;/proc/slabinfo<br />slabinfo&nbsp;-&nbsp;version:&nbsp;2.1&nbsp;(statistics)<br />#&nbsp;name&nbsp;:&nbsp;tunables&nbsp;:&nbsp;slabdata&nbsp;:&nbsp;globalstat&nbsp;:&nbsp;cpustat&nbsp;<br />scullc&nbsp;207&nbsp;207&nbsp;4020&nbsp;1&nbsp;1&nbsp;:&nbsp;tunables&nbsp;24&nbsp;12&nbsp;0&nbsp;:&nbsp;slabdata&nbsp;207&nbsp;207&nbsp;0&nbsp;:&nbsp;globalstat&nbsp;208&nbsp;207&nbsp;208&nbsp;1&nbsp;0&nbsp;0&nbsp;0&nbsp;0&nbsp;0&nbsp;:&nbsp;cpustat&nbsp;0&nbsp;208&nbsp;1&nbsp;0<br />......<br />[Tekkaman2440@SBC2440V4]#cat&nbsp;/proc/scullcmem<br /><br />Device&nbsp;0:&nbsp;qset&nbsp;4,&nbsp;q&nbsp;10,&nbsp;sz&nbsp;2070<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c3edc930,&nbsp;qset&nbsp;at&nbsp;c3edc8c8<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c3edc894,&nbsp;qset&nbsp;at&nbsp;c3edc860<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c3edc82c,&nbsp;qset&nbsp;at&nbsp;c3edc7f8<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c3edc7c4,&nbsp;qset&nbsp;at&nbsp;c3edc790<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c3edc75c,&nbsp;qset&nbsp;at&nbsp;c3edc178<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c3edc624,&nbsp;qset&nbsp;at&nbsp;c3edc4b8<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c3edc9cc,&nbsp;qset&nbsp;at&nbsp;c3edc998<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c3edc4ec,&nbsp;qset&nbsp;at&nbsp;c3edc964<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c3edc484,&nbsp;qset&nbsp;at&nbsp;c3edccd8<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c3edcca4,&nbsp;qset&nbsp;at&nbsp;c3edcc70<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c3edcc3c,&nbsp;qset&nbsp;at&nbsp;c3edcc08<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c3edcbd4,&nbsp;qset&nbsp;at&nbsp;c3edcba0<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c3edcb6c,&nbsp;qset&nbsp;at&nbsp;c3edcb38<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c3edcb04,&nbsp;qset&nbsp;at&nbsp;c3edcad0<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c3edca9c,&nbsp;qset&nbsp;at&nbsp;c3edca68<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c3edca34,&nbsp;qset&nbsp;at&nbsp;c3edca00<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c3edc8fc,&nbsp;qset&nbsp;at&nbsp;c3edcfb0<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c3edcf7c,&nbsp;qset&nbsp;at&nbsp;c3edcf48<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c3edcf14,&nbsp;qset&nbsp;at&nbsp;c3edcee0<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c3edceac,&nbsp;qset&nbsp;at&nbsp;c3edce78<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c3edce44,&nbsp;qset&nbsp;at&nbsp;c3edce10<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c3edcddc,&nbsp;qset&nbsp;at&nbsp;c3edcda8<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c3edcd74,&nbsp;qset&nbsp;at&nbsp;c3edcd40<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c3edcd0c,&nbsp;qset&nbsp;at&nbsp;c388a450<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c388a41c,&nbsp;qset&nbsp;at&nbsp;c388a3e8<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c388a3b4,&nbsp;qset&nbsp;at&nbsp;c388a380<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c388a34c,&nbsp;qset&nbsp;at&nbsp;c388a318<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c388a2e4,&nbsp;qset&nbsp;at&nbsp;c388a2b0<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c388a27c,&nbsp;qset&nbsp;at&nbsp;c388a248<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c388a214,&nbsp;qset&nbsp;at&nbsp;c388a1e0<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c388a1ac,&nbsp;qset&nbsp;at&nbsp;c388a178<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c388a144,&nbsp;qset&nbsp;at&nbsp;c388a790<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c388a75c,&nbsp;qset&nbsp;at&nbsp;c388a728<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c388a6f4,&nbsp;qset&nbsp;at&nbsp;c388a6c0<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c388a68c,&nbsp;qset&nbsp;at&nbsp;c388a658<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c388a624,&nbsp;qset&nbsp;at&nbsp;c388a5f0<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c388a5bc,&nbsp;qset&nbsp;at&nbsp;c388a588<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c388a554,&nbsp;qset&nbsp;at&nbsp;c388a520<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c388a4ec,&nbsp;qset&nbsp;at&nbsp;c388a4b8<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c388a484,&nbsp;qset&nbsp;at&nbsp;c388aad0<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c388aa9c,&nbsp;qset&nbsp;at&nbsp;c388aa68<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c388aa34,&nbsp;qset&nbsp;at&nbsp;c388aa00<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c388a9cc,&nbsp;qset&nbsp;at&nbsp;c388a998<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c388a964,&nbsp;qset&nbsp;at&nbsp;c388a930<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c388a8fc,&nbsp;qset&nbsp;at&nbsp;c388a8c8<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c388a894,&nbsp;qset&nbsp;at&nbsp;c388a860<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c388a82c,&nbsp;qset&nbsp;at&nbsp;c388a7f8<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c388a7c4,&nbsp;qset&nbsp;at&nbsp;c388ae10<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c388addc,&nbsp;qset&nbsp;at&nbsp;c388ada8<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c388ad74,&nbsp;qset&nbsp;at&nbsp;c388ad40<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c388ad0c,&nbsp;qset&nbsp;at&nbsp;c388acd8<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c388aca4,&nbsp;qset&nbsp;at&nbsp;c388ac70<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0:&nbsp;c38fb028<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1:&nbsp;c38fc028<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2:&nbsp;c38fd028<br />[Tekkaman2440@SBC2440V4]#insmod&nbsp;/lib/modules/scullp.ko<br />[Tekkaman2440@SBC2440V4]#cat&nbsp;/proc/devices<br />Character&nbsp;devices:<br />&nbsp;&nbsp;1&nbsp;mem<br />&nbsp;&nbsp;2&nbsp;pty<br />&nbsp;&nbsp;3&nbsp;ttyp<br />&nbsp;&nbsp;4&nbsp;/dev/vc/0<br />&nbsp;&nbsp;4&nbsp;tty<br />&nbsp;&nbsp;4&nbsp;ttyS<br />&nbsp;&nbsp;5&nbsp;/dev/tty<br />&nbsp;&nbsp;5&nbsp;/dev/console<br />&nbsp;&nbsp;5&nbsp;/dev/ptmx<br />&nbsp;&nbsp;7&nbsp;vcs<br />10&nbsp;misc<br />13&nbsp;input<br />14&nbsp;sound<br />81&nbsp;video4linux<br />89&nbsp;i2c<br />90&nbsp;mtd<br />116&nbsp;alsa<br />128&nbsp;ptm<br />136&nbsp;pts<br />153&nbsp;spi<br />180&nbsp;usb<br />189&nbsp;usb_device<br />204&nbsp;s3c2410_serial<br />251&nbsp;scullp<br />252&nbsp;scullc<br />253&nbsp;usb_endpoint<br />254&nbsp;rtc<br /><br />Block&nbsp;devices:<br />&nbsp;&nbsp;1&nbsp;ramdisk<br />256&nbsp;rfd<br />&nbsp;&nbsp;7&nbsp;loop<br />31&nbsp;mtdblock<br />93&nbsp;nftl<br />96&nbsp;inftl<br />179&nbsp;mmc<br />[Tekkaman2440@SBC2440V4]#mknod&nbsp;-m&nbsp;666&nbsp;/dev/scullp&nbsp;c&nbsp;251&nbsp;0<br />[Tekkaman2440@SBC2440V4]#cat&nbsp;/tmp/test&nbsp;&gt&nbsp;/dev/scullp<br />[Tekkaman2440@SBC2440V4]#cat&nbsp;/proc/scullpseq<br /><br />Device&nbsp;0:&nbsp;qset&nbsp;1000,&nbsp;q&nbsp;4000,&nbsp;sz&nbsp;2070<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c3edc964,&nbsp;qset&nbsp;at&nbsp;c3eff000<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0:&nbsp;c3f21000<br />[Tekkaman2440@SBC2440V4]#/tmp/scullp_test<br />open&nbsp;scull&nbsp;!<br />scull_quantum=1000&nbsp;scull_qset=2<br />close&nbsp;scull&nbsp;!<br /><br />[Tekkaman2440@SBC2440V4]#cat&nbsp;/tmp/test&nbsp;&gt&nbsp;/dev/scullp<br />[Tekkaman2440@SBC2440V4]#cat&nbsp;/proc/scullpseq<br /><br />Device&nbsp;0:&nbsp;qset&nbsp;2,&nbsp;q&nbsp;1000,&nbsp;sz&nbsp;2070<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c3edcc70,&nbsp;qset&nbsp;at&nbsp;c3edcca4<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c3edc484,&nbsp;qset&nbsp;at&nbsp;c3edcc3c<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0:&nbsp;c383f000<br />[Tekkaman2440@SBC2440V4]#insmod&nbsp;/lib/modules/scullv.ko<br />[Tekkaman2440@SBC2440V4]#cat&nbsp;/proc/devices<br />Character&nbsp;devices:<br />&nbsp;&nbsp;1&nbsp;mem<br />&nbsp;&nbsp;2&nbsp;pty<br />&nbsp;&nbsp;3&nbsp;ttyp<br />&nbsp;&nbsp;4&nbsp;/dev/vc/0<br />&nbsp;&nbsp;4&nbsp;tty<br />&nbsp;&nbsp;4&nbsp;ttyS<br />&nbsp;&nbsp;5&nbsp;/dev/tty<br />&nbsp;&nbsp;5&nbsp;/dev/console<br />&nbsp;&nbsp;5&nbsp;/dev/ptmx<br />&nbsp;&nbsp;7&nbsp;vcs<br />10&nbsp;misc<br />13&nbsp;input<br />14&nbsp;sound<br />81&nbsp;video4linux<br />89&nbsp;i2c<br />90&nbsp;mtd<br />116&nbsp;alsa<br />128&nbsp;ptm<br />136&nbsp;pts<br />153&nbsp;spi<br />180&nbsp;usb<br />189&nbsp;usb_device<br />204&nbsp;s3c2410_serial<br />250&nbsp;scullv<br />251&nbsp;scullp<br />252&nbsp;scullc<br />253&nbsp;usb_endpoint<br />254&nbsp;rtc<br /><br />Block&nbsp;devices:<br />&nbsp;&nbsp;1&nbsp;ramdisk<br />256&nbsp;rfd<br />&nbsp;&nbsp;7&nbsp;loop<br />31&nbsp;mtdblock<br />93&nbsp;nftl<br />96&nbsp;inftl<br />179&nbsp;mmc<br />[Tekkaman2440@SBC2440V4]#mknod&nbsp;-m&nbsp;666&nbsp;/dev/scullv&nbsp;c&nbsp;250&nbsp;0<br />[Tekkaman2440@SBC2440V4]#cat&nbsp;/tmp/test&nbsp;&gt&nbsp;/dev/scullv<br />[Tekkaman2440@SBC2440V4]#cat&nbsp;/proc/scullvseq<br /><br />Device&nbsp;0:&nbsp;qset&nbsp;1000,&nbsp;q&nbsp;4000,&nbsp;sz&nbsp;2070<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c3edc7c4,&nbsp;qset&nbsp;at&nbsp;c389d000<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0:&nbsp;c485e000<br />[Tekkaman2440@SBC2440V4]#cat&nbsp;/proc/scullpseq<br /><br />Device&nbsp;0:&nbsp;qset&nbsp;2,&nbsp;q&nbsp;1000,&nbsp;sz&nbsp;2070<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c3edcc70,&nbsp;qset&nbsp;at&nbsp;c3edcca4<br />&nbsp;&nbsp;item&nbsp;at&nbsp;c3edc484,&nbsp;qset&nbsp;at&nbsp;c3edcc3c<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0:&nbsp;c383f000<br /><br /><br />从上面的数据可以看出:&nbsp;在s3c2440&nbsp;(ARM9)体系上,&nbsp;vmalloc返回的虚拟地址就在用于映射物理内存的地址上。<br />&nbsp;<br /> &nbsp;&nbsp;<br />
initer 发表于 2009-4-7 15:05 | 显示全部楼层

内存分配也比较麻烦

  
msleep 发表于 2009-4-8 09:52 | 显示全部楼层

要弄嵌入式,需要很多的技术支持!必须基础好一些

  
您需要登录后才可以回帖 登录 | 注册

本版积分规则

139

主题

185

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部