首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

linux中断之下半部

linux中断之下半部

1.中断是内核不可缺少的一部分,但是中断处理程序本身存在一些局限性,
    a.中断方式以异步方式执行并且有可能会打断期待其他重要代码,甚至包括其它中断处理程序的执行,因此为了避免被打断的代码停止的时间过长,中断处理程序应该执行得越快越好。
    b.如果当前有一个中断处理程序在运行,在最好的情况下(如果设置了SA_INTERRUPT),与该中断同级的其它中断会被屏蔽,在最坏的情况下,当前处理器上所有其它中断都会被屏蔽,导致丢失中断,因此应当让他们执行得越快越好。
    c.由于中断处理程序往往需要对硬件进行操作,所以他们通常有很高的时限要求。
    d.中断处理程序不在进程上下文中运行,所以他们不能被阻塞。这限制了他们所做的事情。
    内核将中断分为上下两个部分,第一部分是中断处理程序,用来完成对硬件中断的即使响应,第二部分就是我们所说的下半部,完成余下的相对宽松的任务。
    上下部分的具体划分:
    a.如果一个任务对时间非常敏感,将其放在中断处理程序中运行
    b.如果一个任务和迎接爱你相关,放到中断处理程序
    c.如果一个任务要保证不被其它中断特别的相同的中断打断,将其放到中断处理程序
    d.余下任务放到下半部
    中断程序在运行当中,当前的中断线在所有处理器上都会被屏蔽,如果一个处理程序是SA_INTERRUPT类型,它执行的时候还会禁止所有本地中断,把本地中短线全局地屏蔽掉,缩短中断被屏蔽的时间对系统的响应能力和性能至关重要。下半部执行的关键在于他们运行的时候,允许响应中断。
    2.下半部的实现方式
    2.6中内核提供了三种不同形式的下半部实现机制,软中断,tasklet和工作队列。还有一个特殊的延迟工具内核定时器。
    tasklet通过软中断形式实现。利用了一个软中断进入口。
    3.软中断
    软中断在编程的时候金泰分配,有softirq_action结构表示,在<linux/interrupt.h>中,
    struct softirq_action {
    void (*action)(struct softirq_action *);
    void *data;
    }
    kernel/softirq.c中定义了一个包含32个该结构的数组。
    static struct softirq_action softirq_vec[32];
    由此可知最多有32个软中断。
    软中断处理程序原型如下:
    void softirq_handler(struct softirq_action *);
    一个软中断不会抢占另外一个软中断,唯一可抢占软中断的是中断处理程序,不过其它的软中断,或者相同类型的中断是可以在其它处理器上同时运行的。
    软中断的执行,触发;
    一个注册的软中断必须在被标记后才会执行,这被称作触发软中断,中断处理程序会返回前标记它的软中断,使其
    稍后被执行,于是在合适的时刻,该软中断就会运行,这些时刻包括如下这些:
    a.从一个硬件中断处理代码返回时
    b.zai ksoftirqd内核线程中
    c.在显示检查和执行待处理的软中断代码中,如网络子系统中。
    这些方法最终都是调用do_softirq()也就是,触发软中断一定要调用该函数。do_softirq()会循环遍历每一个调用他们的处理程序。
    软中断的使用
    软中断保留给系统中时间要求最严格最重要的下半部,目前只有两个子系统--网络和scsi--直接使用软中断,
    此外,内核定时器和tasklet都是简历在软中断上的,如果你想加入一个软中断,首先要考虑tasklet为什么不可以。
    编译期间可以通过<linux/interrupt.h>中定义一个枚举类型来静态地声明软中断,内核用从0开始表示一种相对优先级,索引号小的软中断在索引大的软中断之前运行。
    已有的tasklet类型,
    HI_SOFTIRQ              //0,优先级高的tasklet
    TIMER_SOFTIRQ           //1,定时器的下半部
    NET_TX_SOFTIRQ          //2,发送网络数据包
    NET_RX_SOFTIRQ          //3,接收网络数据包
    SCSI_SOFTIRQ            //4,SCSI的下半部
    TASKLET_SOFTIRQ         //5,tasklet
    可以选择定义优先级在这几个中间,一般插在网络之后最后一项之前。
    软中断的注册,
    open_softirq(索引号,处理函数,相关数据);
    软中断处理程序执行时候,允许响应中断,但它自己不能睡眠,在一个处理程序运行的时候,当前处理器上的软中断被禁止。其它处理器仍可以执行别的软中断。实际上如果同一个软中断在它被执行的时候再次被触发,那么另外一个处理器可以同事运行其处理程序,因为进入接口只有一个,所以任何共享数据即使内部的全局变量都要严格保护。
    tasklet本身也是软中断,只不过同一个处理程序的多个实例不能在多个处理器上运行。
    raise_softirq()可以将一个软中断设置为挂起,让他在下次调用do_softirq时候运行。
    该函数在触发一个软中断之前要先禁止中断,触发后在回复回原来的状态,如果中断本来就被禁止了,可以使用raise_softirq_irqoff.
    tasklet
    tasklet是利用软中断实现的一种下半部机制,在执行频率很高和连续性要求很高的情况下使用软中断,否则请使用tasklet。
    tasklet本身也是软中断,tasklet由tasklet_struct结构表示,每个结构体代表一个tasklet,在<linux/interrupt.h>中定义:
    struct tasklet_struct{
    struct tasklet_struct *next;    //下一个tasklet
    unsigned long state;            //状态,0,TASKLET_STATE_SCHED,TASKLET_STATE_RUN.
    atomic_t      count;            //引用计数,如果不为0则tasklet被禁止,不允许执行,如果为0,激活
    void (*func)(unsigned long);    //tasklet处理函数
    unsigned long data;             //处理函数参数
    }
    结构tasklet_vec代表普通tasklet,tasklet_hi_vec代表优先级高的tasklet,tasklet由tasklet_schedule()和
    tasklet_hi_schedule()函数进行调度,他们接受一个指向tasklet_stuct结构的指针作为参数,看一下
    tasklet_schedule()细节,
    1.检查tasklet转台是否为tasklet_state_sched,如果是说明tasklet已经被调度过了,函数立即返回,此时可能
    该tasklet被调度但还没有被执行。
    2.保存中断状态,禁止本地中断。保证数据不会弄乱。
    3.把要调度的tasklet加到对应的tasklet_VEC或者tasklet_hi_vec中去
    4.唤起TASKLET_SOFTIRQ或者TASKLET_IRQ软中断,下次就会执行do_softirq调用到
    5.恢复中断
    其中tasklet_action()和tasklet_hi_action()是tasklet处理的核心:
    1.禁止中断,
    2.将当前处理器上的该链表清空
    3.允许响应中断
    4.循环遍历获得链表上的每一个待处理的tasklet
    5.多处理器通过检测tasklet_state_run来判断是否在其它处理器上运行,如果运行那么现在该处理器不运行,同一  时间相同的tasklet只执行一个
    6.如果当前的tasklet没有执行,将其状态标志位tasklet_state_run这样别的处理器就不会执行他
    7.检查count是否为0,如果被禁止,跳到下一个
    8.执行tasklet
    9.清除tasklet的state状态
    10.重复执行下一个tasklet,直到没有剩余的等待处理的tasklet。
    所有的tasklet都是通过HI_SOFTIRQ和TASKLET_SOFTIRQ这两个软中断实现,在他们之上又延伸了一层。
    tasklet的使用:
    1.声明你的tasklet
    可以静态也可以动态
返回列表