linux在应用程序中屏蔽中断,嵌入式Linux驱动程序设计(六)中断处理程序

  • Post author:
  • Post category:linux


if(offset>=__pa(high_memory)||(filp->f_flags&O_SYNC))vma->vm_flags|=VM_IO;vma->vm_flags|=VM_RESERVED;if (remap_page_range(vma, vma->vm_start, offset,vma->vm_end-vma->vm_start,vma->vm_page_prot))return -EAGAIN;return 0;}我们看到这里面只调用了一个remap_page_range(vma, vma->vm_start, offset,vma->vm_end-vma->vm_start,vma->vm_page_prot)函数。3.2.6中断实现及信号量问题前

面提到过 设备每20ms就会产生一个中断,我们拟用了9号中断,并且不共享。

在linux处理中断的方法有了较大的改变。早期PC中的中断处理很简单,仅仅涉及到一个处理器和16条中断信号线。而现在硬件可以拥有更多的中断,并且

还可能配有奇特的高级可编程中断控制器,该控制器可以智能的(和可编程)的方式在多个处理器之间分发中断。Linux下使用中断资源的过程:安装中断处理程序,设备驱动程序通过调用request_irq函数来申请中断,通过free_irq来释放中断。它们的定义为:#includeint request_irq(unsigned int irq,void (*handler)(int irq,void dev_id,struct pt_regs *regs),unsigned long flags,const char *device,void *dev_id);void free_irq(unsigned int irq, void *dev_id);参数irq表示所要申请的硬件中断号。handler为向系统登记的中断处理子程序,中断产生时由系统来调用,调用时所带参数irq为中断号,dev_id为申请时告诉系统的设备标识,regs为中断发生时寄存器内容。device为设备名,将会出现在/proc/interrupts文件里。flag是申请时的选项,它决定中断处理程序的一些特性,其中最重要的是中断处理程序是快速处理程序(flag里设置了SA_INTERRUPT)还是慢速处理程序(不设置SA_INTERRUPT),快速处理程序运行时,所有中断都被屏蔽,而慢速处理程序运行时,除了正在处理的中断外,其它中断都没有被屏蔽。在LINUX系统中,中断可以被不同的中断处理程序共享,这要求每一个共享此中断的处理程序在申请中断时在flags里设置SA_SHIRQ,这些处理程序之间以dev_id来区分。如果中断由某个处理程序独占,则dev_id可

以为NULL。request_irq返回0表示成功,返回-INVAL表示irq>15或handler==NULL,返回-EBUSY表示中断

已经被占用且不能共享。对于驱动程序来说,我们可以通过查看/proc/interrupt文件来确定我们是否申请到了irq。也是这个文件,可以告诉我

们设备要申请的中断号是可否用。

但这里还牵涉到一个技术问题,那就是我们如何来检测我们设备使用的是哪个中断号呢?也就是说我们可以自动检测设备irq号吗?linux内核支持

这种自动检测。内核提供了一个自动检测函数probe_irq_on,它只能在非共享模式下工作。这个函数返回一个未分配中断的位掩码。驱动程序必须返回

未掩码,并且将它传递给后面的probe_irq_off函数,调用该函数后,驱动程序要设法安排设备发生一次中断。对probe_irq_off函数,

是在请求设备产生中断后,被驱动程序调用,并使用前面probe_irq_on函数所产生的位掩码作为参数。这个函数返回probe_irq_on之后中

断发生的次数。在内核帮助下自动检测irq号过程:调用probe_irq_on函数安排设备产生一次中断调用probe_irq_off函数 根据probe_irq_off函数的返回值来判断设备所使用的中断号。当然,除了这种在内核帮助下自动检测irq号,我们还可以手动检测设备的irq。其原理为:1.启用所有未被使用的中断号,同时循环的调用request_irq申请所有的这些中断号,并2.在中断处理程序里做好标志3.设法安排设备产生一次中断4.根据中断处理程序里的标志来判断中断处理程序是否运行,从而确定中断号5.释放中断号中断处理程序的实现:中断处理程序事实上还是一个普通的函数,只是我们要注意中断处理程序是

在中断期间运行的,具有最高的优先级别。还有中断处理程序必须要在尽量短的时间内完成,这对于那些需在中断期间内完成耗时的时候是一个问题。在linux

下可以使用tasket和底半部处理来解决这个问题,在这里我们不加以详细的阐述了,我们只具体谈谈我们的中断处理程序的实现。前面提到过,在方案1里中断处理程序还需将数据拷贝到缓冲区中,而在方案2中这个都不用实现了,那中断处理程序到底都做了什么呢? 事实上我们只作了一件事—那就是发送一个信号给我们的应用程序,通知它来处理数据。具体实现为:kill_fasync(&(dev->async_queue),SIGIO,POLL_IN);这是一个进程异步通知函数。那么驱动程序究竟是如何实现异步信号传输的呢?下面我们从内核角度来看的详细操作过程:当 F_SETOWN被调用时,对filp->f_owner赋值,此外什么也不做。在执行F_SETFL以启用FASYNC时,调用驱动程序的fasync方法。只要filp->f_flags中的FASYNC标志发生了变化,就会调用该方法,以便把这个变化通知驱动程序,使其能正确响应。文件打开时,FASYNC标志被默认为清除。当 数据到达时,发送一个信号给所有注册为异步通知的进程。至于发送什么信号由上层应用程序决定,默认为  SIGIO信号。在这里你也许会问,应用程序如何成为设备的异步访问者呢? 这个就是驱动程序中异步函数的实现过程了。在我的驱动中, fasync方法的实现为:int mylubo_fasync(int fd,struct file *filp,int mode){    mylubo *dev=filp->private_data;return fasync_helper(fd,filp,mode,&dev->async_queue);}关

于应用程序具体如何使用异步传输机制,请看源代码。下面我们来讨论关于信号的传输问题。我们也许会问,驱动程序发送给应用程序后信号会丢失吗?会很及时的

发送给应用程序吗?如果不那样的话,那我们的数据就有可能被洗掉了。这个也就是上面我所说过的使用mmap时,可能会导致数据丢失的根源所在。我曾经对

linux下的信号量机制作过一些研究,下面就是我对信号的理解:一、 信号本质信

号是在软件层次上对中断机制的一种模拟,在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。信号是异步的,一个进程不必通过任何操

作来等待信号的到达,事实上,进程也不知道信号到底什么时候到达。信号是进程间通信机制中唯一的异步通信机制,可以看作是异步通知,通知接收信号的进程有

哪些事情发生了。信号机制经过POSIX实时扩展后,功能更加强大,除了基本通知功能外,还可以传递附加信息。[url=]二、信号的种类[/url]可以从两个不同的分类角度对信号进行分类:(1)可靠性方面:可靠信号与不可靠信号;(2)与时间的关系上:实时信号与非实时信号。1、可靠信号与不可靠信号”不可靠信号”Linux信

号机制基本上是从Unix系统中继承过来的。早期Unix系统中的信号机制比较简单和原始,后来在实践中暴露出一些问题,因此,把那些建立在早期机制上的

信号叫做”不可靠信号”,信号值小于SIGRTMIN(Red hat

7.2中,SIGRTMIN=32,SIGRTMAX=63)的信号都是不可靠信号。这就是”不可靠信号”的来源。它的主要问题是:进程每次处理信号后,就将对信号的响应设置为默认动作。在某些情况下,将导致对信号的错误处理;因此,用户如果不希望这样的操作,那么就要在信号处理函数结尾再一次调用signal(),重新安装该信号。信号可能丢失,后面将对此详细阐述。

此,早期unix下的不可靠信号主要指的是进程可能对信号做出错误的反应以及信号可能丢失。

Linux支持不可靠信号,但是对不可靠信号机制做了改进:在调用完信号处理函数后,不必重新调用该信号的安装函数(信号安装函数是在可靠机制上的实

现)。因此,Linux下的不可靠信号问题主要指的是信号可能丢失。”可靠信号”随

着时间的发展,实践证明了有必要对信号的原始机制加以改进和扩充。所以,后来出现的各种Unix版本分别在这方面进行了研究,力图实现”可靠信号”。由于

原来定义的信号已有许多应用,不好再做改动,最终只好又新增加了一些信号,并在一开始就把它们定义为可靠信号,这些信号支持排队,不会丢失。同时,信号的

发送和安装也出现了新版本:信号发送函数sigqueue()及信号安装函数sigaction()。POSIX.4对可靠信号机制做了标准化。但

是,POSIX只对可靠信号机制应具有的功能以及信号机制的对外接口做了标准化,对信号机制的实现没有作具体的规定。信号值位于SIGRTMIN和

SIGRTMAX之间的信号都是可靠信号,可靠信号克服了信号可能丢失的问题。Linux在支持新版本的信号安装函数sigation()以及信号发送函

数sigqueue()的同时,仍然支持早期的signal()信号安装函数,支持信号发送函数kill()。[url=]2[/url]、实时信号与非实时信号早期Unix系统只定义了32种信号,Ret hat7.2支持64种信号,编号0-63(SIGRTMIN=31,SIGRTMAX=63),将来可能进一步增加,这需要得到内核的支持。前32种信号已经有了预定义值,每个信号有了确定的用途及含义,并且每种信号都有各自的缺省动作。如按键盘的CTRL ^C时,会产生SIGINT信号,对该信号的默认反应就是进程终止。后32个信号表示实时信号,等同于前面阐述的可靠信号。这保证了发送的多个实时信号都被接收。实时信号是POSIX标准的一部分,可用于应用进程。非实时信号都不支持排队,都是不可靠信号;实时信号都支持排队,都是可靠信号。[url=]三、进程对信号的响应[/url]进程可以通过三种方式来响应一个信号:(1)忽略信号,即对信号不做任何处理,其中,有两个信号不能忽略:SIGKILL及SIGSTOP;(2)捕捉信号。定义信号处理函数,当信号发生时,执行相应的处理函数;(3)执行缺省操作,Linux对每种信号都规定了默认操作当一个实时信号发送给一个进程时,不管该信号是否已经在进程中注册,都会被再注册一次,因此,信号不会丢失,因此,实时信号又叫做”可靠信号”。这意味着同一个实时信号可以在同一个进程的未决信号信息链中占有多个sigqueue结构(进程每收到一个实时信号,都会为它分配一个结构来登记该信号信息,并把该结构添加在未决信号链尾,即所有诞生的实时信号都会在目标进程中注册);当一个非实时信号发送给一个进程时,如果该信号已经在进程中注册,则该信号将被丢弃,造成信号丢失。因此,非实时信号又叫做”不可靠信号”。这意味着同一个非实时信号在进程的未决信号信息链中,至多占有一个sigqueue结构(一个非实时信号诞生后,(1)、如果发现相同的信号已经在目标结构中注册,则不再注册,对于进程来说,相当于不知道本次信号发生,信号丢失;(2)、如果进程的未决信号中没有相同信号,则在进程中注册自己)。四 信号的执行时间1.

在进程中的注销。在目标进程执行过程中,会检测是否有信号等待处理(每次从系统空间返回到用户空间时都做这样的检查)。如果存在未决信号等待处理且该信号

没有被进程阻塞,则在运行相应的信号处理函数前,进程会把信号在未决信号链中占有的结构卸掉。是否将信号从进程未决信号集中删除对于实时与非实时信号是不

同的。对于非实时信号来说,由于在未决信号信息链中最多只占用一个sigqueue结构,因此该结构被释放后,应该把信号在进程未决信号集中删除(信号注

销完毕);而对于实时信号来说,可能在未决信号信息链中占用多个sigqueue结构,因此应该针对占用sigqueue结构的数目区别对待:如果只占用

一个sigqueue结构(进程只收到该信号一次),则应该把信号在进程的未决信号集中删除(信号注销完毕)。否则,不应该在进程的未决信号集中删除该信

号(信号注销完毕)。

进程在执行信号相应处理函数之前,首先要把信号在进程中注销。2.生命终止。进程注销信号后,立即执行相应的信号处理函数,执行完毕后,信号的本次发送对进程的影响彻底结束。由以上的分析知,我们应该采用实时信号,以防止信号丢失;而且由于信号是在每次系统执行了从内核空间返回到用户空间都会执行信号的检测,因此采用信号作为传输介质是值得信赖的,一般不会造成很大的延迟。