0
点赞
收藏
分享

微信扫一扫

linux - 中断子系统概论

      写这篇文章的目的,就是对linux中断子系统的概论,扫盲!后面会以此为专栏,详细扒代码去研究中断子系统。

      刚接触中断时,还是在大学阶段玩51单片机的时候,最基础的概念就不用介绍了,中断的原理及作用这些相信能看到这篇博客的同学都懂,我就不再赘述!

1.linux对中断的扩展

     linux将中断分为:硬件中断 & 软件中断

1.1硬件中断

       按键中断等硬件产生的中断,称之为“硬件中断”(hard irq)。每个硬件中断都有对应的处理函数,比如按键中断、网卡中断的处理函数肯定不一样。

        为方便理解,你可以先认为对硬件中断的处理是用数组来实现的,数组里存放的是函数指针(下图仅供参考,实际上内部实现比这个复杂许多):

linux - 中断子系统概论_软件中断

      当发生A中断时,对应的irq_function_A函数被调用。硬件导致该函数被调用。

1.2软件中断

      相对应的,还可以人为制造中断,这就引出了另一个重要的概念:软件中断

linux - 中断子系统概论_linux中断子系统_02

      概念有了,就会有疑问:

      Q & A环节:

      Q1:何时发生?

      A1:由软件决定,对于X号软件中断,只需要将flag_x置1,就表示要发生中断

      Q2:软件中断何时处理?

      A2:软件中断的优先级相较于硬件中断普遍不高,因此只有在CPU有空再去处理。这个回答肯定会产生一个新的疑问:什么时候有空?,linux操作系统中,各种硬件中断发生的很频繁,至少定时器中断每10ms发生一次(心跳),所以在处理完硬件中断后,再去处理软件中断

2.中断的处理原则

2.1原则一:不可嵌套

中断处理函数需要调用函数,这就需要用到栈。

中断A正在处理的过程中,假设又发生了中断B,那么在栈里要保存A的现场,然后处理B。

在处理B的过程中又发生了中断C,那么在栈里要保存B的现场,然后处理C。

如果中断嵌套突然暴发,那么栈将越来越大,栈终将耗尽。

所以,为了防止这种情况发生,也是为了简单化中断的处理,在Linux系统上中断无法嵌套:即当前中断A没处理完之前,不会响应另一个中断B(即使它的优先级更高)。

2.2原则二:越快越好

      为什么越快越好?这是韦神举的栗子:

妈妈在家中照顾小孩时,门铃响起,她开门取快递:这就是中断的处理。她取个快递敢花上半天吗?不怕小孩出意外吗?

      在单芯片系统中,例如MCU,假设中断处理程序冗长,那应用程序在这段时间内就无法执行:系统显得很迟顿。

     在SMP系统中,假设中断处理很慢,那么正在处理这个中断的CPU上的其他线程也无法执行。

     在中断的处理过程中,中断产生的CPU是不能进行调度的,所以中断的处理要越快越好,尽早让其他中断能被处理──进程调度靠定时器中断来实现。

     在linux系统中使用中断比较简单,为某个中断irq注册中断处理函数handler,可以使用request_irq函数:

linux - 中断子系统概论_中断处理_03

     handler就是需要注册的中断处理函数,irq_handler_t就是一个返回值为irqreturn_t类型的函数指针数据类型!

typedef irqreturn_t (*irq_handler_t)(int, void *);

      但是还存在一个问题,某些中断要做的事情就是很多,没办法加快,例如按键中断,在此期间需要几十ms的消抖,难道要在handler中等待么?这对于现今的操作需系统来说,是不可接受的。

3.中断过程的拆分

     针对2.2中结尾提出的问题,linux中断系统的维护者们提出了将中断分为上半部、下半部,按中断的紧急程度来区分;在handler里只处理紧凑的事情(关闭中断),耗时操作的处理时就打开中断

linux - 中断子系统概论_软件中断_04

3,1中断下半部

     中断下半部的实现有很多方法,概述两种主要的:小任务&工作队列

3.1.1小任务(tasklet)

     小任务是处理操作耗时但可以忍受的方法,tasklet的实现是借助软件中断实现的,因为是概述,所以只做原理性介绍,后续需要大量看代码来梳理。总结一下:

A. 中断上半部,用来处理紧急的事,需要关闭中断

B. 中断下半部,用来处理耗时的、不那么紧急的事,它是在开中断的状态下执行的

C. 中断下半部执行时,有可能会被多次打断,有可能会再次发生同一个中断

D. 中断上半部执行完后,触发中断下半部的处理

E. 中断上半部、下半部的执行过程中,不能休眠

因为中断上下文(执行上半部、下半部函数时)切换是,CPU会进入内核空间,这个过程中,硬件的 一些变量和参数也要传递给内核,内核通过这些参数进行中断处理,即所谓的“ 中断上下文”。在此期间如果让CPU休眠或者阻塞,将无法退出这种状态,直接导致内核僵死!

3.1.2工作队列

     动作队列针对操作复杂,耗时时间长的handler。针对这种情况,就不能再用中断下半部来做,需要使用内核线程来完成这个任务。

     这个线程式系统帮我们创建的,一般是worker线程,内核有很多这样的线程

在console内使用如下命令:ps -A | grep kworker

     kworker线程需要在“工作队列(worke queue)”上去申请一个work,来执行它内部的函数。总结一下流程:

     A.在中断上半部调用schedule_work(),触发work处理

     B.声明一下,既然将任务放到了线程里面运行,那对应的任务可以休眠

4.参考文章

    1.​​Linux中断机制 - Edver - 博客园 (cnblogs.com)​​


举报

相关推荐

0 条评论