仓库源文

.. Kenneth Lee 版权所有 2019-2020

:Authors: Kenneth Lee :Version: 1.0

从内核终止用户态程序的IO访问


本文调查这个问题的解决方案:如果我暴露一个设备的IO空间给用户态的程序,这个设备 出了异常,我如何可以终止用户态的程序,不让它继续访问这个设备。

我发现设备异常,无论是设备自己报错,还是通过RAS系统报错,反映出来肯定是中断。所 以我肯定是从中断开始做我的处理,这是基本假设。

发生异常后需要立即通知用户程序终止对设备的访问,我第一个考虑是用是kill_pid(pid, SIGXXX, 1),发一个信号给用户进程。但这是异步的,要等到用户程序被中断了,或者从 系统调用中返回才会起作用。当我收到中断的时候,用户进程大可以正在工作在另一个核 的用户态上,不会被打断。这时如果我回收设备的相关资源,肯定不妥。

第二种方法是把mmap的IO空间的页表给它回收了,但这个更危险,因为这要主动拿到这个 进程的vma,然后在另一个CPU的上下文中操作它。这从锁的角度也很不安全,估计要改变 不少内核的锁设计上的假设,不到万不得已,我们不考虑走这条路。

这些方法的问题是没法立即打断那个执行的进程,而很明显,要立即打断某个被执行的进 程,唯一的手段是给它所在CPU发一个中断,比如发一个IPI。但收到一个中断,然后发起 一个IPI,这个事情本身就是异步的。既然不能通过同步方式立即终止一个进程,快一点慢 一点又有什么所谓?反正在(设备)异常发生的时候,用户进程怎么都得走几步的。

所以,这种情形需要做的设计是这样的:

首先硬件接口必须能保证,无论硬件发生什么异常,用户进程做任何硬件动作,最多没反 应,但不能造成设备本身状态的异常。

其次,在设备异常后,通过信号通知进程退出,但在进程没有被杀死前,设备必须处于一 种“不可操作状态”直到所有使用了设备的进程关闭关联为止。

最后,在所有进程关闭了关联后,复位设备。

其中,第二步需要进程的配合,如果某个进程恶意在收到中断后,既不退出,也不关闭和 设备的关联(比如不关闭对应的文件句柄),我们有两个选择:第一,在设备驱动上设置 一种配置属性,如果这个属性被配置了,直接发SIGKILL,这样进程一定会死。就没有这个 问题了。第二,等管理员主动去杀掉它。

用户进程的推荐算法如下:::

    atomic_flag = 0

    def sighandler():
      if it_is_device_reset:
          atomic_flag = 1

    def device_loop():
      fd = open(device)
      mem = mmap(fd)
      while atomic_flag:
        device_io(mem)
        close(fd)

这个方案应该比较保险了吧?