把握linux内核设计思想(二):硬中断及中断处理
【版权声明:尊重原创。转载请保留出处:blog.csdn.net/shallnet,文章仅供学习交流,请勿用于商业用途】
操作系统负责管理硬件设备。为了使系统和硬件设备的协同工作不减少机器性能。系统和硬件的通信使用中断的机制,也就是让硬件在须要的时候向内核发出信号,这样使得内核不用去轮询设备而导致做非常多无用功。
也就是说,内核随时可能由于新到来的中断而被打断。
当接收到一个中断后,中断控制器会给处理器发送一个电信号,处理器检測到该信号便中断自己当前工作而处理中断。
中断处理程序运行与中断上下文,中断上下文中运行的代码不可堵塞,应该高速运行,这样才干保证尽快恢复被中断的代码的运行。中断处理程序是管理硬件驱动的驱动程序的组成部分,假设设备使用中断。那么对应的驱动程序就注冊一个中断处理程序。
extern int __must_check
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
const char *name, void *dev);
static inline int __must_check
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
const char *name, void *dev)
{
return request_threaded_irq(irq, handler, NULL, flags, name, dev);
} int request_threaded_irq(unsigned int irq, irq_handler_t handler,
irq_handler_t thread_fn, unsigned long irqflags,
const char *devname, void *dev_id)
{
struct irqaction *action;
struct irq_desc *desc;
int retval;
/*
* handle_IRQ_event() always ignores IRQF_DISABLED except for
* the _first_ irqaction (sigh). That can cause oopsing, but
* the behavior is classified as "will not fix" so we need to
* start nudging drivers away from using that idiom.
*/
if ((irqflags & (IRQF_SHARED|IRQF_DISABLED)) ==
(IRQF_SHARED|IRQF_DISABLED)) {
pr_warning(
"IRQ %d/%s: IRQF_DISABLED is not guaranteed on shared IRQs\n",
irq, devname);
}
#ifdef CONFIG_LOCKDEP
/*
* Lockdep wants atomic interrupt handlers:
*/
irqflags |= IRQF_DISABLED;
#endif
/*
* Sanity-check: shared interrupts must pass in a real dev-ID,
* otherwise we'll have trouble later trying to figure out
* which interrupt is which (messes up the interrupt freeing
* logic etc).
*/
if ((irqflags & IRQF_SHARED) && !dev_id)
return -EINVAL;
desc = irq_to_desc(irq);
if (!desc)
return -EINVAL;
if (desc->status & IRQ_NOREQUEST)
return -EINVAL;
if (!handler) {
if (!thread_fn)
return -EINVAL;
handler = irq_default_primary_handler;
}
//分配一个irqaction
action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
if (!action)
return -ENOMEM;
action->handler = handler;
action->thread_fn = thread_fn;
action->flags = irqflags;
action->name = devname;
action->dev_id = dev_id;
chip_bus_lock(irq, desc); //将创建并初始化完在的action增加desc
retval = __setup_irq(irq, desc, action);
chip_bus_sync_unlock(irq, desc);
if (retval)
kfree(action);
#ifdef CONFIG_DEBUG_SHIRQ
if (irqflags & IRQF_SHARED) {
/*
* It's a shared IRQ -- the driver ought to be prepared for it
* to happen immediately, so let's make sure....
* We disable the irq to make sure that a 'real' IRQ doesn't
* run in parallel with our fake.
*/
unsigned long flags;
disable_irq(irq);
local_irq_save(flags);
handler(irq, dev_id);
local_irq_restore(flags);
enable_irq(irq);
}
#endif
return retval;
}
/*
* XXX Interrupt pin #7 in Espresso is shared between RTC and
* PCI Slot 2 INTA# (and some INTx# in Slot 1).
*/
if (request_irq(rtc_irq, rtc_interrupt, IRQF_SHARED, "rtc",
(void *)&rtc_port)) {
rtc_has_irq = 0;
printk(KERN_ERR "rtc: cannot register IRQ %d\n", rtc_irq);
return -EIO;
}
/*
* A very tiny interrupt handler. It runs with IRQF_DISABLED set,
* but there is possibility of conflicting with the set_rtc_mmss()
* call (the rtc irq and the timer irq can easily run at the same
* time in two different CPUs). So we need to serialize
* accesses to the chip with the rtc_lock spinlock that each
* architecture should implement in the timer code.
* (See ./arch/XXXX/kernel/time.c for the set_rtc_mmss() function.)
*/
static irqreturn_t rtc_interrupt(int irq, void *dev_id)
{
/*
* Can be an alarm interrupt, update complete interrupt,
* or a periodic interrupt. We store the status in the
* low byte and the number of interrupts received since
* the last read in the remainder of rtc_irq_data.
*/
spin_lock(&rtc_lock); //保证rtc_irq_data不被SMP机器上其它处理器同一时候訪问
rtc_irq_data += 0x100;
rtc_irq_data &= ~0xff;
if (is_hpet_enabled()) {
/*
* In this case it is HPET RTC interrupt handler
* calling us, with the interrupt information
* passed as arg1, instead of irq.
*/
rtc_irq_data |= (unsigned long)irq & 0xF0;
} else {
rtc_irq_data |= (CMOS_READ(RTC_INTR_FLAGS) & 0xF0);
}
if (rtc_status & RTC_TIMER_ON)
mod_timer(&rtc_irq_timer, jiffies + HZ/rtc_freq + 2*HZ/100);
spin_unlock(&rtc_lock);
/* Now do the rest of the actions */
spin_lock(&rtc_task_lock); //避免rtc_callback出现系统情况,RTC驱动同意注冊一个回调函数在每一个RTC中断到来时运行。
if (rtc_callback)
rtc_callback->func(rtc_callback->private_data);
spin_unlock(&rtc_task_lock);
wake_up_interruptible(&rtc_wait);
kill_fasync(&rtc_async_queue, SIGIO, POLL_IN);
return IRQ_HANDLED;
}
对于每条中断线。处理器都会跳到相应的一个唯一的位置。这样,内核就能够知道所接收中断的IRQ号了。初始入口点仅仅是在栈中保存这个号,并存放当前寄存器的值(这些值属于被中断的任务);然后,内核调用函数do_IRQ().从这里開始。大多数中断处理代码是用C写的。
do_IRQ()的声明例如以下:
/**
* handle_IRQ_event - irq action chain handler
* @irq: the interrupt number
* @action: the interrupt action chain for this irq
*
* Handles the action chain of an irq event
*/
irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action)
{
irqreturn_t ret, retval = IRQ_NONE;
unsigned int status = 0; //假设没有设置IRQF_DISABLED。将CPU中断打开。应该尽量避免中断关闭情况,本地中断关闭情况下会导致中断丢失。
if (!(action->flags & IRQF_DISABLED))
local_irq_enable_in_hardirq(); do { //遍历执行中断处理程序
trace_irq_handler_entry(irq, action);
ret = action->handler(irq, action->dev_id);
trace_irq_handler_exit(irq, action, ret); switch (ret) {
case IRQ_WAKE_THREAD:
/*
* Set result to handled so the spurious check
* does not trigger.
*/
ret = IRQ_HANDLED; /*
* Catch drivers which return WAKE_THREAD but
* did not set up a thread function
*/
if (unlikely(!action->thread_fn)) {
warn_no_thread(irq, action);
break;
} /*
* Wake up the handler thread for this
* action. In case the thread crashed and was
* killed we just pretend that we handled the
* interrupt. The hardirq handler above has
* disabled the device interrupt, so no irq
* storm is lurking.
*/
if (likely(!test_bit(IRQTF_DIED,
&action->thread_flags))) {
set_bit(IRQTF_RUNTHREAD, &action->thread_flags);
wake_up_process(action->thread);
}
/* Fall through to add to randomness */
case IRQ_HANDLED:
status |= action->flags;
break; default:
break;
} retval |= ret;
action = action->next;
} while (action); if (status & IRQF_SAMPLE_RANDOM)
add_interrupt_randomness(irq);
local_irq_disable();//关中断 return retval;
}
为了求得平衡,内核把中断处理工作分成两半。中断处理程序是上半部——接收到中断就開始运行。可以稍后完毕的工作推迟到下半部操作,下半部在合适的时机被开中段运行。比如网卡收到数据包时马上发出中断,内核运行网卡已注冊的中断处理程序,此处工作就是通知硬件拷贝最新的网络数据包到内存,然后将控制权交换给系统之前被中断的任务,其它的如处理和操作数据包等任务被放到随后的下半部中去运行。
下一节我们将了解中断处理的下半部。
把握linux内核设计思想(二):硬中断及中断处理的更多相关文章
- 把握linux内核设计思想系列【转】
转自:http://blog.csdn.net/shallnet/article/details/47734053 版权声明:本文为博主原创文章,未经博主允许不得转载.如果您觉得文章对您有用,请点击文 ...
- 把握linux内核设计思想系列
[版权声明:尊重原创,转载请保留出处:blog.csdn.net/shallnet,文章仅供学习交流,请勿用于商业用途] 本专栏分析linux内核的设计实现,包含系统调用.中断.下半部机制.时间管理. ...
- 把握linux内核设计思想(三):下半部机制之软中断
[版权声明:尊重原创.转载请保留出处:blog.csdn.net/shallnet,文章仅供学习交流,请勿用于商业用途] 中断处理程序以异步方式执行,其会打断其它重要代码,其执行时该中 ...
- 把握linux内核设计思想(十三):内存管理之进程地址空间
[版权声明:尊重原创,转载请保留出处:blog.csdn.net/shallnet.文章仅供学习交流,请勿用于商业用途] 进程地址空间由进程可寻址的虚拟内存组成,Linux 的虚拟地址空间为0~4G字 ...
- 把握linux内核设计思想(十二):内存管理之slab分配器
[版权声明:尊重原创,转载请保留出处:blog.csdn.net/shallnet,文章仅供学习交流.请勿用于商业用途] 上一节最后说到对于小内存区的请求,假设採用伙伴系统来进行分配,则会在页内产生非 ...
- 把握linux内核设计思想(七):内核定时器和定时运行
[版权声明:尊重原创,转载请保留出处:blog.csdn.net/shallnet,文章仅供学习交流,请勿用于商业用途] 前面章节说到了把工作推后到除如今以外的时间运行的机制是下半部机 ...
- 把握linux内核设计思想(五):下半部机制之工作队列及几种机制的选择
[版权声明:尊重原创.转载请保留出处:blog.csdn.net/shallnet,文章仅供学习交流,请勿用于商业用途] 工作队列是下半部的第二种将工作推后运行形式.和软中断.task ...
- 《linux内核设计与实现》读书笔记第一、二章
第一章 Linux内核简介 1.1 Unix的历史 1971年,Unix被移植到PDP-11型机中. 1973年,Unix操作系统用C语言改写——为Unix系统的广泛移植铺平了道路. 1977年,伯克 ...
- 《Linux内核设计与实现》 第一二章学习笔记
<Linux内核设计与实现> 第一二章学习笔记 第一章 Linux内核简介 1.1 Unix的历史 Unix的特点 Unix很简洁,所提供的系统调用都有很明确的设计目的. Unix中一切皆 ...
随机推荐
- Java EE 学习(1):什么是Java EE
转载: http://www.jb51.net/article/13059.htm 经常听朋友说什么J2EE,终于知道点什么是J2EE了,汗一个,上网搜了下这个说的比较详细了,J2EE,Java2平台 ...
- jsp导出table数据excel表
<html> <head> <meta http-equiv="content-Type" content="text/html;chars ...
- 标准IO与文件IO 的区别
先来了解下什么是标准IO以及文件IO. 标准IO:标准I/O是ANSI C建立的一个标准I/O模型,是一个标准函数包和stdio.h头文件中的定义,具有一定的可移植性.标准IO库处理很多细节.例如缓存 ...
- python之装饰器【转】
装饰器 先来看一个例子,一个函数f1接收另一个函数f2作为参数,并将该参数f2返回给f2: def deco(func): print("before myfunc() called.&qu ...
- 8个学习.net的博客链接 (以前收藏过更多的,被百度新版搞没了,恨死了)
原文发布时间为:2012-09-18 -- 来源于本人的百度文章 [由搬家工具导入] Simone Chiaretta’s CodeClimber http://www.haacked.com/ ( ...
- jquery.slider jquery滑块插件
原文发布时间为:2011-03-08 -- 来源于本人的百度文章 [由搬家工具导入] http://jqueryui.com/demos/slider jquery滑块插件
- 【简单的原创】div简单轮换显示
原文发布时间为:2009-05-10 -- 来源于本人的百度文章 [由搬家工具导入] <html><head><title>div简单轮换显示</title& ...
- ubuntu php编译安装配置
安装参考:http://ilanni.blog.51cto.com/526870/1569322/ 加载扩展的一些参数参考:http://java-er.com/blog/nginx-php-fpm/
- 《Linux命令行与shell脚本编程大全 第3版》Linux命令行---54
以下为阅读<Linux命令行与shell脚本编程大全 第3版>的读书笔记,为了方便记录,特地与书的内容保持同步,特意做成一节一次随笔,特记录如下:
- final、finalize()、finally、static
一.final final的三种情况: 1.变量 1)对于基本类型,final使数值恒定不变:而对于对象引用,final使引用恒定不变,即一旦引用被初始化指向一个对象,就无法再把它改为指向另一个对象, ...