news 2026/3/20 20:08:54

嵌入式Linux软件中断概述

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
嵌入式Linux软件中断概述

嵌入式Linux软件中断概述

一、软件中断概述

软件中断(SoftIRQ)是Linux内核中中断处理的下半部机制,用于延迟处理非紧急的中断任务,平衡系统实时性和吞吐量。

1.1 软件中断特点

  • 延迟执行:在中断上下文中标记,在合适时机处理
  • 可抢占:处理过程中可以被更高优先级任务抢占
  • 多核并行:每个CPU有独立的软件中断处理
  • 优先级机制:HI_SOFTIRQ优先级最高,RCU_SOFTIRQ最低

1.2 软件中断类型

软中断类型编号说明典型用途
HI_SOFTIRQ0高优先级tasklet高实时性要求任务
TIMER_SOFTIRQ1定时器处理时钟中断下半部
NET_TX_SOFTIRQ2网络发送网络数据包发送
NET_RX_SOFTIRQ3网络接收网络数据包接收(NAPI)
BLOCK_SOFTIRQ4块设备块设备I/O完成
IRQ_POLL_SOFTIRQ5IRQ轮询中断轮询机制
TASKLET_SOFTIRQ6普通tasklet一般延迟任务
SCHED_SOFTIRQ7调度负载均衡和调度
HRTIMER_SOFTIRQ8高精度定时器高精度时钟处理
RCU_SOFTIRQ9RCU回调RCU延迟释放

二、软件中断核心数据结构

2.1 软件中断向量表

c
// 软件中断处理函数定义
struct softirq_action {
void (*action)(struct softirq_action *);
};
// 全局软中断向量表(每个CPU独立)
static struct softirq_action softirq_vec[NR_SOFTIRQS]
__cacheline_aligned_in_smp;

2.2 每CPU软件中断状态

c
// 每个CPU的软中断状态
typedef struct {
unsigned int __softirq_pending; // 挂起的软中断位图
unsigned int ipi_pending; // IPI中断
} irq_cpustat_t;
DECLARE_PER_CPU_SHARED_ALIGNED(irq_cpustat_t, irq_stat);

2.3 Tasklet数据结构

c
// Tasklet结构体
struct tasklet_struct {
struct tasklet_struct *next; // 链表指针
unsigned long state; // 状态位
atomic_t count; // 引用计数
void (*func)(unsigned long); // 处理函数
unsigned long data; // 函数参数
};
// Tasklet状态定义
enum {
TASKLET_STATE_SCHED, // Tasklet已调度
TASKLET_STATE_RUN, // Tasklet正在运行
};

三、软件中断执行流程

3.1 完整执行流程图

┌─────────────────┐
│ 硬件中断触发 │
└────────┬────────┘


┌─────────────────┐
│ 硬件中断处理程序 │
└────────┬────────┘


┌─────────────────┐
│ raise_softirq() │
└────────┬────────┘


┌─────────────────┐
│ 设置pending位图 │
└────────┬────────┘


┌─────────────────┐
│ irq_exit() │
└────────┬────────┘
├────────────────────────────┐
│是否需要立即处理? │
▼ │
┌─────────────────┐ │
│ invoke_softirq()│ │
└────────┬────────┘ │
│ │
▼ │
┌─────────────────┐ │
│ __do_softirq() │ │
└────────┬────────┘ │
│ │
▼ │
┌─────────────────┐ │
│ 处理pending软中断 │ │
└─────────────────┘ │
│ │
│ │
▼ │
┌─────────────────┐ │
│ wakeup_softirqd()│◀─────────────────┘
└────────┬────────┘


┌─────────────────┐
│ ksoftirqd线程唤醒 │
└────────┬────────┘


┌─────────────────┐
│ run_ksoftirqd() │
└────────┬────────┘


┌─────────────────┐
│ __do_softirq() │
└─────────────────┘

3.2 详细执行步骤

步骤1:硬件中断触发软件中断

c
// 硬件中断处理中触发软件中断
irqreturn_t handle_irq_event(struct irq_desc *desc)
{
// … 硬件中断处理 …
// 触发对应的软件中断
if (need_softirq) {
__raise_softirq_irqoff(softirq_nr);
}

return IRQ_HANDLED;
}

步骤2:中断退出路径

c
void irq_exit(void)
{
// … 中断退出处理 …
// 检查并处理软件中断
if (!in_interrupt() && local_softirq_pending())
invoke_softirq();
}

步骤3:决定处理方式

c
static inline void invoke_softirq(void)
{
if (force_irqthreads) {
// 强制使用线程化处理
wakeup_softirqd();
} else {
// 如果ksoftirqd正在运行,唤醒它
if (__this_cpu_read(ksoftirqd) &&
__this_cpu_read(ksoftirqd)->state == TASK_RUNNING)
wakeup_softirqd();
else
__do_softirq(); // 直接处理
}
}

四、软件中断处理机制

4.1 __do_softirq() 核心处理

c
asmlinkage __visible void __do_softirq(void)
{
unsigned long end = jiffies + MAX_SOFTIRQ_TIME; // 时间限制
int max_restart = MAX_SOFTIRQ_RESTART; // 重启次数限制
__u32 pending;
// 1. 获取pending的软中断
pending = local_softirq_pending();

// 2. 进入软中断上下文
__local_bh_disable_ip(RET_IP, SOFTIRQ_OFFSET);
restart:
// 3. 清除pending位,允许新中断
set_softirq_pending(0);
// 4. 开启本地中断
local_irq_enable();

// 5. 处理所有pending的软中断
h = softirq_vec;
while ((softirq_bit = ffs(pending))) {
unsigned int vec_nr = softirq_bit - 1;

// 执行软中断处理函数 h->action(h); h++; pending >>= softirq_bit;

}

// 6. 关闭本地中断
local_irq_disable();

// 7. 检查是否有新的软中断
pending = local_softirq_pending();
if (pending) {
// 如果未超时且不需要调度,重启处理
if (time_before(jiffies, end) &&
!need_resched() &&
–max_restart)
goto restart;

// 否则唤醒ksoftirqd线程 wakeup_softirqd();

}

// 8. 退出软中断上下文
__local_bh_enable(SOFTIRQ_OFFSET);
}

4.2 软件中断初始化

c
void __init softirq_init(void)
{
int cpu;
// 为每个CPU初始化tasklet链表
for_each_possible_cpu(cpu) {
per_cpu(tasklet_vec, cpu).tail =
&per_cpu(tasklet_vec, cpu).head;
per_cpu(tasklet_hi_vec, cpu).tail =
&per_cpu(tasklet_hi_vec, cpu).head;
}

// 注册各软中断类型的处理函数
open_softirq(TASKLET_SOFTIRQ, tasklet_action);
open_softirq(HI_SOFTIRQ, tasklet_hi_action);
open_softirq(TIMER_SOFTIRQ, run_timer_softirq);
open_softirq(NET_TX_SOFTIRQ, net_tx_action);
open_softirq(NET_RX_SOFTIRQ, net_rx_action);
// … 其他软中断注册
}

五、ksoftirqd内核线程

5.1 ksoftirqd线程定义

c
// SMP热插拔线程定义
static struct smp_hotplug_thread softirq_threads = {
.store = &ksoftirqd, // 每CPU线程指针
.thread_should_run = ksoftirqd_should_run, // 运行条件
.thread_fn = run_ksoftirqd, // 线程函数
.thread_comm = “ksoftirqd/%u”, // 线程名称
.setup = ksoftirqd_setup, // 设置函数
.cleanup = ksoftirqd_cleanup, // 清理函数
};

5.2 ksoftirqd线程函数

c
static void run_ksoftirqd(unsigned int cpu)
{
// 设置线程名称和调度参数
snprintf(current->comm, sizeof(current->comm), “ksoftirqd/%u”, cpu);
sched_set_fifo_low(current);
while (!kthread_should_stop()) {
// 1. 检查是否有pending的软中断
if (!local_softirq_pending()) {
// 无pending,进入睡眠
set_current_state(TASK_INTERRUPTIBLE);
schedule();
continue;
}

__set_current_state(TASK_RUNNING); // 2. 处理软中断 do { __do_softirq(); } while (!kthread_should_stop() && local_softirq_pending()); // 3. 让出CPU cond_resched(); set_current_state(TASK_INTERRUPTIBLE);

}

__set_current_state(TASK_RUNNING);
}

5.3 嵌入式系统调优配置

c
static int ksoftirqd_setup(unsigned int cpu)
{
struct sched_param param = { .sched_priority = 1 };
struct task_struct *tsk = per_cpu(ksoftirqd, cpu);
cpumask_t cpumask;
// 1. 设置实时优先级
sched_setscheduler_nocheck(tsk, SCHED_FIFO, &param);

// 2. 设置CPU亲和性(绑定到小核)
cpumask_clear(&cpumask);
if (cpu_is_little(cpu))
cpumask_set_cpu(cpu, &cpumask);
else
cpumask_set_cpu(get_little_core(), &cpumask);
set_cpus_allowed_ptr(tsk, &cpumask);

// 3. 设置nice值
set_user_nice(tsk, -10);

return 0;
}

六、Tasklet机制

6.1 Tasklet执行流程

┌─────────────┐
│tasklet初始化 │
└──────┬──────┘


┌─────────────┐
│tasklet_schedule│
└──────┬──────┘


┌─────────────┐
│ 设置SCHED状态 │
└──────┬──────┘


┌─────────────┐
│ 添加到CPU链表 │
└──────┬──────┘


┌─────────────┐
│ 触发软中断 │
└──────┬──────┘


┌─────────────┐
│tasklet_action│
└──────┬──────┘


┌─────────────┐
│ 执行处理函数 │
└─────────────┘

6.2 Tasklet调度与处理

c
// Tasklet调度
void __tasklet_schedule(struct tasklet_struct *t)
{
unsigned long flags;
local_irq_save(flags);

// 添加到per-CPU链表
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);
}
// Tasklet处理
static __latent_entropy 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_ptr(&tasklet_vec.head));
local_irq_enable();

// 处理所有tasklet
while (list) {
struct tasklet_struct *t = list;
list = list->next;

if (tasklet_trylock(t)) { if (!atomic_read(&t->count)) { // 执行tasklet处理函数 t->func(t->data); tasklet_unlock(t); continue; } tasklet_unlock(t); } // 重新调度未处理的tasklet local_irq_disable(); t->next = NULL; *__this_cpu_read(tasklet_vec.tail) = t; __this_cpu_write(tasklet_vec.tail, &(t->next)); __raise_softirq_irqoff(TASKLET_SOFTIRQ); local_irq_enable();

}
}

七、嵌入式系统优化策略

7.1 CPU亲和性配置

bash
将ksoftirqd绑定到特定CPU
taskset -p 1 $(pidof ksoftirqd/0) # 绑定到CPU0
taskset -p 2 $(pidof ksoftirqd/1) # 绑定到CPU1
将网络中断绑定到特定CPU
echo 2 > /proc/irq/100/smp_affinity # IRQ 100绑定到CPU1

7.2 内核参数调优

bash
调整网络软中断处理预算
echo 600 > /proc/sys/net/core/netdev_budget
echo 60000 > /proc/sys/net/core/netdev_budget_usecs
启用RPS(Receive Packet Steering)
echo f > /sys/class/net/eth0/queues/rx-0/rps_cpus
设置实时优先级
chrt -f 50 $(pidof ksoftirqd/0)
chrt -f 50 $(pidof ksoftirqd/1)

7.3 负载均衡策略

c
// 软中断负载均衡
static void migrate_softirq(int src_cpu, int dst_cpu)
{
unsigned int pending = per_cpu(irq_stat, src_cpu).__softirq_pending;
int i;
for_each_set_bit(i, &pending, NR_SOFTIRQS) {
// 迁移可迁移的软中断
if (softirq_vec[i].migratable) {
per_cpu(irq_stat, src_cpu).__softirq_pending &= ~(1 << i);
per_cpu(irq_stat, dst_cpu).__softirq_pending |= (1 << i);
wake_up_process(per_cpu(ksoftirqd, dst_cpu));
}
}
}

八、网络软中断(NAPI)

8.1 NAPI处理流程

c
static __latent_entropy void net_rx_action(struct softirq_action *h)
{
struct softnet_data *sd = this_cpu_ptr(&softnet_data);
unsigned long time_limit = jiffies + 2;
int budget = netdev_budget;
LIST_HEAD(list);
// 获取poll列表
list_splice_init(&sd->poll_list, &list);

while (!list_empty(&list)) {
struct napi_struct *n;

// 检查预算和时间 if (unlikely(budget <= 0 || time_after_eq(jiffies, time_limit))) goto softnet_break; n = list_first_entry(&list, struct napi_struct, poll_list); // 执行NAPI poll函数 int work = n->poll(n, budget); budget -= work; if (work < budget) { // 处理完成 list_del(&n->poll_list); napi_complete_done(n, work); }

}

return;
softnet_break:
// 将未处理的设备放回列表
list_splice_tail(&list, &sd->poll_list);
// 触发下一次软中断
__raise_softirq_irqoff(NET_RX_SOFTIRQ);
}

九、实时性优化

9.1 实时补丁(RT-Preempt)支持

c
ifdef CONFIG_PREEMPT_RT
// 实时补丁中的软中断线程化
struct softirq_thread {
struct task_struct *thread;
struct sched_param param;
int cpu;
wait_queue_head_t wait;
bool running;
};
static DEFINE_PER_CPU(struct softirq_thread, softirq_threads);
// 实时补丁中的软中断处理
static int softirq_thread_fn(void *data)
{
struct softirq_thread *sirq = data;
sched_setscheduler_nocheck(current, SCHED_FIFO, &sirq->param);
set_cpus_allowed_ptr(current, cpumask_of(sirq->cpu));

while (!kthread_should_stop()) {
wait_event_interruptible(sirq->wait,
kthread_should_stop() ||
local_softirq_pending() & sirq->softirq_mask);

sirq->running = true; sirq->handler(); sirq->running = false;

}

return 0;
}
endif

9.2 延迟测量

c
// 软中断延迟测量
struct softirq_latency {
ktime_t enter_time;
ktime_t exit_time;
unsigned int max_latency_ns;
unsigned int total_latency_ns;
unsigned int count;
};
static DEFINE_PER_CPU(struct softirq_latency[NR_SOFTIRQS], softirq_latencies);
static inline void softirq_enter(int nr)
{
struct softirq_latency *lat = &per_cpu(softirq_latencies, smp_processor_id())[nr];
lat->enter_time = ktime_get();
}
static inline void softirq_exit(int nr)
{
struct softirq_latency *lat = &per_cpu(softirq_latencies, smp_processor_id())[nr];
ktime_t delta = ktime_sub(ktime_get(), lat->enter_time);
unsigned int delta_ns = (unsigned int)ktime_to_ns(delta);
lat->total_latency_ns += delta_ns;
lat->count++;

if (delta_ns > lat->max_latency_ns)
lat->max_latency_ns = delta_ns;

// 延迟超限警告
if (delta_ns > SOFTIRQ_LATENCY_WARN_THRESHOLD_NS) {
printk_deferred_once(KERN_WARNING
“softirq %d (%s) took %u ns\n”,
nr, softirq_to_name[nr], delta_ns);
}
}

十、监控与调试

10.1 监控命令

bash

  1. 查看软中断统计
    cat /proc/softirqs
    输出示例:
    CPU0 CPU1
    HI: 5 2
    TIMER: 123456 789012
    NET_TX: 123 456
    NET_RX: 456789 123456
    BLOCK: 12 34
    TASKLET: 56 78
    SCHED: 234567 345678
    HRTIMER: 12 34
    RCU: 789012 456789
  2. 查看ksoftirqd线程状态
    ps -eLo pid,tid,psr,pcpu,pri,rtprio,ni,policy,stat,wchan:20,comm | grep ksoftirq
    输出示例:
    PID TID PSR %CPU PRI RTPRIO NI POLICY STAT WCHAN COMMAND
    12 12 0 1.2 20 - 0 SCHED S smp_call_function ksoftirqd/0
    13 13 1 0.8 20 - 0 SCHED S smp_call_function ksoftirqd/1
  3. 跟踪软中断事件
    echo 1 > /sys/kernel/debug/tracing/events/irq/softirq_entry/enable
    echo 1 > /sys/kernel/debug/tracing/events/irq/softirq_exit/enable
    cat /sys/kernel/debug/tracing/trace_pipe

10.2 性能分析

bash

  1. CPU使用率分析(包含软中断)
    mpstat -P ALL 1
    输出示例:
    12:00:00 CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle
    12:00:01 all 5.2 0.0 3.1 0.2 0.1 1.5 0.0 0.0 0.0 89.9
    12:00:01 0 6.1 0.0 4.2 0.3 0.2 2.1 0.0 0.0 0.0 87.1
    12:00:01 1 4.3 0.0 2.0 0.1 0.0 0.9 0.0 0.0 0.0 92.7
  2. 查看系统中断统计
    cat /proc/interrupts
  3. 查看网络软中断统计
    cat /proc/net/softnet_stat

十一、常见问题排查

11.1 软中断负载过高

bash

  1. 检查哪个CPU软中断负载高
    watch -n 1 ‘cat /proc/softirqs’
  2. 检查网络软中断
    watch -n 1 ‘cat /proc/net/softnet_stat | column -t’
  3. 检查ksoftirqd线程状态
    top -H -p $(pgrep ksoftirqd)
  4. 使用perf分析软中断热点
    perf record -g -a -e irq:softirq_entry sleep 10
    perf report

11.2 实时性问题排查

bash

  1. 检查实时延迟
    cyclictest -t1 -p 80 -n -i 10000 -l 10000
  2. 跟踪软中断延迟
    echo 1 > /sys/kernel/debug/tracing/events/irq/softirq_raise/enable
    echo 1 > /sys/kernel/debug/tracing/events/irq/softirq_entry/enable
    echo 1 > /sys/kernel/debug/tracing/events/irq/softirq_exit/enable
    cat /sys/kernel/debug/tracing/trace_pipe > /tmp/softirq_trace.txt
  3. 分析延迟分布
    trace-cmd report -i /tmp/softirq_trace.txt | grep “softirq” |
    awk ‘{print $5}’ | sort -n | uniq -c

十二、总结

12.1 关键要点

  1. 分层处理机制:硬件中断 → 软件中断 → ksoftirqd线程
  2. 实时性平衡:中断上下文中快速标记,线程上下文中长时间处理
  3. 多核优化:每个CPU有独立的软中断处理,支持负载均衡
  4. 可配置性:通过内核参数和调度策略优化性能
  5. 监控完善:通过/proc、调试接口和跟踪点全面监控

12.2 嵌入式系统注意事项

  • CPU亲和性:合理绑定中断和线程到特定CPU
  • 实时性配置:根据应用需求设置优先级和调度策略
  • 资源限制:在资源受限环境中合理配置预算和时间限制
  • 功耗管理:与CPU空闲状态和电源管理机制协同工作

理解Linux软件中断的实现机制和执行流程,对于嵌入式系统开发和性能优化至关重要。通过合理的配置和调优,可以在保证实时性的同时提高系统吞吐量。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/19 17:59:01

手把手教你使用USB Burning Tool进行固件烧录

从“变砖”到重生&#xff1a;深入掌握USB Burning Tool的实战秘籍你有没有遇到过这样的场景&#xff1f;一台智能电视盒插上电&#xff0c;屏幕却毫无反应&#xff1b;串口输出停在UBOOT阶段&#xff0c;反复重启——典型的“变砖”。这时候OTA升级救不了你&#xff0c;SD卡启…

作者头像 李华
网站建设 2026/3/15 19:58:06

Qt 信号与槽机制深度解析

目录一、 connect 函数的深度应用与原理1.1 信号与槽的关联逻辑1.2 connect 函数的参数详解1.3 内置槽函数的调用实例1.4 类的继承关系对信号查找的影响1.5 Qt 5 语法与泛型检查二、 自定义槽函数的实现2.1 声明与实现2.2 UI 设计器中的自动连接三、 自定义信号的机制3.1 信号的…

作者头像 李华
网站建设 2026/3/19 20:39:48

YOLOv11 改进 - C2PSA | C2PSA融合Mask Attention掩码注意力,可学习掩码矩阵破解低分辨率特征提取难题 | 2025 预印

前言 本文提出了用于低分辨率图像分割的MaskAttn - UNet框架,并将其核心的掩码注意力机制集成到YOLOv11中。传统U - Net类模型难以捕捉全局关联,Transformer类模型计算量大,而掩码注意力机制通过可学习的掩码,让模型选择性关注重要区域,融合了卷积的局部效率和注意力的全…

作者头像 李华
网站建设 2026/3/16 3:46:37

I2C读写时序基础:一文说清起始与停止条件

I2C起始与停止条件详解&#xff1a;从时序到实战的完整解析在嵌入式开发的世界里&#xff0c;I2C&#xff08;Inter-Integrated Circuit&#xff09;总线就像一条“双线高速公路”&#xff0c;连接着主控芯片和各种传感器、存储器、电源管理模块。它只需要两根线——SDA&#x…

作者头像 李华
网站建设 2026/3/16 3:46:37

【课程设计/毕业设计】基于SpringBoot非物质文化网站系统基于springboot的非物质文化遗产再创新系统设计与实现【附源码、数据库、万字文档】

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/3/16 3:46:32

Java毕设项目:基于springboot的非物质文化遗产再创新系统设计与实现(源码+文档,讲解、调试运行,定制等)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华