问题: 在linux系统下,使用插件挂载与卸载光驱。 在按下物理设备自带光驱的弹出按钮后,光驱正常弹出,但lsblk下挂载点还在,且目录可看。
自己的机器不复现,问题机器不在身边 调试起来有些不方便。
对问题分析、排查、定位过程:
首先简单的加日志替换 挂载插件udevil--devmon shell脚本 查看日志。 发现按弹出按钮后无日志更新,也就是说没有触发正常的devmon流程。
阅读源码分析开源挂载插件流转。发现devmon进程,实际是一个while 一直在read -u ${COPROC[0]}; 这里的${COPROC[0]} 是协同进程的管道的文件描述符。 可以通过echo打印COPROC_PID来看 pid找到向他发送数据的进程。 这里我看到的就是 udevli这个插件进程。
通过日志,可以判断 在执行弹出操作时,一直是阻塞到read -u ${COPROC[0]}; 也就是说udevil进程没有向他发送数据。再继续阅读udevil的源码,整理了一下他的设计。
在插件源码 udevil.c中,通过调用 udev库函数,新建了一个 socket(PF_NETLINK, SOCK_DGRAM|SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT); 这里PF_NETLINK是linux特殊的socket可以建立内核与用户的通信。 查阅资料看大多usb光驱等事件都是这样搞得。
建立完成后,再将fd 通过g_io_channel_watch检测G_IO_IN(需读取消息事件),如有事件就进入cb处理,cb中通过 udev_device_get_action等函数判断设备执行了什么操作。最后调用printf,devmon中的read就可以收到。 这里对于printf的使用还没有弄太懂。
在源码中整个流程中间可能会影响接收的地方都加了日志,在udevil.c中使用printf会发送到devmon中 所以临时封装一个log_func 将我的日志重定向到指定文件。 编译源码。 替换到异常机器,发现没有进入错误逻辑,就是在弹出操作后,一直没有进入 cb也就是说没有收到内核的G_IO_IN。
这就奇怪了,同一操作系统iso 在别的设备上都是正常的,此异常设备用移动光驱也没有问题。就是自带光驱弹出有问题。这个udevil插件还是依靠的udev机制,查阅了一下资料,了解了下udev。
使用udevadm monitor -p命令可以监测 内核消息与udev事件,测试了一下。正常的信息为:
先会收到:
KERNEL[65601.889278] change /devices/pci0000:00/0000:00:02.0/0000:03:00.0/usb1/1-3/1-3:1.0/host4/target4:0:0/4:0:0:0/block/sr0 (block)
这是kernel到的设备变动。
紧接着通过netlink 套接字会发给 UDEV:
UDEV [65593.698640] change /devices/pci0000:00/0000:00:02.0/0000:03:00.0/usb1/1-3/1-3:1.0/host4/target4:0:0/4:0:0:0/block/sr0 (block)、
果然第二条及时UDEV开头的事件,再后面就是我插件干的事了。
这里的"change"是光驱读盘与弹出事件;
还有“add”新设备插入事件;
“remove”设备拔出事件;
在异常设备上 弹出时没有收到第一条KERNEL信息;也就是说内核都没检测到设备动作。 自然无法向下流转,到不了我的插件执行卸载挂载点操作。/var/log/message 与dmesg也没有异常日志。
总结: 分析定位后是设备问题,因为装不装我的挂载插件, KERNEL都检测不到设备变动,UDEV也没法拿到。
了解了一下linux下 UDEV机制,博大精深。
udevadm monitor命令非常好用。