如果让内核定期对设备进行轮询,以便处理设备,那会做很多无用功,如果能让设备在需要内核时主动通知内核,会是一个聪明的方式,这便是中断。
在响应一个特定中断时,内核会执行一个函数——中断处理程序。中断处理程序与其他内核函数的区别在于,中断处理程序是被内核调用来响应中断的,而它们运行于我们称之为中断上下文的特殊上下文中。
我们期望让中断处理程序运行得快,并想让它完成的工作量多,这两个目标相互制约,如何解决——上下半部机制。
我们把中断处理切为两半。我们用网卡来解释一下这两半。当网卡接受到数据包时,通知内核,触发中断,所谓的上半部就是,及时读取数据包到内存,防止因为延迟导致丢失,这是很急迫的工作。读到内存后,对这些数据的处理不再紧迫,此时内核可以去执行中断前运行的程序,而对网络数据包的处理则交给下半部处理。
我们先来看一下上半部的处理过程。
中断处理程序的注册与注销
设备驱动程序利用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内核中断处理.doc
把内核中断的流程说的很清楚,学习Linux的必备资料
Linux内核中断机制,值得看看啊。 Linux内核中断机制,值得看看啊。 Linux内核中断机制,值得看看啊。 Linux内核中断机制,值得看看啊。
Linux内核情景分析之三中断和函数调用 Linux内核情景分析之三中断和函数调用
Linux内核中断分析,希望对初学者有所帮助!
linux内核中断实例,可能通过模块参数插入指定的中断中
将Linux内核的修炼之道总结为四个层次:第一层次目的是对Linux以及内核有个全面的认识和了解,掌握Linux内核源代码的分析方法。第二个层次讨论了内核中系统初始化、系统调用、中断处理、进程管理及调度、内存管理、...
linux内核源代码分析中断处理程序与内核同步ppt课件.ppt
Linux内核的中断机制分析 Linux内核的中断机制分析
LINUX内核经典面试题 ,20) 如何加载、卸载一个模块? 21) 模块和应用程序分别运行在什么空间? 22) Linux中的浮点运算由应用程序实现还是内核实现? 23) 模块程序能否使用可链接的库函数? 24) TLB中缓存的是什么...
linux内核的时钟中断详解,介绍内核时钟中断知识
---------------------------------------------------Linux 中的各种栈:进程栈 线程栈 内核栈 中断栈
《Linux内核设计与实现》 第一章 读书笔记 Linux内核简介 面试被怼了Linux内核,于是决定好好看一下这本书。作为经典书籍,Linux内核设计与实现是一本很重要的书籍。在大学本科的课程中已经学习过有关操作系统的内容...
将Linux内核的修炼之道总结为四个层次:第一层次目的是对Linux以及内核有个全面的认识和了解,掌握Linux内核源代码的分析方法。第二个层次讨论了内核中系统初始化、系统调用、中断处理、进程管理及调度、内存管理、...
1.3走进Linux内核 1.4 分析Linux内核的意义 1.5 Linux内核结构 1.6 Linux内核源代码 1.7 Linux内核源代码分析工具 第二章 Linux运行的硬件基础 2.1 i386的寄存器 2.2 内存地址 2.3 段机制和描述符 2.4 分页机制 2.5 ...
《Linux内核API完全参考手册》以最新的Linux内核版本2.6.3 0为依据,对常用的内核API作了系统分析和归纳,设计了典型实例并对开发场景进行了详细讲解。《Linux内核API完全参考手册》中分析的内核API模块包括:内核...
linux内核知识系列:软中断 华嵌智能提供 www.embedded-cn.com http://embedded-cn.taobao.com
《Linux内核修炼之道》语言通俗易懂,内容覆盖了内核的学习方法到内核设计与实现等各方面内容,能够带领读者快速走入Linux内核的世界,适合对Linux内核学习茫然的初学者,也适合各类希望深入理解Linux内核的读者。
Linux操作系统内核的时钟中断机制.pdf
《深入分析Linux内核源代码》 第一章 走进Linux 第二章 Linux运行的硬件基础 第三章 中断机制 第四章 进程描述 第五章 进程调度与切换 第六章 Linux 内存管理 第七章 进程间通信 第八章 虚拟文件系统 第九章 Ext2 ...