用户空间如何处理中断2005

原文链接: https://lwn.net/Articles/127698

原文作者:Corbet

原文时间:2005年3月15日

Peter Chubb一直从事设备驱动程序移植到用户空间的项目。他指出,将驱动程序移到内核之外有很多好处。驱动故障占内核故障相当大的百分比,将驱动程序移植到核外将极大提高整个系统的稳定性。驱动重启和更新也会更容易实现。用户态驱动程序还有助于提供相对稳定的驱动API,这一点很受设备厂商欢迎。

支持用户态驱动程序的大部分功能组件在Linux内核中都已经准备好了。进程【用户态驱动程序】通过将设备IO内存直接映射到进程地址空间上,就可以实现与硬件设备通信;这就是Xserver与显卡的通信方式。但还缺少一个环节:用户态驱动不能处理设备中断。在大多数情况下,缺少中断的支持,驱动程序就不能正常工作,因此用户态驱动就不可能实现。

Peter提交了用户空间中断机制补丁。这种机制简单且容易编程,但也存在一个很大缺陷。

这个机制的基本原理是:用户态驱动打开一个新的/proc文件,例如10号中断,那就是/proc/irq/10/irq;然后读该文件,从而获得最近一次读到这次读之间中断发生的次数;如果最近一次读之后,没有硬件中断发生,此次读操作就会被内核阻塞一直到有一个中断发生。除了读read()操作,也可以支持系统调用select()和poll()操作,这样一来就可以在一次事件循环中即做了中断处理,还做了其他处理。

在内核里,真正的中断处理过程就像下面的样子:

static irqreturn_t irq_proc_irq_handler(int irq, void *vidp, struct pt_regs *regs)
{
    struct irq_proc *idp = (struct irq_proc *) vidp;
    
    BUG_ON(idp->irq != irq);
    disable_irq_nosync(irq);  //在中断上下文中屏蔽中断,不会阻塞
                              //注意和disable_irq的区别,在进程上下文中使用,可能会阻塞
    atomic_inc(&idp->count);  //中断计数
    wake_up(&idp->q);         //唤醒用户态驱动程序的中断处理过程
    return IRQ_HANDLED; 
}

换句话说,内核部分主要是对中断计数,并唤醒一个等待处理中断的进程。

上述过程在返回之前将中断屏蔽。这样做的原因是:内核处理过程对触发中断的设备一无所知,它不能对中断进行确认,也不能关闭中断。因此,内核处理过程返回时,设备依然可以触发中断信号。只要电平触发的中断没有被处理器屏蔽,处理器就会一直地、反复地被中断。因此,内核处理过程首先屏蔽中断让程序继续运行下去,直到用户态驱动程序被调度运行。

这里有一个问题:若干设备常常共享同一个中断。屏蔽一个共享中断就会关闭该中断连接上的所有设备的中断,而不是仅仅关闭某个特定用户态驱动中断处理程序对应的设备上的中断。这样一来,屏蔽一个共享中断就会阻塞用户态处理程序对应的设备,这个设备可能是硬盘。在这种情况下,系统就会死锁。基于这个原因,补丁不适用于共享类型的中断。这种限制可以避免问题发生,但是降低了用户态驱动程序的可用性。

Alan Cox提交了另一个方案。这个方案需要用户态驱动程序给内核传递少量的数据结构,用于描述硬件IRQ接口信息。这样一来,内核能直到究竟是哪个设备被中断了,也可以对中断进行确认,并通知设备关闭中断。这个方案一旦完成,内核处理过程保持中断使能状态,也能让用户态驱动按照设备的需要来处理。他还指出,这种简单的方案面对更复杂的硬件时可能还存在问题,但至少往前迈进了一步。

与此同时,Michael Raymond描述了另外一种用户态中断机制,即用户级中断ULI【这个机制是在SGI计算机上实现的,Silicon Graphis Inc.是美国的创新技术公司】。这个补丁更加复杂。这种机制的用户态驱动直接向内核注册中断处理函数。中断发生时,ULI内核补丁就执行汇编级黑魔法,即用"return from interrupt"跳转指令,跳转到用户态中断处理函数。一旦用户态中断处理函数返回,ULI库给魔法设备写入一段代码,将内核堆栈和相关数据结构恢复到中断之前的状态。整个机制实现更加复杂,当前只能运行在IA-64体系结构上,但是它能提供比/proc方法更好的性能。

你可能感兴趣的:(linux)