在编写图像识别模块时,遇到了一个莫名的大问题,(如代码所示)
unsigned
char
*
image0,
*
image1;
image0 = get_image_from_camera(...);
image1 = get_image_from_camera(...);
image0 = get_image_from_camera(...);
image1 = get_image_from_camera(...);
以上代码是连续拍摄两张照片,但奇怪的是,拍摄完毕时发现,image0保存的图片内容和image1的内容竟然完全相同!!!!太不可思议了。首先,通过如下代码排除了获取照片错误的问题:
image0
=
get_image_from_camera(...);
write_jpeg(image0...);
image1 = get_image_from_camera(...);
write_jpeg(image1...);
write_jpeg(image0...);
image1 = get_image_from_camera(...);
write_jpeg(image1...);
以上代码的含义是拍摄下图片后立即保存为jpeg文件,这样我就能检测两张图片是否内容相同,不出所料,检测结果显示两个jpeg文件的图片内容是完全不同的。但即使是这样,在以上操作后,依旧发现image0和image1指向的图片内容相同。似乎是中邪了,保存的jpeg文件内容不同,但是指针指向的却是相同的内容(都是image1的内容,也就是后一张图片)。然后想着,是不是在获取image1的同时,把image0的内存区域给覆盖了,这个是很有可能的,那么这个问题就100%出在get_image_from_camera函数中。打开这个函数的定义发现:
char *
map;
...
map = malloc(...);
...
return (map);
...
map = malloc(...);
...
return (map);
这个就和我想的不一样了,这个函数非常正确,它在获取图像时,是采用了动态内存分配,并且返回内存区域的首地址,这样的话,就根本不可能会出现内存覆盖的现象。至此,这个问题一直困扰了我半个星期,一点进展都没有,大脑一团浆糊,唯一想的就是中邪了。
然后,就在n天后,吃饱了撑的,在校园里走着,忽然脑袋里想起了我们在用户空间中的内存地址都是虚拟内存地址,也就是需要通过内存映射,才能够访问到物理地址的。那么这样的映射关系到底是什么关系呢?第一个闪现的就是一一映射,也就是说只允许唯一的虚拟地址去映射唯一的物理地址。那么到底有没有其它的可能性呢?一对多?这个马上被否定了,因为假如一个虚拟地址能够映射多个物理地址的话,根本是不可能实现的,因为操作系统无法分清某一时刻这个虚拟地址到底要映射到哪个物理地址上。那么多对一呢?也就是说多个虚拟地址可以映射到唯一的物理地址上?也就是说通过不同的虚拟地址,我们可以访问同一个物理地址。这个可行。忽然间就豁然开朗,考虑到项目上出的那个奇怪的问题,会不会是image0和image1同时映射到了相同的物理地址上?
以上分析确实有硬伤,当时没怎么细想,解决方案可行,并且没有大问题,就记了下来,……其实,通过共享内存的方式很容易就能实现,动态分配的内存映射到进程地址空间的同一位置,比较郁闷,没想到这一点,一开始以为是分配的内存在内核空间直接映射到了camera的缓存里……后来,也证实了共享内存的这个想法,就此改正。
但是由于时间关系,没能确实的验证,但是基于这个不晓得是对是错的设想,却很容易的想出了解决问题的办法,代码缩写如下:
image0
=
get_image(...);
image0_tmp = (unsigned char * )malloc(image_size);
memset(image0_tmp, 0 , image_size);
memcpy(image0_tmp, image0, image_size);
image1 = get_image(...);
image0_tmp = (unsigned char * )malloc(image_size);
memset(image0_tmp, 0 , image_size);
memcpy(image0_tmp, image0, image_size);
image1 = get_image(...);