在Linux中模拟设备驱动崩溃,并让Python重新加载它
我在Linux上用uvcvideo模块运行一个网络摄像头。然后我用一个Python程序来访问这个摄像头并显示图像。
我希望这个Python程序能处理摄像头出现故障的情况。如果我在运行Python代码之前先卸载模块,效果很好。但如果在使用中强行卸载模块,就会出现以下问题。
VIDIOC_DQBUF: Inappropriate ioctl for device
而且如果我结束Python代码,然后重新启动它,整个机器就会卡住。
我尝试运行的代码是
import pygame
import Image
from pygame.locals import *
import sys
import time, os
import opencv
from opencv import highgui
camera = highgui.cvCreateCameraCapture(0)
fps = 10.0
pygame.init()
window = pygame.display.set_mode((640,480))
pygame.display.set_caption("WebCam Demo")
screen = pygame.display.get_surface()
while True:
events = pygame.event.get()
for event in events:
if event.type == QUIT or event.type == KEYDOWN:
sys.exit(0)
while True:
try:
ima = highgui.cvQueryFrame(camera)
im = opencv.adaptors.Ipl2PIL(ima)
break;
except TypeError:
print 'No camera'
os.system('sudo modprobe uvcvideo')
time.sleep(1)
camera = highgui.cvCreateCameraCapture(0)
pg_img = pygame.image.frombuffer(im.tostring(), im.size, im.mode)
screen.blit(pg_img, (0,0))
pygame.display.flip()
pygame.time.delay(int(1000 * 1.0/fps))
这是一个修改过的版本,来源于http://www.jperla.com/blog/2007/09/26/capturing-frames-from-a-webcam-on-linux/,使用的是openvc 1.x版本,而不是2.x。
有没有什么办法可以让这个程序正常工作?
3 个回答
Linux系统在你试图删除一个内核驱动的时候,如果有程序正在使用这个驱动,它会非常不高兴。对于普通用户的应用程序来说,我觉得没有什么好的办法可以做到这一点(而且让你的应用程序去运行'sudo modprobe uvcvideo'这条命令,听起来就已经够让人紧张的了)。
你的代码现在崩溃的原因是,当驱动程序崩溃时,代表你硬件的特殊设备文件就消失了。而你的代码仍然保持着对这些设备的打开文件句柄。根据你的代码在后台具体做了什么,它可能试图对一个现在无效的文件句柄发出IOCTL命令,这种情况通常是库代码处理得不好,因为这种情况一般只会在内核出现故障时发生,而用户空间的代码对此无能为力。
处理摄像头停止工作和处理驱动崩溃是完全不同的事情。一个故障的摄像头不应该导致(正确编写的)驱动程序崩溃。如果驱动崩溃了,你的用户空间代码几乎无能为力,也不应该需要去处理。如果驱动崩溃,那是驱动开发者的问题,而不是你的。如果你使用的驱动经常崩溃到让你想要尝试处理它的地步,那我建议你换一个驱动,或者尝试修复你正在使用的那个。再怎么写应用代码也无法修复一个有问题的驱动。
别忘了,你的代码并不是唯一在使用这个驱动的代码。内核内部的进程或其他应用程序也可能在使用这个驱动。如果在你拔掉驱动的时候,其他代码正在使用它,你可能会导致那段代码挂掉(超出你的控制),甚至可能让整个系统崩溃。
现在,如果你的摄像头硬件有问题,驱动应该优雅地给你一个消息或某种错误提示,让你的应用代码能够检测到并采取行动,同时它自己也在努力让摄像头恢复工作。故障的硬件不应该给应用代码带来负担;让驱动去做它的工作,如果可能的话,它会让摄像头重新上线。如果无法做到,那可能是摄像头处于不可恢复的状态,或者驱动还有改进的空间(如果是这样,向驱动的开发者提出在你的硬件上测试他们的代码,有时可以快速获得更好的驱动支持)。
与其试图在驱动运行时把它拔掉,不如集中精力编写代码来处理驱动可能返回的所有错误状态。
你是说USB摄像头吗?我不太清楚在模块使用时强制卸载的情况,但这种情况不会发生,而且这也不是一个好的模拟摄像头失效的方式。首先,试着优雅地处理摄像头的断开和重新连接。
我不太明白你想通过模拟驱动崩溃来达到什么目的,但你无法用用户代码来处理驱动崩溃,这可能会导致系统错误或者其他问题。一旦内核代码出错,没有什么防御性编程能拯救你。
如果驱动代码出现错误(错误和崩溃是不同的),那么这个错误应该会反馈给你,你能做的就是重试或者退出。如果你的应用是为了支持任何UVC摄像头,那么就买一个符合UVC标准的USB摄像头,试着玩一下(断开/重新连接)。
至于硬件故障,你能做的也不多,或许可以设置一个超时。如果你在代码中发现了某个特定的驱动问题,那你可以避免触发这个特定的问题。例如,如果你知道从分辨率x切换到分辨率y会导致摄像头卡住或者驱动崩溃,那就尽量避免这样做。
不过,我建议你不要花太多时间去处理那些你根本不知道的假设性崩溃。相反,你应该尝试走一些错误处理的代码路径。例如,如果你的系统内存不足会发生什么?或者如果系统负载很高,你的应用无法跟上输入帧的速度,会怎样?