全志平台mmap踩坑

最近两天突然遇到一个bug,想了很多方法都没解决,最后发现是一个很弱智的问题导致的。遂记录一下。
顺便提醒自己,如果一个bug在网上完全找不到相似,很有可能是使用不当导致的,应当细心检查代码,别想什么规避方案。

问题描述:
使用全志SDK打开camera,在不退出进程的情况下,多次打开,相机会出现query_buf失败的情况,
sunxi-vin-core vinc0: ion_alloc failed!!退出进程后重新进入,camera又可以正常打开了。cat /proc/pid/maps发现dma_buf占了很多内存,正常只有8块,而异常的时候有几十块。。。
好的,问题很清楚了,mmap的buf,没有被释放,进程一直在申请新内存,导致最终申请buffer失败。现象很简单,需要了解的就是mmap的内存,为什么没有释放。
我们先看看什么是mmap。
在<<深入理解计算机系统>>这本书中,mmap定义为:Linux通过将一个虚拟内存区域与一个磁盘上的对象(object)关联起来,以初始化这个虚拟内存区域的内容,这个过程称为内存映射(memory mapping)。copy一张图,更容易表示。

全志平台mmap踩坑_第1张图片

值得注意的是,mmap只是在虚拟内存分配了地址空间,只有在第一次访问虚拟内存的时候才分配物理内存。

访问内存的操作分为读写。其中读的过程如下图,这个图不知道出处。。。

全志平台mmap踩坑_第2张图片

可以看到进程访问内存时,少了一次copy to user的操作。

普通的写操作流程中,待写入的buffer在内核空间不能直接访问,必须要先拷贝至内核空间对应的主存,再写回磁盘中(延迟写回)。而mmap后的写入,是进程(用户态)将需要写入的数据直接copy到对应的mmap地址(内存copy)。

mmap映射建立之后,即使文件关闭,映射依然存在。因为映射的是磁盘的地址,不是文件本身,和文件句柄无关。同时可用于进程间通信的有效地址空间不完全受限于被映射文件的大小,因为是按页映射。

好的,我们现在的问题就是,文件被close了,munmap了,但是内存却没有释放。

从内核代码中来看,vin_video的release方法会调用vb2_fop_release(file);函数,》vb2_queue_release》__vb2_queue_free》__vb2_buf_mem_free(vb);》call_memop(q, put, vb->planes[plane].mem_priv);》vb2_dc_put》
static void vb2_dc_put(void *buf_priv) {
    struct vb2_dc_buf *buf = buf_priv;
    if (!atomic_dec_and_test(&buf->refcount))   //加log,在这个地方,buf->refcount为2,从这里就返回了。。。
        return;
    if (buf->sgt_base) {
        sg_free_table(buf->sgt_base);
        kfree(buf->sgt_base);
    }
    dma_free_coherent(buf->dev, buf->size, buf->vaddr, buf->dma_addr);
    put_device(buf->dev);
    kfree(buf);
}

为什么呢?因为alloc的时候,buf->refcount为1;mmap之后,buf->refcount变成2;buf_export之后,变成3;
然后atomic_dec_and_test之后,这里只能变成2,不能变成0,所以直接返回了。
而从网上搜的信息,都是说munmap之后buf就free了,而我的代码中,也有调用munmap,却没什么卵用。
尝试着手动减少refcount,在进程运行的时候就出现了报错,显示remove_vma_list的时候出现内存异常。


好吧,一步步来,先看export_buffer这个函数。

After calling ioctl VIDIOC_EXPBUF the fd field will be set by a driver. This is a DMABUF file descriptor. The application may pass it to other DMABUF-aware devices. Refer to DMABUF importing for details about importing DMABUF files into V4L2 nodes. It is recommended to close a DMABUF file when it is no longer used to allow the associated memory to be reclaimed.

没看太懂,但至少这里面说明了,dmabuf file需要被关闭,于是加上了close(dmafd[i]);
更坑的事情发生了,加上这个函数后,maps里面的dma_buffer不再增加了,但是重复几次后,还是申请内存失败。。。。
明明有munmap函数存在,为什么呢???
因为munmap传入的参数有误。。。全志传入的参数是 munmap(mMapMem.mem[i], mMapMem.length);
这个里面mMapMem.length的值为1,导致munmap释放buf不完全,坑爹的是这样写居然不会返回错误,因为mmap的buffer支持一部分一部分的释放。。。。。。
修改了这个值为申请buffer时的大小,然后加上上面的close(dmafd[i]);问题就解决了。。。。
中间纠结着要不要内存一次申请,常驻直到退出进程。应用层释放buffer,以及各种其他手段。。。哪知道最后是全志埋的坑。。。不爽啊。。。

你可能感兴趣的:(camera,car)