仓库源文

.. Kenneth Lee 版权所有 2019-2020

:Authors: Kenneth Lee :Version: 1.0

给使用设备的进程发信号


今天评审一个方案的时候看到有人用到file_operations->fasync()加kill_fasync()的组 合来处理设备异常的时候给进程发信号的问题。我对这个设计的语义不太了解,所以通过 本文来调查一下相关概念。

本分析基于5.1主线Linux内核。

fasync这个回调是在ioctl(FIOASYNC(FASYNC))的时候用的(参见 fs/ioctl.c:ioctl_fioasync),相当于用户态调用了fcntl()并修改了O_ASYNC标记。

回调的实现要求是调用fasync_helper()初始化一个async_struct,然后等到有东西需要汇 报给用户态的时候,调用kill_fasync(async_struct, SIGXXX, POLL_XXX)把不同类型的信 号发送给锁指定的进程(默认用SIGIO,从这个信号的选择上,也可以看到这个设计的主要 意图)。另外,fcntl(F_SETOWN_XX)可以指定接收信号的进程,一般是进程组的组长(不是 随便给一个都可以的,不是本组的进程是肯定不行的。这一点,不看代码,从Unix的权限 管理上就可以推断)。

而且这个接口并非主流:Linux_5.1 - Linux Kernel Newbies,已经有新方案在取代它了 。在这个介绍中也专门说了,这个接口只能配合O_DIRECT标记协同使用,并不能用于广泛 的情形。

把这个逻辑一组合,就可以知道,这个功能本质就是提供异步IO的:你通过fcntl()设定要 求AIO,然后不用使用read/write,poll,select了,主线程直接执行你其他的业务流,等 有数据的时候,系统通过发信号来通知你。这基本上可以认为是用于非密集IO的。我设想 主要场景是:比如你有一个温度传感器,大部分时间都不用去读它的数据的,你只是处理 你的主业务,在Socket上收发什么的,除非温度改变,你需要看看现在温度是多少,才接 收一个信号,更新一下,然后就回去继续处理你的主业务了。所以,这个功能用于 O_DIRECT才没有什么违和。而io_uring才是正经解决大数据流的。

换句话说,fasync这个功能啊,确实有很多硬件通过它给用户进程发信号,但这个其实是 用户进程主动要求的,如果用户进程没有主动要求(调用fcntl让你创建async_struct), 这种方法根本无法直接把信号发出去。

所以,如果你是要在设备异常的时候直接强制相关用户进程处理或者退出,还是只能用 send_sig_info()或者force_sig_info()来发送。

所以,我们做设计写代码,不要看见风就是雨,看见有个signal拿起来就用,能跑就完事 ,这样设计跑不了几步的。设计工作大部分时间不是写代码,而是调查啊。

(在这个专栏里面放这么多实操的东西,也是希望读者可以看到,我们说的玄而又玄的所 谓设计,所谓构架,所谓语义,都是非常具体和解决实际问题的,不是你把代码拷贝上来 ,然后说两句高大上的废话那种就叫设计。这个例子中我可一行代码没写,但我一下就干 掉了上百行代码,预期这个设计继续下去,还有在未来影响数千上万行代码,你还能说“设 计”不重要,主要靠堆人力编码就可以了吗?)