linux中断底半部

ISR        Interrupt Service Routines(中断服务程序)
INTC    Interrupt controller(中断控制器)

Linux中通常分为外部中断(又叫硬件中断)和内部中断(又叫异常)

软件对硬件进行配置后,软件期望等待硬件的某种状态(比如,收到了数据),这里有两种方式,
一种是轮询(polling): CPU 不断的去读硬件状态。另一种是当硬件完成某种事件后,给 CPU 
一个中断,让 CPU 停下手上的事情,去处理这个中断。很显然,中断的交互方式提高了系统的吞吐。

当 CPU 收到一个中断 (IRQ)的时候,会去执行该中断对应的处理函数(ISR)。普通情况下,
会有一个中断向量表,向量表中定义了 CPU 对应的每一个外设资源的中断处理程序的入口,当
发生对应的中断的时候, CPU 直接跳转到这个入口执行程序。也就是中断上下文。(注意:中断上下文中,不可阻塞睡眠)。

在Linux中希望尽快完成ISR,但是有些ISR事务繁重,会消耗很多时间,导致反应速度变差,linux针对这种情况,分了上半部,底半部。
    1. 上半部(top half):收到一个中断,立即执行,有严格的时间限制,只做一些必要的工作,比如:应答,复位等。这些工作都是在所有中断被禁止的情况下完成的。
    2. 底半部(bottom half):能够被推迟到后面完成的任务会在底半部进行。在适合的时机,下半部会被开中断执行。

相关API:
    申请注册一个中断处理函数:
    request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev)
        参数    含义
        irq        表了该中断的中断号,一般 CPU 的中断号都会事先定义好。
        handler    中断发生后的 ISR
        flags    中断标志( IRQF_DISABLED / IRQFSAMPLE_RANDOM / IRQF_TIMER / IRQF_SHARED)
            标志    含义
            IRQF_DISABLED    设置这个标志的话,意味着内核在处理这个 ISR 期间,要禁止其他中断(多数情况不使用这个)
            IRQFSAMPLE_RANDOM    表明这个设备产生的中断对内核熵池有贡献
            IRQF_TIMER    为系统定时器准备的标志
            IRQF_SHARED    表明多个中断处理程序之间共享中断线。同一个给定的线上注册每个处理程序,必须设置这个    
        name     中断相关的设备 ASCII 文本,例如 "keyboard",这些名字会在 /proc/irq 和 /proc/interrupts 文件使用
        dev        用于共享中断线,传递驱动程序的设备结构。非共享类型的中断,直接设置成为 NULL
    return:0 success    -EBUSY表示中断线已使用或者没有指定IRQF_SHARED
    注意:request_irq 函数可能引起睡眠,所以不允许在中断上下文或者不允许睡眠的代码中调用。

    释放中断
    const void *free_irq(unsigned int irq, void *dev_id)
    
    
实现底半部的三种方法:tasklet,等待队列,软中断
    软中断:很少用于实现下半部,但是tasklet是软中断实现的。软中断就是软件实现的异步中断,优先级低于硬件中断,高于普通进程。
            软中断是在编译时候静态分配的,要用软中断就要修改内核代码。

        实现软中断的相关结构体和函数:
            /*include/linux/interrupt.h*/
            struct softirq_action
            {
                void (*action)(struct softirq_action *); //软中断处理函数
            };
            
            /*include/linux/interrupt.h*/
            enum    //软中断的优先级别
            {
                HI_SOFTIRQ=0, //用于tasklet的软中断,优先级最高,为0            
                TIMER_SOFTIRQ, //定时器的下半部            
                NET_TX_SOFTIRQ, //发送网络数据的软中断            
                NET_RX_SOFTIRQ, //接受网络数据的软中断            
                BLOCK_SOFTIRQ,            
                TASKLET_SOFTIRQ, //也是用于实现tasklet            
                SCHED_SOFTIRQ,            
                HRTIMER_SOFTIRQ,            
                RCU_SOFTIRQ, /* Preferable RCU should always be the last softirq */        
                XIAOBAI_SOFTIRQ, //这是自添加软中断的一个中断号,优先级最低
                NR_SOFTIRQS, //这个就是上面所说的软中断结构体数组成员个数
            };
            /*kernel/softirq.c*/
            static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp;    //声明软中断的一个表
            
            void open_softirq(int nr, void (*action)(struct softirq_action *))        //申请注册一个软中断(中断号绑定执行函数)
            注意:如果想自添加软中断,需要导出符将open_softirq导出,在其他驱动中可以调用。EXPORT_SYMBOL(open_softirq);
            
            void raise_softirq(unsigned int nr)    //触发对应的软中断,参数为枚举的中断号XIAOBAI_SOFTIRQ
            注意:如果想自添加软中断,需要导出符将raise_softirq导出,在其他驱动中可以调用。EXPORT_SYMBOL(raise_softirq);
            
            实现流程为:
                1.在include/linux/interrupt.h中添加自己的软中断号XIAOBAI_SOFTIRQ(在RCU_SOFTIRQ后添加)
                2.将kernel/softirq.c中open_softirq和raise_softirq两个函数使用导出符EXPORT_SYMBOL导出。
                3.申请一个软中断并绑定处理函数    open_softirq(XIAOBAI_SOFTIRQ,handler_function);
                4.在中断处理函数中执行下半部,也就是软中断。raise_softirq(XIAOBAI_SOFTIRQ);
        
        软中断补充:所谓的触发软中断,并不是立刻触发,他只是告诉内核,下次执行软中断的时候,记得执行我的软中断处理函数。
            软中断优先级最高的中断号就是tasklet。
            
            
    tasklet:
        和软中断一样,触发函数执行后并不是立刻执行,只是告诉内核,下次执行软中断的时候执行我的处理函数。
        
        实现方法:
            1. struct tasklet_struct test_tasklet; //定义tasklet结构体
            2. tasklet_init(&test_tasklet, test_func, (unsigned long)123);    //test_func为处理函数,123为给处理函数传的参数 
            3. tasklet_schedule(&test_tasklet);或者tasklet_hi_schedule(&test_tasklet);//中断返回前调度tasklet
                前者使用TASKLET_SOFTIRQ优先级,后者使用HI_SOFTIRQ优先级。
            4.移除tasklet的API:void tasklet_kill(struct tasklet_struct *t)//如果tasklet在运行,程序会休眠,等到它执行完毕。
                
            还有禁止和激活tasklet的函数:(禁止后,tasklet就不能用了,直到激活)
                static inline void tasklet_disable(struct tasklet_struct *t) //禁止
                static inline void tasklet_enable (struct tasklet_struct *t) //激活
                
        补充:软中断实现tasklet的中断号优先级有TASKLET_SOFTIRQ和HI_SOFTIRQ两种。
            一般使用tasklet_schedule会使用TASKLET_SOFTIRQ优先级5    (include/linux/interrupt.h中查看优先级)
            使用tasklet_hi_schedule 会使用HI_SOFTIRQ优先级0
                
        总结:软中断的优点:优先级比普通的进程高,调度速度快。
              软中断的缺点:处于中断上下文,所以不能睡眠
              
    工作队列:
        工作队列的执行上下文是内核线程,所以可以调度和睡眠。这就意味着需要获取大量内存时、需要获取信号量时、
        阻塞IO时,工作队列比较有效。
        
        实现方法:
            1.struct workqueue_struct test_wq; //定义工作队列
            2.实现工作队列处理函数test_func();    
            3.INIT_WORK(&test_wq,test_func);//初始化工作队列
                DECLARE_WROK(name,func);//定义并初始化工作队列
            4.schedule_work(&test_wq);    //调度工作队列,成功返回1
                schedule_delayed_work(&test_wq,delay);    //延迟调度
            


总结:
    1,需要睡眠,阻塞的,只能用工作队列。
    2,短时间内中断数量很多的,任务队列,软中断会更好。例如网络。
    3,对性能要求很高的话,软中断最好。
    4,使用任务队列时,应该注意同一个任务被多次调用,同一个函数被多个任务队列注意。
    5,软中断要注意SMP,函数的重入。


------------------------------------------------------------------------------------------------------------
|                    |tasklet                        |    软中断                        |    工作队列
|-----------------------------------------------------------------------------------------------------------    
|SMP             |向一个CPU注册            |多个CPU并发                    |    默认同一个CPU上或指定CPU
|                    |                                |                                |(queue_delayed_work_on)    
|-----------------------------------------------------------------------------------------------------------        
|上下文                |中断                            |中断                            |进程
|函数                |1.同类型只能执行一个            |1.不能阻塞,睡眠                |1.可阻塞,睡眠
|                    |2.不能阻塞睡眠                    |2.不带参数                        |2.不带参数
|                    |3.可带参数                        |3.ksoftirqd内核线程            |    3.events/0内核线程
|                    |4.ksoftirqd内核线程            |                                |
|-----------------------------------------------------------------------------------------------------------                    
|执行                |1.单个tasklet串行                |1.同一个CPU上串行                |1.进程调度内,可重新调度
|                    |2.同一个tasklet,只响应最后一次|    2.不同的CPU并行                |    
|                    |                                |3.不能抢占另一软中断            |
|                    |3.多个之间为并行                |                                |
|-----------------------------------------------------------------------------------------------------------            
|atomic_context        |原子模式执行                    |原子模式执行                    |不是
|-----------------------------------------------------------------------------------------------------------
|响应时间            |1.tasklet_hi_schedule更早        |1.最迟在下一个time tick时,    |    可指定延后时间,jiffies单位(1ms-百ms)
|                    |2.最迟在下一个time tick时,    |中断中调用,就是中断完成        |内核线程,上下文切换
|                    |中断中调用,就是中断完成        |后立即执行                        |
|                    |后立即执行                        |                                |
|-----------------------------------------------------------------------------------------------------------
|开销                |次之(与软中断接近)            |最小                            |开销最大

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值