机关网站建设引导语,创意营销,怎么用服务器lp做网站,公司的网页制作需要考虑什么转自#xff1a;http://blog.csdn.net/droidphone/article/details/7518428 软件中断#xff08;softIRQ#xff09;是内核提供的一种延迟执行机制#xff0c;它完全由软件触发#xff0c;虽然说是延迟机制#xff0c;实际上#xff0c;在大多数情况下#xff0c;它与普… 转自http://blog.csdn.net/droidphone/article/details/7518428 软件中断softIRQ是内核提供的一种延迟执行机制它完全由软件触发虽然说是延迟机制实际上在大多数情况下它与普通进程相比能得到更快的响应时间。软中断也是其他一些内核机制的基础比如tasklet高分辨率timer等。 /*****************************************************************************************************/声明本博内容均由http://blog.csdn.net/droidphone原创转载请注明出处谢谢/*****************************************************************************************************/ 1. 软件中断的数据结构 1.1 struct softirq_action 内核用softirq_action结构管理软件中断的注册和激活等操作它的定义如下 [cpp] view plaincopy struct softirq_action { void (*action)(struct softirq_action *); }; 非常简单只有一个用于回调的函数指针。软件中断的资源是有限的内核目前只实现了10种类型的软件中断它们是 [cpp] view plaincopy enum { HI_SOFTIRQ0, TIMER_SOFTIRQ, NET_TX_SOFTIRQ, NET_RX_SOFTIRQ, BLOCK_SOFTIRQ, BLOCK_IOPOLL_SOFTIRQ, TASKLET_SOFTIRQ, SCHED_SOFTIRQ, HRTIMER_SOFTIRQ, RCU_SOFTIRQ, /* Preferable RCU should always be the last softirq */ NR_SOFTIRQS }; 内核的开发者们不建议我们擅自增加软件中断的数量如果需要新的软件中断尽可能把它们实现为基于软件中断的tasklet形式。与上面的枚举值相对应内核定义了一个softirq_action的结构数组每种软中断对应数组中的一项 [cpp] view plaincopy static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp; 1.2 irq_cpustat_t 多个软中断可以同时在多个cpu运行就算是同一种软中断也有可能同时在多个cpu上运行。内核为每个cpu都管理着一个待决软中断变量pending它就是irq_cpustat_t [cpp] view plaincopy typedef struct { unsigned int __softirq_pending; } ____cacheline_aligned irq_cpustat_t; [cpp] view plaincopy irq_cpustat_t irq_stat[NR_CPUS] ____cacheline_aligned; __softirq_pending字段中的每一个bit对应着某一个软中断某个bit被置位说明有相应的软中断等待处理。 1.3 软中断的守护进程ksoftirqd 在cpu的热插拔阶段内核为每个cpu创建了一个用于执行软件中断的守护进程ksoftirqd同时定义了一个per_cpu变量用于保存每个守护进程的task_struct结构指针 [cpp] view plaincopy DEFINE_PER_CPU(struct task_struct *, ksoftirqd); 大多数情况下软中断都会在irq_exit阶段被执行在irq_exit阶段没有处理完的软中断才有可能会在守护进程中执行。 2. 触发软中断 要触发一个软中断只要调用apiraise_softirq即可它的实现很简单先是关闭本地cpu中断然后调用raise_softirq_irqoff [cpp] view plaincopy void raise_softirq(unsigned int nr) { unsigned long flags; local_irq_save(flags); raise_softirq_irqoff(nr); local_irq_restore(flags); } 再看看raise_softirq_irqoff [cpp] view plaincopy inline void raise_softirq_irqoff(unsigned int nr) { __raise_softirq_irqoff(nr); ...... if (!in_interrupt()) wakeup_softirqd(); } 先是通过__raise_softirq_irqoff设置cpu的软中断pending标志位irq_stat[NR_CPUS] 然后通过in_interrupt判断现在是否在中断上下文中或者软中断是否被禁止如果都不成立则唤醒软中断的守护进程在守护进程中执行软中断的回调函数。否则什么也不做软中断将会在中断的退出阶段被执行。 3. 软中断的执行 基于上面所说软中断的执行既可以守护进程中执行也可以在中断的退出阶段执行。实际上软中断更多的是在中断的退出阶段执行irq_exit以便达到更快的响应加入守护进程机制只是担心一旦有大量的软中断等待执行会使得内核过长地留在中断上下文中。 3.1 在irq_exit中执行 看看irq_exit的部分 [cpp] view plaincopy void irq_exit(void) { ...... sub_preempt_count(IRQ_EXIT_OFFSET); if (!in_interrupt() local_softirq_pending()) invoke_softirq(); ...... } 如果中断发生嵌套in_interrupt()保证了只有在最外层的中断的irq_exit阶段invoke_interrupt才会被调用当然local_softirq_pending也会实现判断当前cpu有无待决的软中断。代码最终会进入__do_softirq中内核会保证调用__do_softirq时本地cpu的中断处于关闭状态进入__do_softirq [cpp] view plaincopy asmlinkage void __do_softirq(void) { ...... pending local_softirq_pending(); __local_bh_disable((unsigned long)__builtin_return_address(0), SOFTIRQ_OFFSET); restart: /* Reset the pending bitmask before enabling irqs */ set_softirq_pending(0); local_irq_enable(); h softirq_vec; do { if (pending 1) { ...... trace_softirq_entry(vec_nr); h-action(h); trace_softirq_exit(vec_nr); ...... } h; pending 1; } while (pending); local_irq_disable(); pending local_softirq_pending(); if (pending --max_restart) goto restart; if (pending) wakeup_softirqd(); lockdep_softirq_exit(); __local_bh_enable(SOFTIRQ_OFFSET); } 首先取出pending的状态禁止软中断主要是为了防止和软中断守护进程发生竞争清除所有的软中断待决标志打开本地cpu中断循环执行待决软中断的回调函数如果循环完毕发现新的软中断被触发则重新启动循环直到以下条件满足才退出 没有新的软中断等待执行循环已经达到最大的循环次数MAX_SOFTIRQ_RESTART目前的设定值时10次如果经过MAX_SOFTIRQ_RESTART次循环后还未处理完则激活守护进程处理剩下的软中断推出前恢复软中断 3.2 在ksoftirqd进程中执行 从前面几节的讨论我们可以看出软中断也可能由ksoftirqd守护进程执行这要发生在以下两种情况下 在irq_exit中执行软中断但是在经过MAX_SOFTIRQ_RESTART次循环后软中断还未处理完这种情况虽然极少发生但毕竟有可能内核的其它代码主动调用raise_softirq而这时正好不是在中断上下文中守护进程将被唤醒 守护进程最终也会调用__do_softirq执行软中断的回调具体的代码位于run_ksoftirqd函数中内核会关闭抢占的情况下执行__do_softirq具体的过程这里不做讨论。 4. tasklet 因为内核已经定义好了10种软中断类型并且不建议我们自行添加额外的软中断所以对软中断的实现方式我们主要是做一个简单的了解对于驱动程序的开发者来说无需实现自己的软中断。但是对于某些情况下我们不希望一些操作直接在中断的handler中执行但是又希望在稍后的时间里得到快速地处理这就需要使用tasklet机制。 tasklet是建立在软中断上的一种延迟执行机制它的实现基于TASKLET_SOFTIRQ和HI_SOFTIRQ这两个软中断类型。 4.1 tasklet_struct 在软中断的初始化函数softirq_init的最后内核注册了TASKLET_SOFTIRQ和HI_SOFTIRQ这两个软中断 [cpp] view plaincopy void __init softirq_init(void) { ...... open_softirq(TASKLET_SOFTIRQ, tasklet_action); open_softirq(HI_SOFTIRQ, tasklet_hi_action); } 内核用一个tasklet_struct来表示一个tasklet它的定义如下 [cpp] view plaincopy struct tasklet_struct { struct tasklet_struct *next; unsigned long state; atomic_t count; void (*func)(unsigned long); unsigned long data; }; next用于把同一个cpu的tasklet链接成一个链表state用于表示该tasklet的当前状态目前只是用了最低的两个bit分别用于表示已经准备被调度执行和已经在另一个cpu上执行 [cpp] view plaincopy enum { TASKLET_STATE_SCHED, /* Tasklet is scheduled for execution */ TASKLET_STATE_RUN /* Tasklet is running (SMP only) */ }; 原子变量count用于tasklet对tasklet_disable和tasklet_enable的计数count为0时表示允许tasklet执行否则不允许执行每次tasklet_disable时该值加1tasklet_enable时该值减1。func是tasklet被执行时的回调函数指针data则用作回调函数func的参数。 4.2 初始化一个tasklet 有两种办法初始化一个tasklet第一种是静态初始化使用以下两个宏这两个宏定义一个tasklet_struct结构并用相应的参数对结构中的字段进行初始化 DECLARE_TASKLET(name, func, data)定义名字为name的tasklet默认为enable状态也就是count字段等于0。DECLARE_TASKLET_DISABLED(name, func, data)定义名字为name的tasklet默认为enable状态也就是count字段等于1。 第二个是动态初始化方法先定义一个tasklet_struct然后用tasklet_init函数进行初始化该方法默认tasklet处于enable状态 [cpp] view plaincopy struct tasklet_struct tasklet_xxx; ...... tasklet_init(tasklet_xxx, func, data); 4.3 tasklet的使用方法 使能和禁止tasklet使用以下函数 tasklet_disable() 通过给count字段加1来禁止一个tasklet如果tasklet正在运行中则等待运行完毕才返回通过TASKLET_STATE_RUN标志。 tasklet_disable_nosync() tasklet_disable的异步版本它不会等待tasklet运行完毕。 tasklet_enable() 使能tasklet只是简单地给count字段减1。 调度tasklet的执行使用以下函数 tasklet_schedule(struct tasklet_struct *t) 如果TASKLET_STATE_SCHED标志为0则置位TASKLET_STATE_SCHED然后把tasklet挂到该cpu等待执行的tasklet链表上接着发出TASKLET_SOFTIRQ软件中断请求。 tasklet_hi_schedule(struct tasklet_struct *t) 效果同上区别是它发出的是HI_SOFTIRQ软件中断请求。 销毁tasklet使用以下函数 tasklet_kill(struct tasklet_struct *t) 如果tasklet处于TASKLET_STATE_SCHED状态或者tasklet正在执行则会等待tasklet执行完毕然后清除TASKLET_STATE_SCHED状态。 4.4 tasklet的内部执行机制 内核为每个cpu用定义了一个tasklet_head结构用于管理每个cpu上的tasklet的调度和执行 [cpp] view plaincopy struct tasklet_head { struct tasklet_struct *head; struct tasklet_struct **tail; }; static DEFINE_PER_CPU(struct tasklet_head, tasklet_vec); static DEFINE_PER_CPU(struct tasklet_head, tasklet_hi_vec); 回到4.1节我们知道tasklet是利用TASKLET_SOFTIRQ和HI_SOFTIRQ这两个软中断来实现的两个软中断只是有优先级的差别所以我们只讨论TASKLET_SOFTIRQ的实现TASKLET_SOFTIRQ的中断回调函数是tasklet_action我们看看它的代码 [cpp] view plaincopy static void tasklet_action(struct softirq_action *a) { struct tasklet_struct *list; local_irq_disable(); list __this_cpu_read(tasklet_vec.head); __this_cpu_write(tasklet_vec.head, NULL); __this_cpu_write(tasklet_vec.tail, __get_cpu_var(tasklet_vec).head); local_irq_enable(); while (list) { struct tasklet_struct *t list; list list-next; if (tasklet_trylock(t)) { if (!atomic_read(t-count)) { if (!test_and_clear_bit(TASKLET_STATE_SCHED, t-state)) BUG(); t-func(t-data); tasklet_unlock(t); continue; } tasklet_unlock(t); } local_irq_disable(); t-next NULL; *__this_cpu_read(tasklet_vec.tail) t; __this_cpu_write(tasklet_vec.tail, (t-next)); __raise_softirq_irqoff(TASKLET_SOFTIRQ); local_irq_enable(); } } 解析如下 关闭本地中断的前提下移出当前cpu的待处理tasklet链表到一个临时链表后清除当前cpu的tasklet链表之所以这样处理是为了处理当前tasklet链表的时候允许新的tasklet被调度进待处理链表中。遍历临时链表用tasklet_trylock判断当前tasklet是否已经在其他cpu上运行而且tasklet没有被禁止 如果没有运行也没有禁止则清除TASKLET_STATE_SCHED状态位执行tasklet的回调函数。如果已经在运行或者被禁止则把该tasklet重新添加会当前cpu的待处理tasklet链表上然后触发TASKLET_SOFTIRQ软中断等待下一次软中断时再次执行。 分析到这了我有个疑问看了上面的代码如果一个tasklet被tasklet_schedule后在没有被执行前被tasklet_disable了岂不是会无穷无尽地引发TASKLET_SOFTIRQ软中断 通过以上的分析我们需要注意的是tasklet有以下几个特征 同一个tasklet只能同时在一个cpu上执行但不同的tasklet可以同时在不同的cpu上执行一旦tasklet_schedule被调用内核会保证tasklet一定会在某个cpu上执行一次如果tasklet_schedule被调用时tasklet不是出于正在执行状态则它只会执行一次如果tasklet_schedule被调用时tasklet已经正在执行则它会在稍后被调度再次被执行两个tasklet之间如果有资源冲突应该要用自旋锁进行同步保护 【作者】张昺华 【出处】http://www.cnblogs.com/sky-heaven/ 【博客园】 http://www.cnblogs.com/sky-heaven/ 【新浪博客】 http://blog.sina.com.cn/u/2049150530 【知乎】 http://www.zhihu.com/people/zhang-bing-hua 【我的作品---旋转倒立摆】 http://v.youku.com/v_show/id_XODM5NDAzNjQw.html?spma2hzp.8253869.0.0fromy1.7-2 【我的作品---自平衡自动循迹车】 http://v.youku.com/v_show/id_XODM5MzYyNTIw.html?spma2hzp.8253869.0.0fromy1.7-2 【新浪微博】 张昺华--sky 【twitter】 sky2030_ 【facebook】 张昺华 zhangbinghua 本文版权归作者和博客园共有欢迎转载但未经作者同意必须保留此段声明且在文章页面明显位置给出原文连接否则保留追究法律责任的权利.