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