linux中断下半部

Linux内核中的Tasklet调度机制:tasklet_schedulesoftirq_init的调用关系解析 🚀

在Linux内核中,软中断(SoftIRQ) 💫 和 Tasklet 🛠️ 是实现中断下半部(Bottom Half)的核心机制。其中,softirq_init 是软中断子系统的初始化入口,而 tasklet_schedule 是Tasklet机制的调度核心。本文将揭示Linux内核如何通过它们实现高效的中断延迟任务处理。


linux

一、核心机制背景 🔍

1. 软中断(SoftIRQ) ⚡

软中断是Linux内核中处理延迟敏感任务的核心框架,支持静态注册的预定义类型(如网络收发🌐、定时器⏰、Tasklet等)。其特点包括:

  • 🚦优先级分层:支持多个优先级(如 HI_SOFTIRQ 高于 TASKLET_SOFTIRQ)。
  • 🔄可重入性:同一软中断可在不同CPU上并行执行。
  • 🔒原子性约束:运行在软中断上下文,不可睡眠。

2. Tasklet 🛠️

Tasklet是基于软中断(TASKLET_SOFTIRQHI_SOFTIRQ)的动态任务队列机制,特点包括:

  • ⏳串行执行:同一Tasklet在多个CPU上不会并发执行。
  • 📝动态注册:通过 tasklet_initDECLARE_TASKLET 动态创建。
  • ⚖️轻量级:适用于小型延迟任务(如硬件中断后的数据预处理)。

二、初始化:softirq_init 的职责 🛠️

softirq_init 是软中断子系统的初始化函数,在内核启动阶段(start_kernel)被调用,主要完成以下任务:

1. 初始化每CPU的Tasklet链表 🔗

void __init softirq_init(void) {
    int cpu;

    for_each_possible_cpu(cpu) {
        // 初始化普通优先级Tasklet链表
        per_cpu(tasklet_vec, cpu).tail = &per_cpu(tasklet_vec, cpu).head;
        // 初始化高优先级Tasklet链表
        per_cpu(tasklet_hi_vec, cpu).tail = &per_cpu(tasklet_hi_vec, cpu).head;
    }

    // 注册TASKLET_SOFTIRQ的处理函数
    open_softirq(TASKLET_SOFTIRQ, tasklet_action);
    // 注册HI_SOFTIRQ的处理函数
    open_softirq(HI_SOFTIRQ, tasklet_hi_action);
}
  • 📂链表结构:每个CPU维护两个Tasklet链表(tasklet_vectasklet_hi_vec),分别对应普通和高优先级任务。
  • 🎯尾指针优化:通过 tail 指针实现O(1)时间复杂度的链表追加操作。

2. 注册软中断处理函数 📌

  • open_softirq 将软中断号与处理函数绑定:
    • TASKLET_SOFTIRQtasklet_action 🛠️
    • HI_SOFTIRQtasklet_hi_action 🔥

三、调度入口:tasklet_schedule 的工作流程 🚀

tasklet_schedule 是驱动开发者最常用的接口,用于将Tasklet加入调度队列。其核心步骤如下:

1. 检查并设置Tasklet状态 ✅

static inline void tasklet_schedule(struct tasklet_struct *t) {
    // 检查Tasklet是否已被调度(TASKLET_STATE_SCHED)
    if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
        __tasklet_schedule(t);
}
  • ⚡原子操作test_and_set_bit 确保同一Tasklet不会被重复加入队列。

2. 将Tasklet加入当前CPU链表 🔄

void __tasklet_schedule(struct tasklet_struct *t) {
    unsigned long flags;

    // 关闭本地中断,防止并发修改 🔒
    local_irq_save(flags);
    // 将Tasklet添加到链表尾部 ➕
    t->next = NULL;
    *__this_cpu_read(tasklet_vec.tail) = t;
    __this_cpu_write(tasklet_vec.tail, &t->next);
    // 触发TASKLET_SOFTIRQ软中断 🚨
    raise_softirq_irqoff(TASKLET_SOFTIRQ);
    // 恢复本地中断 🔓
    local_irq_restore(flags);
}
  • 🔗链表操作:通过尾指针 tail 实现高效追加。
  • 🚀触发软中断:调用 raise_softirq_irqoff 设置当前CPU的软中断挂起标志。

四、处理流程:软中断触发与Tasklet执行 ⚙️

当软中断被触发后,内核会在适当的时机(如中断返回时)调用 tasklet_actiontasklet_hi_action 处理Tasklet。

Tasklet处理函数 tasklet_action 🛠️

static void tasklet_action(struct softirq_action *a) {
    struct tasklet_struct *list;

    // 原子获取当前CPU的Tasklet链表 🔄
    local_irq_disable();
    list = __this_cpu_read(tasklet_vec.head);
    __this_cpu_write(tasklet_vec.head, NULL);
    __this_cpu_write(tasklet_vec.tail, &__this_cpu_read(tasklet_vec.head));
    local_irq_enable();

    // 遍历链表并处理每个Tasklet 🔍
    while (list) {
        struct tasklet_struct *t = list;
        list = list->next;

        // 尝试加锁(防止多CPU并发执行同一Tasklet) 🔒
        if (tasklet_trylock(t)) {
            if (!atomic_read(&t->count)) {
                // 清除调度状态,执行回调 🧹
                if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state))
                    BUG();
                t->func(t->data);
            }
            tasklet_unlock(t);
        } else {
            // 加锁失败,重新加入链表并触发软中断 🔄
        }
    }
}
  • ⛓️串行保证tasklet_trylock 通过原子操作检查 TASKLET_STATE_RUN 标志,确保同一Tasklet不会跨CPU并发执行。

五、调用关系全景图 🗺️

未调度
已调度
TASKLET_SOFTIRQ
HI_SOFTIRQ
成功
失败
系统启动
softirq_init 初始化
初始化每CPU Tasklet链表
注册 TASKLET_SOFTIRQ
注册 HI_SOFTIRQ
驱动调用 tasklet_schedule
检查状态
加入CPU本地链表
直接返回
触发软中断
中断返回路径
检查软中断
调用 tasklet_action
调用 tasklet_hi_action
原子获取链表
遍历Tasklet
尝试加锁
执行回调
重新入队
清除状态
触发软中断重试

六、设计哲学与性能考量 🧠

  1. 🚀无锁化设计

    • 通过每CPU链表避免多核竞争。
    • 原子操作(test_and_set_bit)替代传统锁。
  2. ⏳延迟处理优化

    • 合并多次Tasklet调度,减少软中断触发次数。
    • 允许在 ksoftirqd 线程中处理积压任务。
  3. 🛡️实时性保障

    • 高优先级Tasklet(HI_SOFTIRQ)优先执行。
    • 限制单个软中断处理时间,防止CPU被独占。

七、总结 🎯

  • softirq_init:在内核启动阶段初始化Tasklet框架,注册软中断处理函数。🏗️
  • tasklet_schedule:在运行时动态调度Tasklet,利用软中断机制实现延迟执行。⚡
  • 🤝协作关系softirq_init 搭建舞台,tasklet_schedule 在此舞台上高效调度任务,二者共同构成Linux内核中断下半部的核心基础设施。

dance

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值