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

linux 中断机制的处理过程(3)

linux 中断机制的处理过程(3)

根据以上内容可以得出中断机制各个数据结构之间的联系如下图所示:

三.中断的处理过程
Linux中断分为两个半部:上半部(tophalf)和下半部(bottom half)。上半部的功能是"登记中断",当一个中断发生时,它进行相应地硬件读写后就把中断例程的下半部挂到该设备的下半部执行队列中去。因此,上半部执行的速度就会很快,可以服务更多的中断请求。但是,仅有"登记中断"是远远不够的,因为中断的事件可能很复杂。因此,Linux引入了一个下半部,来完成中断事件的绝大多数使命。下半部和上半部最大的不同是下半部是可中断的,而上半部是不可中断的,下半部几乎做了中断处理程序所有的事情,而且可以被新的中断打断!下半部则相对来说并不是非常紧急的,通常还是比较耗时的,因此由系统自行安排运行时机,不在中断服务上下文中执行。
中断号的查看可以使用下面的命令:“cat /proc/interrupts”。
Linux实现下半部的机制主要有tasklet和工作队列。
小任务tasklet的实现
其数据结构为struct tasklet_struct,每一个结构体代表一个独立的小任务,定义如下

struct tasklet_struct
{
    struct tasklet_struct *next;/*指向下一个链表结构*/
    unsigned long state;/*小任务状态*/
    atomic_t count;/*引用计数器*/
    void (*func)(unsigned long);/*小任务的处理函数*/
    unsigned long data;/*传递小任务函数的参数*/
};

state的取值参照下边的枚举型:
enum
{
    TASKLET_STATE_SCHED,    /* 小任务已被调用执行*/
    TASKLET_STATE_RUN   /*仅在多处理器上使用*/
};
count域是小任务的引用计数器。只有当它的值为0的时候才能被激活,并其被设置为挂起状态时,才能够被执行,否则为禁止状态。
I、声明和使用小任务tasklet
静态的创建一个小任务的宏有一下两个:
#define DECLARE_TASKLET(name, func, data)  \
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }

#define DECLARE_TASKLET_DISABLED(name, func, data) \
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(1), func, data }
这两个宏的区别在于计数器设置的初始值不同,前者可以看出为0,后者为1。为0的表示激活状态,为1的表示禁止状态。其中ATOMIC_INIT宏为:
#define ATOMIC_INIT(i)   { (i) }
即可看出就是设置的数字。此宏在include/asm-generic/atomic.h中定义。这样就创建了一个名为name的小任务,其处理函数为func。当该函数被调用的时候,data参数就被传递给它。
II、小任务处理函数程序
    处理函数的的形式为:void my_tasklet_func(unsigned long data)。这样DECLARE_TASKLET(my_tasklet, my_tasklet_func, data)实现了小任务名和处理函数的绑定,而data就是函数参数。
III、调度编写的tasklet
调度小任务时引用tasklet_schedule(&my_tasklet)函数就能使系统在合适的时候进行调度。函数原型为:
static inline void tasklet_schedule(struct tasklet_struct *t)
{
    if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
       __tasklet_schedule(t);
}
这个调度函数放在中断处理的上半部处理函数中,这样中断申请的时候调用处理函数(即irq_handler_t handler)后,转去执行下半部的小任务。
如果希望使用DECLARE_TASKLET_DISABLED(name,function,data)创建小任务,那么在激活的时候也得调用相应的函数被使能
tasklet_enable(struct tasklet_struct *); //使能tasklet
tasklet_disble(struct tasklet_struct *); //禁用tasklet
tasklet_init(struct tasklet_struct *,void (*func)(unsigned long),unsigned long);
当然也可以调用tasklet_kill(struct tasklet_struct *)从挂起队列中删除一个小任务。清除指定tasklet的可调度位,即不允许调度该tasklet 。
继承事业,薪火相传
返回列表