《下半段和推后执行的工作》由会员分享,可在线阅读,更多相关《下半段和推后执行的工作(26页珍藏版)》请在金锄头文库上搜索。
1、第7章 下半段和推后执行的工 作 下半部 下半部的任务主要是执行与中断相关的工作,这些工 作没有被中断服务程序本身完成. 主进程 中断 top bottom 1.与时间相关的工作 2.与硬件相关的工作 3.不希望被其他中断打断 其他工作 历史 最初的机制:BH 2.5内核后被废止 任务队列 2.5内核之后被工作队列替代 2.3内核引入软中断(softirq)和tasklet 32个软中断,实际上只用到6个 tasklet实现于软中断之上 2.5内核引入工作队列 为什么要把中断分为两部分 中断服务程序异步执行,可能会中断其他的重要代码 ,包括其他中断服务程序。因此,为了避免被中断的 代码延迟太长
2、的时间,中断服务程序需要尽快运行. 希望限制中断服务程序所做的工作,因此处理中断的 时间越短越好。 中断服务程序只作必须的工作,其他的工作推迟到以 后处理。 2.6内核的三种方法 : softirqs, tasklet, 和 work queue 软中断Softirqs kernel/softirq.c定义了一个32个元素的结构数组 /*本结构代表了一个软中断项*/ struct softirq_action void (*sction) (struct softirq_action *);/*带执行的函数*/ void *data; /*传给函数的指针*/ 固定 登记的softirqs最大数
3、目不能动态改变. 工作时允许中断. 一个softirq不能抢占另一个。它只能被中断服务 程序抢占. 另一个softirq,甚至是同一个可运行于另一个 CPU. 使用软中断 分配索引 注册处理程序 open_softirq(索引号,处理程序,NULL) 触发软中断 raise_softirq(索引号) softirq_pending(CPU)宏:返回指定CPU上未处理的软中断 软软中断优优先级级Softirq 描述 HI_SOFTIRQ0优先级最高的tasklets TIMER_SOFTIRQ1定时器的下半部 NET_TX_SOFTIRQ2发送网络数据报 NET_RX_SOFTIRQ3接受网络数
4、据报 SCSI_SOFTIRQ4SCSI的下半部 TASKLET_SOFTIRQ5Tasklets 软中断的执行 中断处理程序在返回前标记它的软中断,使其在 稍后执行 检查和执行待处理的软中断的时机: -从硬件中断代码返回; - 在ksoftirqd内核线程中; - 显式检查和处理软中断的代码中 使用软中断的情况:对时间要求最严格和最重要 的下半部使用 - 网络和SCSI设备 - 内核定时器和tasklet Softirqs的执行 u32 pending = softirq_pending(cpu); if (pending) struct softirq_action *h = softir
5、q_vec; softirq_pending(cpu) = 0; do if (pending h+; pending = 1; while (pending); 111 0 31 Bitmap Pending = 100101 Pending = 001010 Pending = 010100 Pending = 101000 Pending = 010000 Pending = 100000 100101 Tasklet 的类型 Tasklet优优 先 级级 Softirq 描述 HI_SOFTIRQ0优先级最高的tasklets TIMER_SOFTIRQ1定时器的下半部 NET_TX_
6、SOFTIRQ2发送网络数据报 NET_RX_SOFTIRQ3接受网络数据报 SCSI_SOFTIRQ4SCSI的下半部 TASKLET_SOFTIRQ5Tasklets Tasklets的结构 struct tasklet_struct struct tasklet_struct *next; unsigned long state; /见下 atomic_t count; /*reference count 锁*/ void (*func)(unsigned long); /*tasklet 处理函数*/ unsigned long data; /*tasklet 处理函数的参数 */ ;
7、 File: includeinterrupt.h Enum TASKLET_STATE_SCHED,/* Tasklet 已被调度 */ TASKLET_STATE_RUN/* Tasklet正在运行(只用于SMP) */ ; Tasklets Tasklets 实现于softirq之上,实际上 也是softirqs. Tasklet 同时只运行于一个 CPU. 注: softirq,甚至是同一个可运行 于另一个CPU. 不同的tasklets 可同时运行于不同的 CPU. tasklet_schedule()的调度过程 检查tasklet的状态是否为 TASKLET_STATE_SCHED
8、,如果是,则立即返 回; 保存中断状态,禁止本地中断; 把需要调度的tasklet指定处理器的tasklet_vec或 tasklet_hi_vec链表; 唤起TASKLET_SOFTIRQ或HI_SORTIRQ软中断 ,这样在下一次调用do_softirq()时会执行该 tasklet; 恢复中断到原状态并返回。 tasklet处理过程 禁止中断,取当前处理器的tasklet_vec或tasklet_hi_vec 链表; 将当前处理器的这两个链表清空; 允许响应中断; 循环遍历链表上的每一个待处理的tasklet; 如果是多处理器系统,则检查TASKLET_STATE_RUN标 志判断这个t
9、asklet是否在其他处理器上运行,如果在运行 ,则跳转掉下一个tasklet,否则置其状态为 TASKLET_STATE_RUN; 检查tasklet是否被禁止(count=0),如果被禁止则跳到下一 个tasklet; 执行tasklet程序; 执行完毕后,清除TASKLET_STATE_RUN标志; 重复下一个tasklet;直到所有的tasklet处理完。 使用tasklet 编写tasklet处理函数 void my_tasklet_fun (unsigned long data) 声明tasklet 静态 DECLARE_TASKLET(my_tasklet,my_tasklet_
10、func,data); 动态 tasklet_init(t,tasklet_handler,dev) 调度 tasklet tasklet_schedule( const char *name; /工作者线程的名字 struct list_head list; /指向其他工作者线程 ; 工作者线程 系统中每一个CPU有一个默认的工作者线 程events/n,n是处理器编号。也可自己在 创建新的工作者线程 工作者线程是一个内核线程,当有工作被 插入到工作队列时,被唤醒,执行这些工 作;没有工作时睡眠 工作者线程的主要流程 循环执行: 线程将自己设置为睡眠态,等待有工作加入队 列; 如果工作队列空
11、,睡眠 否则线程执行 循环执行推迟的工作 伪代码见教材88页 工作的执行过程 循环执行: 链表非空时,取下一个工作 获取要执行的函数及参数 将工作从链表中摘除,将pending标志请0 调用函数 重复执行 伪代码见教材88-89页 工作者队列 每个CPU一个 struct cpu_workqueue_struct spinlock_t lock; /* 互斥锁 */ long remove_sequence; /* least-recently added (next to run) */ long insert_sequence; /* next to add */ struct list_
12、head worklist; /* 工作列表 */ wait_queue_head_t more_work; wait_queue_head_t work_done; struct workqueue_struct *wq; /*相关的 workqueue_struct */ task_t *thread; /* 相关的线程 */ int run_depth; /* run_workqueue() recursion depth */ ; 工作 struct work_struct unsigned long pending; /* 工作是否等待处理 */ struct list_head e
13、ntry; /* 所有工作 */ void (*func)(void *); /* 处理函数 */ void *data; /* 处理函数的参数 */ void *wq_data; /* 内部使用 */ struct timer_list timer; /* 推迟的工作的定时器 */ ; Work Queue数据结构 worker thread work_struct cpu_workqueue_struct 1/CPU workqueue_struct 1/thread type work_struct work_struct 1/deferrable function 使用Work que
14、ues Work queues非常易于使用! 创建Work Static DECLARE_WORK(name, void(*function)(viod*), void* data); 调度Work schedule_work(&work) schedule_delayed_work(&work, delay) 刷新Work void flush_scheduled_work(void) 取消 Work int cancel_delayed_work(struct work_struct *work) 应当使用哪一种bottom half? 从设计上讲,Softirq提供最少的顺序保证. 这需要Softirq处理函数采取一些额外的步骤 保证数据安全,因为两个以上的同类型 softirqs只能同时运行于不同的CPU。 Softirq多用于时间要求严格和使用频度高的 场合 如果代码不能很好地线程化,tasklet意义较大 Tasklets 有一个简单的接口,由于两个同类型 的不能同时运行,他们非常易于实现。 如果你的延期的工作需要运行于进程上下文, 唯一的选择是work queue.