1、软中断的来源: 中断服务程序往往都是在CPU关闭中断的条件下执行的,以避免中断嵌套而使控制复杂化;但是CPU的中断又不能长时间关闭,否则,容易丢失后来的中断信号;为此,Linux系统将中断服务程序一分为二,并分别命名为"顶半部"和"底半部";通常,顶半部对时间的要求比较严格,必须在中断请求发出后立即执行或至少要在一定时间限制内执行完成;因此,为了保证中断处理能够原子地完成,顶半部通常是在CPU关闭中断的条件下执行的;具体地说,顶半部的范围包括:从IDT表格中登记的中断入口函数一直到驱动程序注册在中断服务队列中的ISR;而底半部则是顶半部根据需要来调度执行的,这些操作允许延迟到稍后执行,它的时间要求并不严格,因此,底半部通常是在CPU打开中断的条件下执行的;但是,Linux的这种底半部机制有以下两大缺点: (1):在任意时刻,系统中只能有一个CPU可以执行底半部的代码,以防止两个或多个CPU同时来执行底半部代码而相互干扰;因此,底半部代码的执行必须是严格"串行化"的; (2):底半部函数不允许嵌套; 这两个缺点在单CPU系统中是无关紧要的,但是在SMP系统中却是非常致命的;因为,底半部机制的严格串行化执行显然没有充分利用SMP系统的多CPU特点;为此,Linux内核在底半部机制的基础上进行了扩展,那就是所谓的"软中断"(softirq)机制; 2、软中断设计思想: Linux的软中断机制是与SMP紧密联系而不可分的;为此,整个软中断机制的设计与实现,自始至终都贯彻了一个思想:"谁触发,谁执行";也就是说,触发中断的那个CPU负责执行它所触发的软中断,而且,每个CPU都有自己的软中断触发与控制机制;这个设计思想也使得软中断机制充分利用了SMP系统的性能和特点; 注意:软中断是运行于中断上下文的; 3、软中断的使用步骤: STEP1:为软中断分配索引号(类似于中断号);在头文件linux/interrupt.h中的枚举常量定义中添加; enum { HI_SOFTIRQ=0, //用于实现最高优先级的软中断 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 }; STEP2:实现软中断处理函数void (*action)(struct softirq_action*); STEP2:注册软中断void open_softirq(int nr,void (*action)(struct softirq_action*)); STEP3:触发软中断void raise_softirq(unsigned int nr); STEP4:软中断的操作函数都是内核内部的函数,所以,使用软中断的代码都必须编译到内核中,而不是编译成模块; 4、其它描述: 软中断是使用软件方式模拟硬件中断的概念,实现宏观上的异步执行效果;Tasklet就是基于软中断实现的; Linux内核中,使用结构体struct softirq_action来描述一个软中断,这个结构体中包含软中断处理函数指针和传递给该函数的参数;使用open_softirq()函数可以注册软中断对应的处理函数,而raise_softirq()函数可以触发一个软中断; 硬中断、软中断、信号,这三者之间的区别:硬中断是外部设备对CPU发出的物理中断,软中断通常是硬中断服务程序对内核发出的逻辑中断,信号则是由内核或其它进程对某个进程发出的中断; 软中断和Tasklet仍然运行于中断上下文,而工作队列则运行于进程上下文.因此,软中断和tasklet处理函数中不能睡眠,而工作队列处理函数中允许睡眠; local_bh_disable()和local_bh_enable()是内核用于禁止和使能软中断和tasklet底半部机制的函数; 例子: #include <linux/module.h> #include <linux/version.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/jiffies.h> #include <linux/delay.h> #include <linux/sched.h> #include <linux/kthread.h> #include <linux/err.h> //软中断相关 #include <linux/interrupt.h> //STEP1:自定义软中断索引 //TEST_SOFTIRQ; MODULE_LICENSE("GPL"); MODULE_AUTHOR("*************"); MODULE_VERSION("2.6.35.000"); static int flag = 0; //STEP2:实现软中断处理函数; void do_my_softirq(struct softirq_action* sa) { flag = 1; printk("%s:hello, my softird function\n", __FUNCTION__); }; static int thread_process(void* param) { while(1) { set_current_state(TASK_UNINTERRUPTIBLE); if(kthread_should_stop()) { printk("kernel thread should stop;file:%s;line:%d\n", __FILE__, __LINE__); break; } mdelay(10*HZ); //STEP4:触发软中断; if(0 == flag) { raise_softirq(TEST_SOFTIRQ); } else { flag = 0; } } return 123; }; static struct task_struct* my_thread = NULL; static int __init study_init(void) { int err = 0; printk("%s\n", __PRETTY_FUNCTION__); //STEP3:注册软中断; open_softirq(TEST_SOFTIRQ, do_my_softirq); my_thread = kthread_create(thread_process, NULL, "my_thread"); if(IS_ERR(my_thread)) { err = PTR_ERR(my_thread); my_thread = NULL; printk(KERN_ERR "unable to start kernel thread:%d\n", err); return err; } wake_up_process(my_thread); printk("kernel thread start;file:%s;line:%d\n", __FILE__, __LINE__); return 0; } static void __exit study_exit(void) { int ret = -1; printk("%s\n",__PRETTY_FUNCTION__); if(my_thread) { ret = kthread_stop(my_thread); my_thread = NULL; } printk("kernel thread stop,exit code is %d;file:%s;line:%d\n",ret, __FILE__, __LINE__); } module_init(study_init); module_exit(study_exit);
|