Linux2.6 内核中 ” 下半部分 ” 分析 Group:N3608
目录 简介 历史 & 发展 实现机制 软中断 Tasklet 工作队列 (new) 总结
目录 简介 历史 & 发展 实现机制 软中断 Tasklet 工作队列 (new) 总结
中断服务一般都是在将中断请求关闭的条 件下执行,以避免嵌套而使控制复杂化。 可是如果关中断的时间太长就可能因为 CPU 不能及时响应其他的中断请求而使中 断丢失;如果在将中断服务程序挂入中断 请求队列时开中断,又会使中断过程不安 全。 如何解决这种矛盾?
将中断服务的过程分成两个部分,开头的 部分在关中断的条件下执行,是原子性的 关键操作;后半部分在开中断的条件下执 行,允许延迟,并可以把多个中断服务中 此部分合并在一起处理 —— 下半部分。
如何选取? 如果一个任务对时间非常敏感,在中断处 理程序中执行。 如果一个任务和硬件相关,在中断处理程 序中执行。 如果一个任务要保证不被其他中断打断, 在中断处理程序中执行。 其他所有任务,考虑放置在下半部分执行。
目录 简介 历史 & 发展 实现机制 软中断 Tasklet 工作队列 (new) 总结
bottom half(BH) BH 接口非常简单,它提供一个静态创建、 由 32 个 bottom half 组成的链表。上半部分 通过一个 32 位整数中的一位来识出哪个 bottom half 可以执行。每个 BH 都在全局范 围内进行同步。即使分属于不同的处理器, 也不允许两个 bottom half 同时执行。 使用不灵活,大大降低多处理器的性能。
任务队列 内核定义了一组队列,其中每个队列都包含 一个由等待调用的函数组成链表。根据其 所处队列的位置,这些函数会在某个时刻 被执行。 不能胜任要求高的子系统。
Softirq & tasklet 从 2.3 开始引入。可完全代替 BH 接口。软中 断是一组静态定义的下半部分接口,有 32 个,可以在所有处理器上同时执行 — 即使 两个类型相同也可以。 Tasklet 是一种基于 软中断实现的灵活性强、动态创建的下半 部分实现机制。两个不同类型的 tasklet 可以 在不同的处理器上同时执行,但类型相同 的 tasklet 不能同时执行。
work quque 从 2.5 开始, BH 接口最终被弃置,任务队列被 工作队列( work queue )接口取代。工作 队列可以把工作推后,交由一个内核线程 去执行 — 这个下半部分总会在进程上下文 执行。工作队列允许重新调度甚至睡眠。
2.6 版本中,内核提供了三种不同形式的下 半部分实现机制:软中断、 tasklet 和工作队 列。
目录 简介 历史 & 发展 实现机制 软中断 Tasklet 工作队列 (new) 总结
实现 软中断由 softirq_action 结构表示,定义在 中。
Kernel/softirq.c 中定义了一个包含有 32 个 softirq_action 数组。每个被注册的软中断都 占据该数组中的一项,最多可有 32 个软中 断, 内核中可使用其中的 8 个。
软中断处理函数 void softirq_handler(struct softirq_action *) 当内核运行一个软中断处理程序的时候, 它就会执行这个 action 函数,其唯一的参数 为指向应 softirq_action 结构体的指针。
执行软中断 触发软中断 一个注册的软中断必须在标记后才会被执行, 通常,中断服务程序在返回前标记它的软 中断。在下列地方,待处理的软中断会被 检查和执行 从一个硬件中断代码处返回时。 在 ksoftirqd 内核线程中。 在显式检查和执行待处理的软中断代码中。
do_softirq()
核心部分
执行步骤 用局部变量 pending 保存 local_softirq_pending() 宏的返回值。 将实际的软中断位图清零。 将指针 h 指向 softirq_vec 的第一项。 如果 pending 的第一位被置为1, h- >action(h) 被调用。 指针加 1 ,现在它指向 softirq_vec 数组的第 二项 (续)
位掩码 pending 右移一位。然后让其他各位 依次向右移动一个位置。于是,原来的第 二位就在第一位的位置上。 重复执行上面的步骤,直到 pending 变为0。
目录 简介 历史 & 发展 实现机制 软中断 Tasklet 工作队列 (new) 总结
Tasklet 结构体 Tasklet 由 tasklet_struct 结构表示。每个结 构体代表一个 tasklet. Tasklet 的处理程序
调度 tasklet 已调度的 tasklet 存放在两个数据结构: tasklet_vec 和 tasklet_hi_vec 中。这两个都 是由 tasklet_struct 结构体构成的链表。 Tasklet 由 tasklet_schedule() 和 tasklet_hi_schedule() 调度,它们接受一个 指向 tasklet_struct 的指针作为参数。
执行 do_irq 执行相应的软中断处理程序,而 tasklet_action() 和 tasklet_hi_action() 就是 tasklet 处理的核心。
目录 简介 历史 & 发展 实现机制 软中断 Tasklet 工作队列 (new) 总结
实现 工作队列子系统是一个用于创建内核线程 的接口,通过它创建的进程负责执行由内 核其他部分排到队列里的任务。它创建的 这些线程被称为工作者线程。工作队列子 系统提供一个工作者线程来处理后半部分 的工作。因此,工作队列最基本的表现形 式就转变成了一个把需要推后执行的任务 次给特定的通用线程这样的一种接口。
线程的数据结构
结构中有一个 cpu_workqueue_struct 结构 组成的数组,每一项对应一个处理器。由 于每个处理器对应一个工作者线程,所以 对单台计算机来说,每个工作者线程对应 一个这样的 cpu_workqueue_struct 结构体。
所有的工作者线程都是用普通的内核线程 实现的,它们都要执行 worker_tread(). 在它 初始化完以后,这个函数执行一个列循环 并开始休眠。当有操作被插入到队列里的 时候,线程就会被唤醒,以便执行这些操 作。当没有剩余的操作时,它又会继续休 眠。
work_struct()
这些结构体被连接成链表,在每个处理器 上每种类型队列都对应这样一个链表。当 一个工作者线程被唤醒时,它会执行它的 链表上的所有工作。工作被执行完毕,它 就将相应的 work_struct 对象从链表上移去。 当链表上有再有对象的时候,它就会继续 休眠。
Work_thread()
核心 for 循环完成的工作 线程将自己设置为休眠状态并把自己加入到 等待队列上。 如果工作链表是空的,线程调用 schedule() 函 数进入睡眠状态 如果链表有对象,将自己设置成 TASK_RUNNING ,脱离等待队列。 如果链表非空,调用 run_workquque() 函数 执行后半部分的工作。
目录 简介 历史 & 发展 实现机制 软中断 Tasklet 工作队列 (new) 总结
小问题,大精力! 存在不是最好的!
谢谢!