系统相关
首页 > 系统相关> > 【原创】Linux中断子系统(三)-softirq和tasklet

【原创】Linux中断子系统(三)-softirq和tasklet

作者:互联网

背景

说明:

  1. Kernel版本:4.14
  2. ARM64处理器,Contex-A53,双核
  3. 使用工具:Source Insight 3.5, Visio

1. 概述

中断子系统中有一个重要的设计机制,那就是Top-half和Bottom-half,将紧急的工作放置在Top-half中来处理,而将耗时的工作放置在Bottom-half中来处理,这样确保Top-half能尽快完成处理,那么为什么需要这么设计呢?看一张图就明白了:

在中断处理过程中,离不开各种上下文的讨论,了解不同上下文的区分有助于中断处理的理解,所以,还是来一张老图吧:

前戏结束了,直奔主题吧。

2. softirq

2.1 初始化

softirq不支持动态分配,Linux kernel提供了静态分配,关键的结构体描述如下,可以类比硬件中断来理解:

/* 支持的软中断类型,可以认为是软中断号, 其中从上到下优先级递减 */
enum
{
	HI_SOFTIRQ=0,       /* 最高优先级软中断 */
	TIMER_SOFTIRQ,      /* Timer定时器软中断 */
	NET_TX_SOFTIRQ,     /* 发送网络数据包软中断 */
	NET_RX_SOFTIRQ,     /* 接收网络数据包软中断 */
	BLOCK_SOFTIRQ,      /* 块设备软中断 */
	IRQ_POLL_SOFTIRQ,   /* 块设备软中断 */
	TASKLET_SOFTIRQ,    /* tasklet软中断 */
	SCHED_SOFTIRQ,      /* 进程调度及负载均衡的软中断 */
	HRTIMER_SOFTIRQ, /* Unused, but kept as tools rely on thenumbering. Sigh! */
	RCU_SOFTIRQ,    /* Preferable RCU should always be the last softirq, RCU相关的软中断 */

	NR_SOFTIRQS
};

/* 软件中断描述符,只包含一个handler函数指针 */
struct softirq_action {
	void	(*action)(struct softirq_action *);
};
/* 软中断描述符表,实际上就是一个全局的数组 */
static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp;

/* CPU软中断状态描述,当某个软中断触发时,__softirq_pending会置位对应的bit */
typedef struct {
	unsigned int __softirq_pending;
	unsigned int ipi_irqs[NR_IPI];
} ____cacheline_aligned irq_cpustat_t;
/* 每个CPU都会维护一个状态信息结构 */
irq_cpustat_t irq_stat[NR_CPUS] ____cacheline_aligned;

/* 内核为每个CPU都创建了一个软中断处理内核线程 */
DEFINE_PER_CPU(struct task_struct *, ksoftirqd);

来一张图吧:

2.2 流程分析

2.2.1 软中断注册

中断处理流程中设备驱动通过request_irq/request_threaded_irq接口来注册中断处理函数,而在软中断处理流程中,通过open_softirq接口来注册,由于它实在是太简单了,我忍不住想把代码贴上来:

void open_softirq(int nr, void (*action)(struct softirq_action *))
{
	softirq_vec[nr].action = action;
}

也就是将软中断描述符表中对应描述符的handler函数指针指向对应的函数即可,以便软中断到来时进行回调。

那么,问题来了,什么时候进行软中断函数回调呢?

2.2.2 软中断执行之一:中断处理后

先看第一种情况,用图片来回答问题:

软中断执行的入口就是invoke_softirq,继续分析一波:

上图中的逻辑可以看出,最终的核心处理都放置在__do_softirq函数中完成:

__do_softirq既然可以在中断处理过程中调用,也可以在ksoftirqd中调用,那么softirq的执行可能有两种context,插张图吧:

让我们来思考最后一个问题:硬件中断触发的时候是通过硬件设备的电信号,那么软中断的触发是通过什么呢?答案是通过raise_softirq接口:

2.2.3 软中断执行之二:Bottom-half Enable后

第二种软中断执行的时间点,在Bottom-half使能的时候,通常用于并发处理,进程空间上下文中进行调用:

3. tasklet

从上文中分析可以看出,tasklet是软中断的一种类型,那么两者有啥区别呢?先说结论吧:

3.1 数据结构

3.2 流程分析

3.3 接口

简单贴一下接口吧:

/* 静态分配tasklet */
DECLARE_TASKLET(name, func, data)

/* 动态分配tasklet */
void tasklet_init(struct tasklet_struct *t, void (*func)(unsigned long), unsigned long data);

/* 禁止tasklet被执行,本质上是增加tasklet_struct->count值,以便在调度时不满足执行条件 */
void tasklet_disable(struct tasklet_struct *t);

/* 使能tasklet,与tasklet_diable对应 */
void tasklet_enable(struct tasklet_struct *t);

/* 调度tasklet,通常在设备驱动的中断函数里调用 */
void tasklet_schedule(struct tasklet_struct *t);

/* 杀死tasklet,确保不被调度和执行, 主要是设置state状态位 */
void tasklet_kill(struct tasklet_struct *t);

收工!

欢迎关注个人公众号,不定期分享Linux内核机制文章。

标签:struct,中断,处理,Linux,tasklet,CPU,softirq
来源: https://www.cnblogs.com/LoyenWang/p/13124803.html