LDD3第八章的学习-分配内存

作者:Aningsk ,本作品采用知识共享署名-非商业性使用-相同方式共享 3.0 未本地化版本许可协议进行许可。 
 

关于内核驱动中分配内存,对于我这种菜小打小闹写写驱动,也就是用个kmalloc啦。这一章讲了不少更深奥的东西,大多数我都没有用过;但也在这里做一下记录。

 

Kmalloc

简而言之,我只用过kmalloc(size, GFP_KERNEL);了。当使用GFP_KERNEL的时候,表示使用kmalloc的函数正代表某个进程执行系统调用(比如,驱动里实现read、write时使用了kmalloc),这是以内核空间进程来执行的。GFP_KERNEL允许当前进程进入休眠来等待获取空闲的内存页面。

但是在某些情况下是不允许进程进入休眠的,如中断处理、tasklet和内核定时器。在这些上下文中,进程不应该休眠,所以要使用的标志是GFP_ATOMIC。

至于其他的标志,在书中214页,就不多说了。还有要说的是kmalloc不应该尝试去分配大于128KB的内存。最后记得kfree释放内存。

 

后备高速缓存

内核中维护了一组拥有相同大小的内存块组成的内存池,通常称为后备高速缓存。当驱动需要反复分配很多大小相同的内存块时,可以使用这种机制。使用后备高速缓存的好处就是,众多大小的内存块达到最大程度的密集,从而不会在内存中造成内存碎片,这样便可以提高运行速度。

在使用后备高速缓存时,首先要使用kmem_cache_create创建高速缓存分配器,然后可以使用kmem_cache_alloc从分配器中分配内存;当不再使用时,要使用kmem_cache_free释放内存,最后释放高速缓存kmem_cache_destroy。在destroy之前,我们必须确保所有alloc

出的内存都已经free,否则destroy会失败表示发生了内存泄漏。在书中219页有使用例子。

上面所说的高速缓存在进行内存分配时,是可能失败的。但有时候我们不允许失败。所以内核在后备高速缓存的基础上,提供了更加高级的内存池机制,称为mempool。它会始终持有一定量的空闲内存,以保证分配一定量的内存一定会成功。

使用方法:首先要创建高速缓存分配器,然后在这个高速缓存上创建内存池。也就是在上文的步骤中再加入创建内存池的操作,而不是直接就alloc。在创建内存池时,指定了内存池持有的最小内存块数目(内存块的大小是在创建高速缓存时指定的)。类似的,在destroy之前,我们要保证所有alloc的内存块都已经free。使用例子参见书中220~221页。

在使用mempool_alloc从内存池分配内存时,它会首先尝试在内存中获取该对象,如果不能获取(即分配失败),就会返回在创建时预先分配好的内存块;同样,当使用mempool_free释放内存块时,如果释放的数量比较多以至于要释放预先分配的内存块,那它不会真正释放而是继续保留在内存池中。也就是说内存池始终持有一定量的可用内存块。

这些预先分配的内存块一般是尽量保持空闲的,但是除了这个内存池,其他的不能使用这些空闲内存;也就是说这些内存空闲但被始终占用。因此使用内存池会浪费一定的内存空间。如果不是内存分配失败会导致严重问题,我们应该尽量不使用内存池,而是在代码里处理内存分配失败的情况。

 

分配大块内存

前面说过kmalloc最好不要去分配大于128KB的内存空间。如果需要分配一块很大的内存,我们需要使用get_free_page族的函数。这些函数参见221页。值得留意的是__get_free_pages的参数,第一参数flags是和kmalloc一样的,第二个参数order表示分配内存页的数量(一个内存页大小一般是4KB),但order不是个数,而是内存页的数量的以2为底的对数,有一点点绕……就是说分配出的页面数量是2^order个,这样就清楚多了。所以不要把order写得太大。使用的例子参见书中223页。

 

Vmalloc

对于kmalloc,它分配的空间在物理内存上是连续的;而vmalloc分配的空间在物理内存上一般是不连续,虚拟地址空间上是连续的。因为vmalloc还要建立虚拟内存的页表,所以它的开销会大一些。使用vmalloc的正确场合时在分配一大块连续的、只在软件中存在的、用于缓冲的内存区域的时候。用vmalloc分配几个页时比其他函数要快一些,但由于存在建立页表的开销,所以当只分配一页时却会慢一些。嗯,这些是照着书上写的。与使用GFP_KERNEL的kmalloc一样,vmalloc不能在原子上下文中使用。记得vfree释放。书中227页有例子。

至于ioremap,我用的时候就是用来映射GPIO的寄存器地址,如:

#define GPIOC_REG_CON   0x56000020  volatile unsigned int * reg_gpioc_con = NULL;  reg_gpioc_con = (volatile unsigned int *)(ioremap(GPIOC_REG_CON, 4));

给ioremap的两个参数分别是GPIOC_REG_CON寄存器的物理地址(在CPU的spec上翻出来的),另一个是这个寄存器的大小(4字节,就是32位)。然后就可以类似这样向寄存器写值:

#define SET_GPIOC_OUT   (*reg_gpioc_con |= 0x55500000)  //set ctrl-pin output

调用这个宏,SET_GPIOC_OUT;就可以在CPU的寄存器(地址为0x56000020)里写入0x55500000了。上面这个宏只是在寄存器原有值的基础上添加(用的或运算)。如果要置为某个值,事先要清除相应的位,例如:

#define SET_LCM_DATA(data) \  (*reg_gpiod_data = ((*reg_gpiod_data & ~0x07F8) | (((unsigned short)data &0xFF) << 3)))

这个宏就是清除了寄存器的3到18位,然后将data的低十六位,放进这个寄存器的3到18位。

对于ioremap这样用,的确好使;但内核里是不是这样控制CPU的GPIO,我还没看到相关的章节。这里只是顺路说了一下我自己用的ioremap的例子罢了。

 

书里还说了per-CPU变量等等,就先不详细写了。

 

Aningsk

2015-03-25

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值