转自:http://blog.youkuaiyun.com/yxfabcdefg/article/details/53127155
http://blog.youkuaiyun.com/qq_29350001/article/details/52118662
中断上半部分与中断下半部分对比
在中断处理程序中,既想让程序运行的快,又想让程序完成的工作量多,这两个目的有抵触。
鉴于这两个目的不可调和的矛盾,中断处理可以分为两部分:
中断处理工作,例如对接收中断进行中断或复位硬件,这些工作都在所有中断被禁止的情况下完成。可以稍后完成的工作推迟到下半部,在合适的情况下执行下半部分中断。
example;
当网卡接收流入网络的数据包时,需要通知内核数据包到了,网卡需要立即完成这件事,从而优化网络的吞吐量和传输周期,以避免超时。因此网卡立即发出中断,通知内核这里有最新的数据包。内核通过执行网卡已注册的中断处理程序作出应答。中断开始运行,应答硬件,复制最新的网络数据包到内存,然后读取网卡更多的数据包,这些都是重要的、紧迫的、又与硬件相关的的工作。处理和操作其他数据包的其他工作在随后的下半部分进行。
中断下半部
下半部分执行的任务是执行与中断处理密切相关,但中断处理程序本身不执行的工作。和上半部分只能通过中断处理程序实现不同,下半部分可以通过多种机制实现
一般硬中断基本为中断的上半部分,软中断和tasklet是中断的下半部分,将上半部分没有实现完的处理继续执行。
中断:
中断使得硬件得以和处理器进行通讯,中断是一种电信号,由硬件设备生成,并且直接送到中断控制器的输入引脚上,然后再由中断控制器向处理器发出相应的信号。处理器检测到此信号,便中断自己当前工作进行适当的处理。处理器便会通知操作系统已经产生中断,这样,操作系统就可以对这个中断进行适当的处理了。不同的设备对应的中断不同,而每个中断都通过一个唯一的数字标识。
因此,来自键盘的中断就有别于来自硬盘的中断,从而使得操作系统能够对中断进行区分,并知道哪个硬件设备产生了哪个中断。这样操作系统才能给中断提供不同的处理程序。
-----------------------------分割线------------------------------------
硬中断:
1. 硬中断是由硬件产生的,比如,像磁盘,网卡,键盘,时钟等。每个设备或设备集都有它自己的IRQ(中断请求)。基于IRQ,CPU可以将相应的请求分发到对应的硬件驱动上(注:硬件驱动通常是内核中的一个子程序,而不是一个独立的进程)。
2. 处理中断的驱动是需要运行在CPU上的,因此,当中断产生的时候,CPU会中断当前正在运行的任务,来处理中断。在有多核心的系统上,一个中断通常只能中断一颗CPU(也有一种特殊的情况,就是在大型主机上是有硬件通道的,它可以在没有主CPU的支持下,可以同时处理多个中断。)。
3. 硬中断可以直接中断CPU。它会引起内核中相关的代码被触发。对于那些需要花费一些时间去处理的进程,中断代码本身也可以被其他的硬中断中断。
4. 对于时钟中断,内核调度代码会将当前正在运行的进程挂起,从而让其他的进程来运行。它的存在是为了让调度代码(或称为调度器)可以调度多任务。
软中断:
1. 软中断的处理非常像硬中断。然而,它们仅仅是由当前正在运行的进程所产生的。
2. 通常,软中断是一些对I/O的请求。这些请求会调用内核中可以调度I/O发生的程序。对于某些设备,I/O请求需要被立即处理,而磁盘I/O请求通常可以排队并且可以稍后处理。根据I/O模型的不同,进程或许会被挂起直到I/O完成,此时内核调度器就会选择另一个进程去运行。I/O可以在进程之间产生并且调度过程通常和磁盘I/O的方式是相同。
3. 软中断仅与内核相联系。而内核主要负责对需要运行的任何其他的进程进行调度。一些内核允许设备驱动的一些部分存在于用户空间,并且当需要的时候内核也会调度这个进程去运行。
4. 软中断并不会直接中断CPU。也只有当前正在运行的代码(或进程)才会产生软中断。这种中断是一种需要内核为正在运行的进程去做一些事情(通常为I/O)的请求。有一个特殊的软中断是Yield调用,它的作用是请求内核调度器去查看是否有一些其他的进程可以运行。
问题解答:
1. 问:对于软中断,I/O操作是否是由内核中的I/O设备驱动程序完成?
答:对于I/O请求,内核会将这项工作分派给合适的内核驱动程序,这个程序会对I/O进行队列化,以可以稍后处理(通常是磁盘I/O),或如果可能可以立即执行它。通常,当对硬中断进行回应的时候,这个队列会被驱动所处理。当一个I/O请求完成的时候,下一个在队列中的I/O请求就会发送到这个设备上。
2. 问:软中断所经过的操作流程是比硬中断的少吗?换句话说,对于软中断就是:进程 ->内核中的设备驱动程序;对于硬中断:硬件->CPU->内核中的设备驱动程序?
答:是的,软中断比硬中断少了一个硬件发送信号的步骤。产生软中断的进程一定是当前正在运行的进程,因此它们不会中断CPU。但是它们会中断调用代码的流程。
如果硬件需要CPU去做一些事情,那么这个硬件会使CPU中断当前正在运行的代码。而后CPU会将当前正在运行进程的当前状态放到堆栈(stack)中,以至于之后可以返回继续运行。这种中断可以停止一个正在运行的进程;可以停止正处理另一个中断的内核代码;或者可以停止空闲进程。
转自: http://www.linuxidc.com/Linux/2014-03/98013.htm
硬中断是外部设备对CPU的中断,软中断是中断底半部的一种处理机制,信号则是由内核(或其他进程)对某个进程的中断。
②硬中断的中断响应周期,CPU需要发中断回合信号(NMI不需要);
③硬中断的中断号是由中断控制器提供的(NMI硬中断中断号系统指定为02H);
④硬中断是可屏蔽的(NMI硬中断不可屏蔽);
软中断是一种推后执行的机制。定时器、网卡的数据的处理是很典型的软中断,这个和中断向 量表里的中断是完全不一样的。以网络数据的处理为例,当网卡接到一个数据包后,其中断处理程序只是把数据复制到缓冲区,然后就告诉网卡,你可以再传数据给 我了,也就是中断返回。但在此之前,网卡的中断处理程序要置一个标志位,告诉操作系统有事要做,这个事就是软中断。但软中断只是很多中断返回时要做的事情 之一,操作系统每次中断返回时会检查着个标志位,看是否有事要做,如果有,就会去处理,象前面提到的网卡,这时候操作系统就回调用软中断的处理函数。网卡 的软中断程序就是做分析数据包啊,这个数据应该传给谁啊等这些工作,没有,就返回了,除了必须的部分。
编写两个中断服务函数的区别
1.软中断发生的时间是由程序控制的,而硬中断发生的时间是随机的
2.软中断是由程序调用发生的,而硬中断是由外设引发的
3.硬件中断处理程序要确保它能快速地完成它的任务,这样程序执行时才不会等侍较长时间
编写这两类的中断处理程序我感觉区别不太大
转自: http://blog.youkuaiyun.com/u010820757/article/details/48930303
------------------分割线-------------------
本文主要内容:硬中断 / 软中断的原理和实现
内核版本:2.6.37
Author:zhangskd @ csdn blog
概述
从本质上来讲,中断是一种电信号,当设备有某种事件发生时,它就会产生中断,通过总线把电信号发送给中断控制器。
如果中断的线是激活的,中断控制器就把电信号发送给处理器的某个特定引脚。处理器于是立即停止自己正在做的事,
跳到中断处理程序的入口点,进行中断处理。
(1) 硬中断
由与系统相连的外设(比如网卡、硬盘)自动产生的。主要是用来通知操作系统系统外设状态的变化。比如当网卡收到数据包
的时候,就会发出一个中断。我们通常所说的中断指的是硬中断(hardirq)。
(2) 软中断
为了满足实时系统的要求,中断处理应该是越快越好。linux为了实现这个特点,当中断发生的时候,硬中断处理那些短时间
就可以完成的工作,而将那些处理事件比较长的工作,放到中断之后来完成,也就是软中断(softirq)来完成。
(3) 中断嵌套
Linux下硬中断是可以嵌套的,但是没有优先级的概念,也就是说任何一个新的中断都可以打断正在执行的中断,但同种中断
除外。软中断不能嵌套,但相同类型的软中断可以在不同CPU上并行执行。
(4) 软中断指令
int是软中断指令。
中断向量表是中断号和中断处理函数地址的对应表。
int n - 触发软中断n。相应的中断处理函数的地址为:中断向量表地址 + 4 * n。
(5)硬中断和软中断的区别
软中断是执行中断指令产生的,而硬中断是由外设引发的。
硬中断的中断号是由中断控制器提供的,软中断的中断号由指令直接指出,无需使用中断控制器。
硬中断是可屏蔽的,软中断不可屏蔽。
硬中断处理程序要确保它能快速地完成任务,这样程序执行时才不会等待较长时间,称为上半部。
软中断处理硬中断未完成的工作,是一种推后执行的机制,属于下半部。
开关
(1) 硬中断的开关
简单禁止和激活当前处理器上的本地中断:
local_irq_disable();
local_irq_enable();
保存本地中断系统状态下的禁止和激活:
unsigned long flags;
local_irq_save(flags);
local_irq_restore(flags);
(2) 软中断的开关
禁止下半部,如softirq、tasklet和workqueue等:
local_bh_disable();
local_bh_enable();
需要注意的是,禁止下半部时仍然可以被硬中断抢占。
(3) 判断中断状态
#define in_interrupt() (irq_count()) // 是否处于中断状态(硬中断或软中断)
#define in_irq() (hardirq_count()) // 是否处于硬中断
#define in_softirq() (softirq_count()) // 是否处于软中断
硬中断
(1) 注册中断处理函数
注册中断处理函数:
- /**
- * irq: 要分配的中断号
- * handler: 要注册的中断处理函数
- * flags: 标志(一般为0)
- * name: 设备名(dev->name)
- * dev: 设备(struct net_device *dev),作为中断处理函数的参数
- * 成功返回0
- */
- int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
- const char *name, void *dev);
中断处理函数本身:
- typedef irqreturn_t (*irq_handler_t) (int, void *);
- /**
- * enum irqreturn
- * @IRQ_NONE: interrupt was not from this device
- * @IRQ_HANDLED: interrupt was handled by this device
- * @IRQ_WAKE_THREAD: handler requests to wake the handler thread
- */
- enum irqreturn {
- IRQ_NONE,
- IRQ_HANDLED,
- IRQ_WAKE_THREAD,
- };
- typedef enum irqreturn irqreturn_t;
- #define IRQ_RETVAL(x) ((x) != IRQ_NONE)
(2) 注销中断处理函数
- /**
- * free_irq - free an interrupt allocated with request_irq
- * @irq: Interrupt line to free
- * @dev_id: Device identity to free
- *
- * Remove an interrupt handler. The handler is removed and if the
- * interrupt line is no longer in use by any driver it is disabled.
- * On a shared IRQ the caller must ensure the interrupt is disabled
- * on the card it drives before calling this function. The function does
- * not return until any executing interrupts for this IRQ have completed.
- * This function must not be called from interrupt context.
- */
- void free_irq(unsigned int irq, void *dev_id);
软中断
(1) 定义
软中断是一组静态定义的下半部接口,可以在所有处理器上同时执行,即使两个类型相同也可以。
但一个软中断不会抢占另一个软中断,唯一可以抢占软中断的是硬中断。
软中断由softirq_action结构体表示:
- struct softirq_action {
- void (*action) (struct softirq_action *); /* 软中断的处理函数 */
- };
目前已注册的软中断有10种,定义为一个全局数组:
- static struct softirq_action softirq_vec[NR_SOFTIRQS];
- enum {
- HI_SOFTIRQ = 0, /* 优先级高的tasklets */
- TIMER_SOFTIRQ, /* 定时器的下半部 */
- NET_TX_SOFTIRQ, /* 发送网络数据包 */
- NET_RX_SOFTIRQ, /* 接收网络数据包 */
- BLOCK_SOFTIRQ, /* BLOCK装置 */
- BLOCK_IOPOLL_SOFTIRQ,
- TASKLET_SOFTIRQ, /* 正常优先级的tasklets */
- SCHED_SOFTIRQ, /* 调度程序 */
- HRTIMER_SOFTIRQ, /* 高分辨率定时器 */
- RCU_SOFTIRQ, /* RCU锁定 */
- NR_SOFTIRQS /* 10 */
- };
(2) 注册软中断处理函数
- /**
- * @nr: 软中断的索引号
- * @action: 软中断的处理函数
- */
- void open_softirq(int nr, void (*action) (struct softirq_action *))
- {
- softirq_vec[nr].action = action;
- }
例如:
open_softirq(NET_TX_SOFTIRQ, net_tx_action);
open_softirq(NET_RX_SOFTIRQ, net_rx_action);
(3) 触发软中断
调用raise_softirq()来触发软中断。
- void raise_softirq(unsigned int nr)
- {
- unsigned long flags;
- local_irq_save(flags);
- raise_softirq_irqoff(nr);
- local_irq_restore(flags);
- }
- /* This function must run with irqs disabled */
- inline void rasie_softirq_irqsoff(unsigned int nr)
- {
- __raise_softirq_irqoff(nr);
- /* If we're in an interrupt or softirq, we're done
- * (this also catches softirq-disabled code). We will
- * actually run the softirq once we return from the irq
- * or softirq.
- * Otherwise we wake up ksoftirqd to make sure we
- * schedule the softirq soon.
- */
- if (! in_interrupt()) /* 如果不处于硬中断或软中断 */
- wakeup_softirqd(void); /* 唤醒ksoftirqd/n进程 */
- }
Percpu变量irq_cpustat_t中的__softirq_pending是等待处理的软中断的位图,通过设置此变量
即可告诉内核该执行哪些软中断。
- static inline void __rasie_softirq_irqoff(unsigned int nr)
- {
- trace_softirq_raise(nr);
- or_softirq_pending(1UL << nr);
- }
- typedef struct {
- unsigned int __softirq_pending;
- unsigned int __nmi_count; /* arch dependent */
- } irq_cpustat_t;
- irq_cpustat_t irq_stat[];
- #define __IRQ_STAT(cpu, member) (irq_stat[cpu].member)
- #define or_softirq_pending(x) percpu_or(irq_stat.__softirq_pending, (x))
- #define local_softirq_pending() percpu_read(irq_stat.__softirq_pending)
唤醒ksoftirqd内核线程处理软中断。
- static void wakeup_softirqd(void)
- {
- /* Interrupts are disabled: no need to stop preemption */
- struct task_struct *tsk = __get_cpu_var(ksoftirqd);
- if (tsk && tsk->state != TASK_RUNNING)
- wake_up_process(tsk);
- }
在下列地方,待处理的软中断会被检查和执行:
1. 从一个硬件中断代码处返回时
2. 在ksoftirqd内核线程中
3. 在那些显示检查和执行待处理的软中断的代码中,如网络子系统中
而不管是用什么方法唤起,软中断都要在do_softirq()中执行。如果有待处理的软中断,
do_softirq()会循环遍历每一个,调用它们的相应的处理程序。
在中断处理程序中触发软中断是最常见的形式。中断处理程序执行硬件设备的相关操作,
然后触发相应的软中断,最后退出。内核在执行完中断处理程序以后,马上就会调用
do_softirq(),于是软中断开始执行中断处理程序完成剩余的任务。
下面来看下do_softirq()的具体实现。
- asmlinkage void do_softirq(void)
- {
- __u32 pending;
- unsigned long flags;
- /* 如果当前已处于硬中断或软中断中,直接返回 */
- if (in_interrupt())
- return;
- local_irq_save(flags);
- pending = local_softirq_pending();
- if (pending) /* 如果有激活的软中断 */
- __do_softirq(); /* 处理函数 */
- local_irq_restore(flags);
- }
- /* We restart softirq processing MAX_SOFTIRQ_RESTART times,
- * and we fall back to softirqd after that.
- * This number has been established via experimentation.
- * The two things to balance is latency against fairness - we want
- * to handle softirqs as soon as possible, but they should not be
- * able to lock up the box.
- */
- asmlinkage void __do_softirq(void)
- {
- struct softirq_action *h;
- __u32 pending;
- /* 本函数能重复触发执行的次数,防止占用过多的cpu时间 */
- int max_restart = MAX_SOFTIRQ_RESTART;
- int cpu;
- pending = local_softirq_pending(); /* 激活的软中断位图 */
- account_system_vtime(current);
- /* 本地禁止当前的软中断 */
- __local_bh_disable((unsigned long)__builtin_return_address(0), SOFTIRQ_OFFSET);
- lockdep_softirq_enter(); /* current->softirq_context++ */
- cpu = smp_processor_id(); /* 当前cpu编号 */
- restart:
- /* Reset the pending bitmask before enabling irqs */
- set_softirq_pending(0); /* 重置位图 */
- local_irq_enable();
- h = softirq_vec;
- do {
- if (pending & 1) {
- unsigned int vec_nr = h - softirq_vec; /* 软中断索引 */
- int prev_count = preempt_count();
- kstat_incr_softirqs_this_cpu(vec_nr);
- trace_softirq_entry(vec_nr);
- h->action(h); /* 调用软中断的处理函数 */
- trace_softirq_exit(vec_nr);
- if (unlikely(prev_count != preempt_count())) {
- printk(KERN_ERR "huh, entered softirq %u %s %p" "with preempt_count %08x,"
- "exited with %08x?\n", vec_nr, softirq_to_name[vec_nr], h->action, prev_count,
- preempt_count());
- }
- rcu_bh_qs(cpu);
- }
- h++;
- pending >>= 1;
- } while(pending);
- local_irq_disable();
- pending = local_softirq_pending();
- if (pending & --max_restart) /* 重复触发 */
- goto restart;
- /* 如果重复触发了10次了,接下来唤醒ksoftirqd/n内核线程来处理 */
- if (pending)
- wakeup_softirqd();
- lockdep_softirq_exit();
- account_system_vtime(current);
- __local_bh_enable(SOFTIRQ_OFFSET);
- }
(4) ksoftirqd内核线程
内核不会立即处理重新触发的软中断。
当大量软中断出现的时候,内核会唤醒一组内核线程来处理。
这些线程的优先级最低(nice值为19),这能避免它们跟其它重要的任务抢夺资源。
但它们最终肯定会被执行,所以这个折中的方案能够保证在软中断很多时用户程序不会
因为得不到处理时间而处于饥饿状态,同时也保证过量的软中断最终会得到处理。
每个处理器都有一个这样的线程,名字为ksoftirqd/n,n为处理器的编号。
- static int run_ksoftirqd(void *__bind_cpu)
- {
- set_current_state(TASK_INTERRUPTIBLE);
- current->flags |= PF_KSOFTIRQD; /* I am ksoftirqd */
- while(! kthread_should_stop()) {
- preempt_disable();
- if (! local_softirq_pending()) { /* 如果没有要处理的软中断 */
- preempt_enable_no_resched();
- schedule();
- preempt_disable():
- }
- __set_current_state(TASK_RUNNING);
- while(local_softirq_pending()) {
- /* Preempt disable stops cpu going offline.
- * If already offline, we'll be on wrong CPU: don't process.
- */
- if (cpu_is_offline(long)__bind_cpu))/* 被要求释放cpu */
- goto wait_to_die;
- do_softirq(); /* 软中断的统一处理函数 */
- preempt_enable_no_resched();
- cond_resched();
- preempt_disable();
- rcu_note_context_switch((long)__bind_cpu);
- }
- preempt_enable();
- set_current_state(TASK_INTERRUPTIBLE);
- }
- __set_current_state(TASK_RUNNING);
- return 0;
- wait_to_die:
- preempt_enable();
- /* Wait for kthread_stop */
- set_current_state(TASK_INTERRUPTIBLE);
- while(! kthread_should_stop()) {
- schedule();
- set_current_state(TASK_INTERRUPTIBLE);
- }
- __set_current_state(TASK_RUNNING);
- return 0;
- }
转自: http://blog.youkuaiyun.com/zhangskd/article/details/21992933