Linux内核学习——下半部与推后执行的工作

下半部与推后执行的工作

中断处理程序异步执行,需要快速地执行硬件与操作系统的操作,避免其它中断停止太久,因此中断处理程序不能阻塞。

下半部

上半部只能通过中断处理程序实现,而下半部则提供了多种实现机制——软中断、tasklet、工作队列。

内核定时器,这个机制用来将工作推后执行,推后到某个明确的时间段之后执行。

软中断

软中断是一组静态定义的下半部接口,有32个。

1
2
3
struct softirq_action {
void (*action)(struct softirq_action *);
};
1
static struct softirq_action softirq_vec[NR_SOFTIRQS];
  1. 软中断处理程序
1
2
3
4
void sofrirq_handler(struct softirq_action *)
{
my_softirq->action(my_softirq);
}

之所以传递一个指针而不是一个数值,是因为防止后面加上新的域时,无需对处理程序进行改动;

  1. 执行软中断

一个注册的软中断会在中断处理程序返回前标记了之后才能被执行,会在标记之后一定时间内执行。

在中断处理程序中触发软中断是比较常见的。软中断会在do_softirq()中遍历所有软中断,进行调用。

tasklet

tasklet是使用软中断实现的一种下半部机制,接口更加简单。

tasklet的实现

tasklet有两种软中断代表:HI_SOFTIRQ和TASKLET_SOFTIRQ。

  1. tasklet结构体
1
2
3
4
5
6
7
struct tasklet_struct{
struct tasklet_struct *next; //链表中下一个tasklet_struct
unsigned long state; //0, TASKLET_STATE_SCHED,TASKLET_STATE_RUN
atomic_t count; //为0时才被激活
void (*func)(unsigned long);//tasklet处理函数
unsigned long data; //给tasklet
};
  1. 调度tasklet

已经调度的tasklet相当于被触发的软中断,会被放在两个数据结构里——tasklet_vec(普通tasklet)和tasklet_hi_vec(高优先级的tasklet)。

tasklet由tasklet_schedule()和tasklet_hi_schedule()进行调度,它接受一个指向tasklet_struct结构的指针作为参数。

执行步骤:

  1. 检查tasklet的状态,如果是TASKLET_STATE_SCHED则直接返回;
  2. 调用_tasklet_schedule();
  3. 保持中断状态,禁止本地中断;
  4. 把需要调度的tasklet加到链表里;
  5. 唤起软中断,这样在后面调用do_softriq()时执行这个tasklet;
  6. 恢复中断到原状态返回;

工作队列

工作队列是另一种将工作推后实现的机制,它是通过将工作交由一个内核线程在进程上下文执行的方法。工作队列可以重新调度,也可以睡眠。

下半部机制的选择

  • 软中断处于中断上下文里,同类型的软中断能同时执行;
  • tasklet处于中断上下文里,同类型不能同时执行;
  • 工作队列处于进程上下文,能睡眠和唤醒;