假设让内核定期对设备进行轮询。以便处理设备,那会做非常多无用功,假设能让设备在须要内核时主动通知内核,会是一个聪明的方式,这便是中断。

在响应一个特定中断时,内核会运行一个函数——中断处理程序。

中断处理程序与其它内核函数的差别在于,中断处理程序是被内核调用来响应中断的,而它们运行于我们称之为中断上下文的特殊上下文中。

我们期望让中断处理程序运行得快。并想让它完毕的工作量多,这两个目标相互制约,怎样解决——上下半部机制

我们把中断处理切为两半。我们用网卡来解释一下这两半。

当网卡接受到数据包时,通知内核,触发中断。所谓的上半部就是,及时读取数据包到内存。防止由于延迟导致丢失,这是非常急迫的工作。读到内存后,对这些数据的处理不再紧迫,此时内核能够去运行中断前运行的程序,而对网络数据包的处理则交给下半部处理。

我们先来看一下上半部的处理过程。

中断处理程序的注冊与注销

设备驱动程序利用request_irq()注冊中断处理程序。并激活给定的中断线。

int request_irq(unsigned int irq,
irq_handler_t handler,
unsigned long flags,
const char *name,
void *dev)

irq表示中断号,handler是指向中断处理程序的指针。request_irq()成功运行返回0,当返回非0值时,表示有发生错误,中断处理程序不会被注冊。

卸载设备驱动程序时,须要注销对应的中断处理程序,并释放中断线。这时须要调用free_irq——假设在给定的中断线上没有中断处理程序,则注销响应的处理程序。并禁用当中断线。

中断处理机制

下半部严格来说不属于中断处理程序(由于中断返回后再运行下半部),它是中断处理程序用来缩减自身工作的分担者。

上下半部划分原则

(1)假设一个任务对时间非常敏感。将其放在中断处理程序中运行;

(2)假设一个任务和硬件有关,将其放在中断处理程序中运行;

(3)假设一个任务要保证不被其它中断打断。将其放在中断处理程序中运行;

(4)其它全部任务。考虑放置在下半部运行。

上下半部的意义

上半部简单高速。运行时禁止一些或者全部中断。下半部稍后运行,并且运行期间能够响应全部的中断。这样的设计能够使系统处于中断屏蔽状态的时间尽可能的短,以此来提高系统的响应能力。

下半部实现机制之软中断

在中断处理程序中触发软中断是最常见的形式。在这样的情况下。中断处理程序运行硬件设备的相关操作。然后触发对应的软中断,最后退出。

内核在运行完中断处理程序后,立即就会调用do_softirq()函数,于是软中断開始运行中断处理程序留给它去完毕的剩余任务。

软中断注冊方式例如以下:

open_softirq(NET_TX_SOFTIRQ, net_tx_action);

前面的參数是软中断的索引號。后面的是处理函数。软中断处理程序运行时。同意响应中断,但它自己不能休眠。

下半部实现机制之tasklet

tasklet是通过软中断实现的,所以它本身也是软中断。

首先声明自己的tasklet,DECLARE_TASKLET(name, func, data),当该tasklet被调度后。给定的函数func会被运行。它的參数由data给出。接下来定义tasklet处理程序void tasklet_handler(unsigned long data),然后開始调度。tasklet由tasklet_schedule()和tasklet_hi_schedule()进行调度。

tasklet_schedule()的运行步骤:

(1)检查tasklet的状态是否为TASKLET_STATE_SCHED。假设是,说明tasklet已经被调度过了,函数立即返回。

(2)调用_tasklet_schedule()。

(3)保存中断状态,然后禁止本地中断。在我们运行tasklet代码时,这么做能够保证当tasklet_schedule()处理这些tasklet时,处理器上的数据不会弄乱。

(4)把须要调度的tasklet加到每一个处理器一个的tasklet_vec链表或tasklet_hi_vec链表的表头上。

(5)唤醒TASKLET_SOFTIRQ或HI_SOFTIRQ软中断,这样在下一次调用do_softirq()时就会运行该tasklet。

(6)恢复中断到原状态并返回。

下半部实现机制之工作队列(work queue)

假设推后运行的任务须要睡眠。那么就选择工作队列,假设不须要睡眠,那么就选择软中断或tasklet。

工作队列能运行在进程上下文,它将工作委托给一个内核线程。我们用结构体workqueue_struct表示工作者线程,工作者线程是用内核线程实现的。而工作者线程是怎样运行被推后的工作——有这样一个链表。它由结构体work_struct组成,而这个work_struct则描写叙述了一个工作,一旦这个工作被运行完。对应的work_struct对象就从链表上移去,当链表上不再有对象时,工作者线程就会继续休眠。这些逻辑是通过函数worker_thread()实现的:

(1)线程将自己设置为休眠状态。并把自己增加到等待队列中。

(2)假设工作链表是空的。线程调用schedule()函数进入休眠状态。

(3)假设链表中有对象,线程不会休眠。相反。它会脱离等待队列。

(4)假设链表非空,调用run_workqueue()运行被推后的工作。

另外,cpu_workqueue_struct表示一个工作者线程。而workqueue_struct表示一类工作者线程。

创建工作者线程,DECLARE_WORK(name, void (*func) (void *), void *data)或INIT_WORK(struct work_struct *work, void (*func) (void *), void *data),前者是静态创建,后者在运行时通过指针创建。

工作者线程创建了,接下来应该定义它要运行的函数work_handler。之后就是用schedule_work(&work)来调度工作线程的唤醒与休眠。

Linux内核设计基础(一)之中断处理的更多相关文章

  1. Linux内核分析-分析system_call中断处理过程

    姓名:江军 ID:fuchen1994 分析system_call中断处理过程 使用gdb跟踪分析一个系统调用内核函数(您上周选择那一个系统调用),系统调用列表参见http://codelab.shi ...

  2. Linux内核分析——分析system_call中断处理过程

    万子惠 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 我选择的是get ...

  3. Linux内核实现中断和中断处理(二)

    第一部分移步传送门召唤!!:http://www.cnblogs.com/lenomirei/p/5562086.html 上回说了Linux内核实现中断会把中断分为两部分进行处理,上回讲了上部分,这 ...

  4. Linux内核设计基础(十)之内核开发与总结

    (1)Linux层次结构: (2)Linux内核组成: 主要由进程调度(SCHED).内存管理(MM).虚拟文件系统(VFS).网络接口(NET)和进程间通信(IPC)等5个子系统组成. (3)与Un ...

  5. Linux内核实现中断和中断处理(一)

    Linux实现中断处理 内核是怎么知道应用程序要调用系统调用的呢?或者说应用程序怎么通知系统内核自己需要执行一个系统调用,这是通过软中断实现的,通过引发一个异常来促使系统切换到内核态去执行异常处理程序 ...

  6. Linux内核设计基础(三)之定时器和时间管理

    版权声明:本文为博主原创文章.未经博主同意不得转载. https://blog.csdn.net/BlueCloudMatrix/article/details/29294529 内核知道连续两次时钟 ...

  7. Linux内核设计基础(四)之虚拟文件系统

    先来看一下写文件函数write的运行过程: ret = write(fd, buf, len); write适用于各种文件系统.它首先运行sys_write(),而正是这个sys_write()进行实 ...

  8. Linux内核设计基础(五)之内存管理

    我感觉学习操作系统首先要从内存分配和管理入手. 首先我们应该知道现代操作系统是以页为单位进行内存管理的,32位体系结构支持4KB的页.而64位体系结构支持8KB的页.页是用来分配的.怎样才干进行高效和 ...

  9. Linux内核设计基础(九)之进程管理和调度

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/BlueCloudMatrix/article/details/30799225 在Linux中进程用 ...

随机推荐

  1. vue refs v-for 使用注意

    当 v-for 用于元素或组件的时候,引用信息将是包含 DOM 节点或组件实例的数组. https://cn.vuejs.org/v2/api/#ref https://www.w3cplus.com ...

  2. Java多线程的悲观锁与乐观锁

    转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6561376.html  一:悲观锁 悲观锁,就是不管是否发生多线程冲突,只要存在这种可能,就每次访问都加锁,加 ...

  3. emplace_back() 和 push_back 的区别(转)

    在引入右值引用,转移构造函数,转移复制运算符之前,通常使用push_back()向容器中加入一个右值元素(临时对象)的时候,首先会调用构造函数构造这个临时对象,然后需要调用拷贝构造函数将这个临时对象放 ...

  4. 索引全扫描(INDEX FULL SCAN)

    所谓的索引全扫描(INDEX FULL SCAN)就是指要扫描目标索引所有叶子块的所有索引行.这里需要注意的是,索引全扫描需要扫描目标索引的所有叶子块,但这并不意味着需要扫描该索引的所有分支块.在默认 ...

  5. 〖Linux〗穿越城墙之后,直接连接国内网站的路由配置

    因为有需要做Android相关的开发工作,很多时候要穿越之后才能做事情: 如Android文件加密预研.Android NDK/SDK的下载,都需要使用得到Google: 但是穿越之后,访问国内网站就 ...

  6. 给本地服务器配置py文件的下载功能

    打开以下网址 http://localhost/Myservers/test/weibo.py //本地服务器,下载Myservers目录下的test目录中的weibo.py文件 错误提示: HTTP ...

  7. Jenkins卸载方法(Windows/Linux/MacOS)

    注意: 命令行运行的war包或者安装包,都会在命令行上提示了配置文件文件夹.jenkins,卸载时,注意一定要把这些一起删除. 比如Windows下用war包部署的命令行信息如下: 查看原图   如上 ...

  8. C# 多线程并发锁模式-总结

    开篇: 互斥还是lock Monitor Mutex 模式! Muex Monitor lock AutoEventSet ManualEventSet 后续的 ReaderWriterLock   ...

  9. Html Agility Pack - API

    Html Agility Pack - APIParserSelectorsManipulationTraversingWriterUtilitiesAttributes HTML Parser HT ...

  10. Newifi2(D1) 刷入pb-boot和breed的记录

    今天要给一个newifi d1刷系统时发现居然还是原厂的uboot, 使用uboot刷入rom时会进行校验拦截第三方的rom. 之前有刷过这个设备的, 但是已经完全记不清怎么处理的了. 查了一下, 这 ...