close-on-exec 相关的一个 bug

close-on-exec 相关的一个 bug

测试一个用 V4L2 拍照的程序时,发现程序单独运行很正常,但在多进程环境下运行时就会出现问题,具体表现为执行 open 系统调用打开 /dev/video 设备时返回 EBUSY 错误,查询 V4L2 的文档可以看到该错误的含义

EBUSY
The driver does not support multiple opens and the device is already in use.

也就是说该设备不支持多次重复打开或者已经在被使用了

在程序运行时执行 lsof,发现是一些和拍照相对无关的进程(比如 wpa_supplicant)持有着 video 设备,这些进程是肯定不会自己的去打开 video 设备的,肯定是由于某种原因从主进程中拿到了 video 的句柄

回忆一下 Linux 中执行其他程序的方式,是先执行 fork 再执行 exec

  • fork 的时候会拷贝父进程的文件描述符表(Linux 由于有写时拷贝,所以不会立即拷贝)
  • 通过 exec 打开新程序后,会替换掉 fork 子进程的正文段、数据段、堆和栈,但并不会关掉已经打开的文件描述符

这种特性被 shell 用来实现 IO 重定向,比如说我们要将标准输出重定向到日志文件 log.txt

  • shell 先 fork 出一个子 shell
  • 在子 shell 中关掉标准输出(即文件描述符 1),然后立即打开 log.txt,那么 log.txt 的 fd 就会是 1,因为 open 是从所有可用的文件描述符中选一个最小的
  • 然后调用 exec 执行 shell 命令,这样 shell 命令写到标准输出里的内容就被写入了 log.txt

然而在套接字编程或使用设备时,这种特性会引来一些意想不到的 BUG

  • 比如继承给新程序的套接字占了父进程的 ip 端口对,导致父进程没法使用
  • 在我们这个场景中,也是因为新程序从父进程拿走了 /dev/video 的描述符,而 /dev/video 本身是不允许共享的(跟驱动程序相关),导致设备使用冲突

为了解决该问题,Linux 中有一个文件描述符标志 FD_CLOEXEC,如果某个文件描述符设定了该标志,执行 exec 期间就会自动关闭该文件描述符

  • 可以通过 fcntlF_GETFDF_SETFD 参数获取和设置该标志
  • 也可以在 open 时指定 O_CLOEXEC 标志,它会给打开的文件描述符设置 FD_CLOEXEC
posted @ 2022-02-26 12:52  路过的摸鱼侠  阅读(72)  评论(0编辑  收藏  举报