经过两周的折腾,终于通过修改内核解决了128M的DDR内存紧张的问题,实现了DM355的500万JPEG抓拍。总结下经验,也给大家提供一点思路。
公司之前开发的DM355通过PASS2机制实现了400万的抓拍效果,分辨率为2304X1760 ,帧率为2.5帧每秒。但当分辨率再提高的话会由于内存不够而导致程序挂掉。
两周前开始接手这项任务,首先尝试了下改大分辨率的效果,结果要么是采集的buffer不够,要么是压缩的buffer不够。同时,通过查看系统内存发现,在系统启动时即占用了48M的内存空间,感觉这里面明显有问题。
当时,我们分给系统的内存是90M,分给cmem的是剩余的38M。分析内存的使用,包括3部分:在内核空间的MT9P031 应用层的采集 应用层的压缩(压缩的内存由cmem分配)。由于已经知道系统占用内存过多,因此怀疑是MT9P031的buffer有问题,那么首先查看这部分。最后发现,他需要3个WIDTH*HEIGHT的buffer, 若是分辨率为2304X1760则buffer大小接近于8M,那么内存需占用24M,此时仍无法解释启动时的48M。当把分辨率改为2560X1920时,通过打印buffer的地址发现,系统分给每个buffer的大小竟然是16M,而其buffer本该是2560X1920X2,约为9M!
呵呵……终于找到了问题所在。 再细看其内存的申请,使用的是get_free_pages(),通过阅读LDD3,终于搞懂了Linux大块内存的分配方式,原来竟是由于遭遇了著名的buddy算法。其分配方式是按照2的n次幂来分。n的值取决于mmzone.h中的order值,一般为10或11,标示2的10次或2的11次幂个页。需要更大内存时,可修改此值。
这样的话,当buffer小于8M时,一切都好说,大不了系统分给一个8M的buffer,内存的浪费不是很严重。而一旦超过8M,就会分16M的buffer,这样的浪费就很严重了。比如此处,(16-9)*3=21M的内存就被浪费掉,极为可怕。
但是Linux的buddy我们不能改,因此只能另辟蹊径。在LDD3中提到,Linux在系统刚启动时并不立即使用buddy算法来分内存。而是有一个bootmem的过程,在这个过程中也可以分配大块的连续内存。呵呵……原来突破口在此。
最后,在start_kernel()中,使用alloc_bootmem_pages()分出了buffer,通过打印buffer地址发现,内存的确是我们需要的大小。这样就绕过了buddy。而这块内存也不再属于系统的一部分。
在完成此工作后,原以为万事大吉,谁料却依旧报内存不够。于是重新查看系统内存,发现现在没有了MT9P031的buffer后,依然会占用30M左右的内存。此时逐渐把目光放在内核和文件系统上。
根文件系统用的是ramdisk,由于ramdisk是要加载到内存使用的。所以怀疑他用了较多内存。一看之下,果不其然,其文件系统大小恰在20M左右。为了验证此想法,我将此文件系统做成cramfs挂载上看,呵呵……果然,内存使用在15M左右。
不得不说,使用ramdisk是一个设计方案的错误,由于IPNC根本就很少与文件系统打交道,所以没有必要把文件系统加载到内存。可喜的是,在dm365上,我们发现其根文件系统已经换成了cramfs。
因此,目前的思路有两条:内核优化和文件系统优化。首先考虑文件系统,我的想法是直接换成cramfs。但问题是,由于cramfs的只读属性,根本没办法在/dev下创建设备节点,也就没办法加载cmem.ko和其他模块。这或许是人家不使用cramfs的一个原因,但也只说明cmem的设计并不成熟,或者只是一个过渡。
我尝试将/dev挂载成tmpfs,呵呵……在低分辨率时OK,高分辨率时,竟然把内核挂掉了,原因未知。
此路暂时不通,但可以考虑精简ramdisk。
但相对而言,裁剪内核简单些。因此转变方向,先裁剪内核。多次尝试之后,竟然成功了。
另外,关于cmem的内存脚本说一句:它分配的内存本来是留给很多codecs使用的。若是没有相应的codec,就不需要为其分配内存。