400 likes | 526 Views
Linux2.6 内核中”下半部分”分析. Group:N3608. 目录. 简介 历史 & 发展 实现机制 软中断 Tasklet 工作队列 (new) 总结. 目录. 简介 历史 & 发展 实现机制 软中断 Tasklet 工作队列 (new) 总结. 中断服务一般都是在将中断请求关闭的条件下执行,以避免嵌套而使控制复杂化。可是如果关中断的时间太长就可能因为 CPU 不能及时响应其他的中断请求而使中断丢失;如果在将中断服务程序挂入中断请求队列时开中断,又会使中断过程不安全。 如何解决这种矛盾?.
E N D
Linux2.6内核中”下半部分”分析 Group:N3608
目录 • 简介 • 历史&发展 • 实现机制 • 软中断 • Tasklet • 工作队列(new) • 总结
目录 • 简介 • 历史&发展 • 实现机制 • 软中断 • Tasklet • 工作队列(new) • 总结
中断服务一般都是在将中断请求关闭的条件下执行,以避免嵌套而使控制复杂化。可是如果关中断的时间太长就可能因为CPU不能及时响应其他的中断请求而使中断丢失;如果在将中断服务程序挂入中断请求队列时开中断,又会使中断过程不安全。中断服务一般都是在将中断请求关闭的条件下执行,以避免嵌套而使控制复杂化。可是如果关中断的时间太长就可能因为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)接口取代。工作队列可以把工作推后,交由一个内核线程去执行—这个下半部分总会在进程上下文执行。工作队列允许重新调度甚至睡眠。
目录 • 简介 • 历史&发展 • 实现机制 • 软中断 • Tasklet • 工作队列(new) • 总结
实现 软中断由softirq_action结构表示,定义在<include/linux/interrupt.h>中。
Kernel/softirq.c中定义了一个包含有32个softirq_action数组。每个被注册的软中断都占据该数组中的一项,最多可有32个软中断,2.6.24.1内核中可使用其中的8个。Kernel/softirq.c中定义了一个包含有32个softirq_action数组。每个被注册的软中断都占据该数组中的一项,最多可有32个软中断,2.6.24.1内核中可使用其中的8个。
软中断处理函数 void softirq_handler(struct softirq_action *) 当内核运行一个软中断处理程序的时候,它就会执行这个action函数,其唯一的参数为指向应softirq_action结构体的指针。
执行软中断 触发软中断 一个注册的软中断必须在标记后才会被执行,通常,中断服务程序在返回前标记它的软中断。在下列地方,待处理的软中断会被检查和执行 从一个硬件中断代码处返回时。 在ksoftirqd内核线程中。 在显式检查和执行待处理的软中断代码中。
执行步骤 • 用局部变量pending保存local_softirq_pending()宏的返回值。 • 将实际的软中断位图清零。 • 将指针h指向softirq_vec的第一项。 • 如果pending的第一位被置为1, h->action(h)被调用。 • 指针加1,现在它指向softirq_vec数组的第二项 (续)
位掩码pending右移一位。然后让其他各位依次向右移动一个位置。于是,原来的第二位就在第一位的位置上。位掩码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结构体。结构中有一个cpu_workqueue_struct结构组成的数组,每一项对应一个处理器。由于每个处理器对应一个工作者线程,所以对单台计算机来说,每个工作者线程对应一个这样的cpu_workqueue_struct结构体。
所有的工作者线程都是用普通的内核线程实现的,它们都要执行worker_tread().在它初始化完以后,这个函数执行一个列循环并开始休眠。当有操作被插入到队列里的时候,线程就会被唤醒,以便执行这些操作。当没有剩余的操作时,它又会继续休眠。 所有的工作者线程都是用普通的内核线程实现的,它们都要执行worker_tread().在它初始化完以后,这个函数执行一个列循环并开始休眠。当有操作被插入到队列里的时候,线程就会被唤醒,以便执行这些操作。当没有剩余的操作时,它又会继续休眠。
这些结构体被连接成链表,在每个处理器上每种类型队列都对应这样一个链表。当一个工作者线程被唤醒时,它会执行它的链表上的所有工作。工作被执行完毕,它就将相应的work_struct对象从链表上移去。当链表上有再有对象的时候,它就会继续休眠。 这些结构体被连接成链表,在每个处理器上每种类型队列都对应这样一个链表。当一个工作者线程被唤醒时,它会执行它的链表上的所有工作。工作被执行完毕,它就将相应的work_struct对象从链表上移去。当链表上有再有对象的时候,它就会继续休眠。
核心for循环完成的工作 线程将自己设置为休眠状态并把自己加入到等待队列上。 如果工作链表是空的,线程调用schedule()函数进入睡眠状态 如果链表有对象,将自己设置成TASK_RUNNING,脱离等待队列。 如果链表非空,调用run_workquque() 函数执行后半部分的工作。
目录 • 简介 • 历史&发展 • 实现机制 • 软中断 • Tasklet • 工作队列(new) • 总结
小问题,大精力! • 存在不是最好的!