您的位置 首页 电子

需求了解Linux设备驱动之中止处理

需要了解Linux设备驱动之中断处理-中断(interrupt)是指CPU在执行程序的过程中,出现了某些突发事件急待处理,CPU必须暂停执行当前的程序,转去处理突发事件,处理完毕后CPU又返回原程序被中断的位置并继续执行。

中止(interrupt)是指CPU在履行程序的过程中,呈现了某些突发事件急待处理,CPU有必要暂停履行当时的程序,转去处理突发事件,处理完毕后CPU又回来原程序被中止的方位并持续履行。

中止服务程序的履行并不存在于进程上下文,因而,要求中止服务程序的时刻尽或许地短。因而,Linux在中止处理中引入了顶半部和底半部别离的机制。顶半部处理紧迫的硬件操作,底半部处理不紧迫的耗时操作。
tasklet和作业行列都是调度中止底半部的杰出机制,tasklet依据软中止完结,原子操作,速度快,常用,但不行堵塞或睡觉。

中止分类

依据中止的来历,可分为内部中止和外部中止

内部中止的中止源来自CPU内部(软件中止指令、溢出、除法过错等,例如,操作体系从用户态切换到内核态需凭借CPU内部的软件中止),外部中止的中止源来自CPU外部,由外设提出恳求。

依据中止是否能够屏蔽分为可屏蔽中止与不屏蔽中止(NMI

可屏蔽中止能够经过屏蔽字(MASK)被屏蔽,屏蔽后,该中止不再得到呼应,而不屏蔽中止不能被屏蔽。

依据中止进口跳转办法的不同,分为向量中止和非向量中止

选用向量中止的CPU一般为不同的中止分配不同的中止号,当检测到某中止号的中止到来后,就主动跳转到与该中止号对应的地址履行。不同中止号的中止有不同的进口地址。非向量中止的多个中止同享一个进口地址,进入该进口地址后再经过软件判别中止标志来辨认具体是哪个中止。也就是说,向量中止由硬件供给中止服务程序进口地址,非向量中止由软件供给中止服务程序进口地址。

Linux中止处理(Interrupt Handling)架构

设备的中止会打断内核中进程的正常调度和运转,体系对更高吞吐率的寻求必然要求中止服务程序尽或许的言简意赅。为了在中止履行时刻尽或许短和中止处理需完结很多作业之间找到一个平衡点, Linux 将中止处理程序分解为两个半部:顶半部(top half)和底半部(bottom half)。

top half

顶半部完结尽或许少的比较紧迫的功用,它往往仅仅简略地读取寄存器中的中止状况并铲除中止标志后就进行“挂号中止”的作业。“挂号中止”意味着将底半部处理程序挂到该设备的底半部履行行列中去。这样,顶半部履行的速度就会很快,能够服务更多的中止恳求。软件上一般选用handler中止呼应程序完结。

bottom half

底半部由顶半部调度而来进行拖延处理,简直做了中止处理程序一切的作业,并且能够被新的中止打断,这也是底半部和顶半部的最大不同,由于顶半部往往被规划成不行中止。底半部则相对来说并不是十分紧迫的,并且相对比较耗时,不在硬件中止服务程序中履行。软件上一般选用tasklet或作业行列机制。

Tip:虽然顶半部、底半部的结合能够改进体系的呼应才能,可是,死板地以为 Linux 设备驱动中的中止处理一定要分两个半部则是不对的。假如中止要处理的作业自身很少,则完全能够直接在顶半部悉数完结。

Linux中止编程

请求和开释(Installing an Interrupt Handler)

#include int /* 回来 0 — OK, -EINVAL — irq/handler invalid, -EBUSY — 中止被占用不能同享 */ request_irq( unsigned int irq, /* 要请求的硬件中止号 */ irq_handler_t handler, /* 向体系挂号的中止处理函数(顶半部)*/ unsigned long flags, /* 中止处理的特点,能够指定中止的触发方法以及处理方法等 */ const char *devname, /* used in /proc/interrupts */ void *dev_id /* 传递给handler的参数 */ ); void free_irq(unsigned int irq,void *dev_id);

TIp: 假如中止确认不被同享可将其装置在初始化中,否则应装置在翻开函数中。interrupt.h有irq_handler_t的界说及flags的具体注释

使能和屏蔽 (Enabling and Disabling Interrupts)

Disabling a single interrupt

#include /* disable并等候指定的中止被处理完,假如调用线程占有interrupt handler需求的资源如spinlock那么就会死 */void disable_irq(int irq); /* disable并当即回来,有或许发生竞态 */void disable_irq_nosync(int irq);void enable_irq(int irq);

Disabling all interrupts

#include void local_irq_save(unsigned long flags); /* save flags then disable local all */void local_irq_disable(void); /* disable local all directly */void local_irq_restore(unsigned long flags);void local_irq_enable(void);

底半部机制

tasklet

void my_tasklet_func(unsigned long); /*界说一个处理函数*//* 界说一个tasklet结构my_tasklet,并与my_tasklet_func(data)处理函数相关联 */DECLARE_TASKLET(my_tasklet, my_tasklet_func, data);/* 在需求调度tasklet的时分引证一个tasklet_schedule()函数就能使体系在恰当的时分进行调度运转 一般在top half即中止呼应函数中调用 */tasklet_schedule(&my_tasklet);

作业行列(workqueues)
与tasklet相似:

void my_wq_func(unsigned long); /*界说一个处理函数*/struct work_struct my_wq; /*界说一个作业行列*//* 初始化作业行列并将其与处理函数绑定 */INIT_WORK(&my_wq, (void (*)(void *)) my_wq_func, NULL);schedule_work(&my_wq);/*调度作业行列履行*/

TIp: tasklet在中止上下文中履行,不行堵塞或睡觉;而workqueues由内核线程去履行,属进程上下文,可堵塞和睡觉。

 

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/qiche/dianzi/90303.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部