首页 > 其他分享 >软中断

软中断

时间:2024-04-19 15:03:10浏览次数:25  
标签:__ softirq 中断 上下文 pending SOFTIRQ

softirq

软中断(softirq)是内核虚拟出的一种异步中断,通过raise_softirq()来触发,可以将一些不紧急的任务推迟执行。在软中断中可以处理中断下半部,比如网卡数据收发的软中断NET_TX_SOFTIRQNET_RX_SOFTIRQ,还可以处理一些需要异步执行的场景,比如定时器软中断TIMER_SOFTIRQsoftirq这种异步处理机制能够避免阻塞高优先级的任务执行(如中断处理程序),保障系统的实时性。

软中断号

// include/linux/interrupt.h
enum
{
    HI_SOFTIRQ=0,
    TIMER_SOFTIRQ,
    NET_TX_SOFTIRQ,
    NET_RX_SOFTIRQ,
    BLOCK_SOFTIRQ,
    IRQ_POLL_SOFTIRQ,
    TASKLET_SOFTIRQ,
    SCHED_SOFTIRQ,
    HRTIMER_SOFTIRQ,
    RCU_SOFTIRQ,    /* Preferable RCU should always be the last softirq */

    NR_SOFTIRQS
};

软中断具有两个特点:

  • 软中断上下文是没有调度实体的,因此softirq在执行过程中无法睡眠。
  • 软中断可以并行(同一个软中断处理函数可以在多核上同时执行),这意味着softirq具有高性能的优势,但是编写一个支持并行的软中断处理函数需要非常小心,因此目前只有网络、定时器等对性能要求高的场景使用软中断。

由于软中断的特点内核中只静态定义了10个软中断,并且不建议增加新的软中断,对于绝大多数场景可以使用不存在并发的tasklet作为替代品。

触发软中断

// kernel/softirq.c
DEFINE_PER_CPU_ALIGNED(irq_cpustat_t, irq_stat);
EXPORT_PER_CPU_SYMBOL(irq_stat);

typedef struct {
    u16	     __softirq_pending;
    ......
} ____cacheline_aligned irq_cpustat_t;

内核定义了一个per-cpu的结构体irq_cpustat_t,这个结构体中存放了很多中断相关的统计信息,以及一个软中断的pending位图__softirq_pending,位图的每一位对应一个软中断号,如果某bit1表示该软中断需要处理。

// kernel/softirq.c
void raise_softirq(unsigned int nr)
{
    unsigned long flags;

    local_irq_save(flags);
    raise_softirq_irqoff(nr);
    local_irq_restore(flags);
}

// 必须在关中断的上下文调用
inline void raise_softirq_irqoff(unsigned int nr)
{
    __raise_softirq_irqoff(nr);
    
    if (!in_interrupt())
        wakeup_softirqd();
}

// 设置位图
void __raise_softirq_irqoff(unsigned int nr)
{
    or_softirq_pending(1UL << nr);
}

raise_softirq()raise_softirq_irqoff()都可以触发一个编号为nr的软中断,只是raise_softirq_irqoff()在关中断的上下文中调用。

__raise_softirq_irqoff()中会设置软中断号对应的pending位,然后会检查当前是否处于中断上下文(硬中断上下文+软中断上下文+中断下半部禁用上下文),如果此时不处于中断上下文,为了软中断能够尽快执行会唤醒ksoftirqd线程参与调度,如果此时已经在中断上下文了,之后从中断上下文返回到进程上下文时会尝试执行软中断。

软中断处理

软中断有两种执行方式:

  • 从中断上下文退出时会尝试处理软中断。这又分为从硬中断上下文和中断下半部禁用状态退出两种情况,此时需要检查是否仍然处于中断状态(可能存在中断嵌套、下半部禁用嵌套或者中断抢占软中断),如果此时即将返回进程上下文则尝试处理软中断。
  • 如果在中断下半部执行时软中断数量过多或者在进程上下文触发一个软中断,此时会唤醒ksoftirqd线程参与调度,执行软中断。

从软中断上下文回到进程上下文时由于刚处理完软中断此时不会再次尝试执行软中断。

从中断下半部禁用回到进程上下文

// include/linux/bottom_half.h
static inline void local_bh_enable(void)
{
    __local_bh_enable_ip(_THIS_IP_, SOFTIRQ_DISABLE_OFFSET);
}

// kernel/softirq.c
void __local_bh_enable_ip(unsigned long ip, unsigned int cnt)
{
    ......
    preempt_count_sub(cnt - 1);
    if (unlikely(!in_interrupt() && local_softirq_pending())) {
        do_softirq();
    }
    preempt_count_dec();
    preempt_check_resched();
}
EXPORT_SYMBOL(__local_bh_enable_ip);

当退出中断下半部禁用临界区时会调用local_bh_enable(),参考preempt_count的位划分,SOFTIRQ_DISABLE_OFFSET0x200,
__local_bh_enable_ip()函数中先将preempt_count减去了SOFTIRQ_DISABLE_OFFSET - 1,这等价于先跳出一层中断下半部禁用,然后禁用了抢占,因为之后还要检查是否有软中断是否需要处理,当没有软中断需要处理或者等到软中断处理结束以后再将抢占计数减一启用抢占。这是因为软中断执行过程中是不允许抢占的,有以下两个原因:

  • 不禁用抢占会出现异常:do_softirq()会先获取本地CPU的pending位图之后再设置软中断标记(可以达到禁用抢占和禁用软中断嵌套的效果),如果在这个过程中发生了抢占,等到下次任务被调度时可能已经不在之前执行的CPU上了,但是此时处理的pending位图是上一个CPU的。
  • 软中断优先级高于进程:do_softirq()有权利在软中断过多时主动放弃CPU,将剩余软中断交给ksoftirqd线程去处理,但是进程没有资格去抢占软中断执行。

从硬中断上下文回到进程上下文

// kernel/softirq.c
void irq_exit(void)
{
    __irq_exit_rcu();
    ......
}

static inline void __irq_exit_rcu(void)
{
    ......
    preempt_count_sub(HARDIRQ_OFFSET);
    if (!in_interrupt() && local_softirq_pending())
        invoke_softirq();
    ......
}

离开硬中断上下文时需要调用irq_exit(),在__irq_exit_rcu()中会将硬中断计数减一,然后判断当前是否不处于中断上下文中(有可能当前硬中断抢占发生在软中断执行过程或者是中断下半部禁用上下文)并且有待处理的软中断,此时调用invoke_softirq()处理。

// kernel/softirq.c
static inline void invoke_softirq(void)
{
    if (ksoftirqd_running(local_softirq_pending()))
        return;

    if (!force_irqthreads) {
#ifdef CONFIG_HAVE_IRQ_EXIT_ON_IRQ_STACK
        __do_softirq();
#else
        do_softirq_own_stack();
#endif
    } else {
        wakeup_softirqd();
    }
}

invoke_softirq()会检查ksoftirqd线程是否已经在运行了,如果已经处于运行状态就交给ksoftirqd去处理软中断。force_irqthreads判断是否强制进行中断线程化,如果有这样的要求则唤醒ksoftirqd线程去处理软中断,否则此时就开始处理软中断。

如果当前Arch下有单独的中断栈,由于此时即将退出最后一层硬中断上下文,栈的空间很充足,可以直接在中断栈上执行软中断。如果当前Arch下没有单独的中断栈,则会在当前进程的内核栈上执行。

软中断执行

从中断下半部禁用和硬中断上下文返回进程上下文时,都会调用__do_softirq()处理软中断。

// kernel/softirq.c
asmlinkage __visible void __softirq_entry __do_softirq(void)
{
    unsigned long end = jiffies + MAX_SOFTIRQ_TIME;
    int max_restart = MAX_SOFTIRQ_RESTART;
    struct softirq_action *h;
    __u32 pending;
    int softirq_bit;

    pending = local_softirq_pending();
    __local_bh_disable_ip(_RET_IP_, SOFTIRQ_OFFSET);
    
restart:
    // 处理阶段
    set_softirq_pending(0);
    local_irq_enable();
    h = softirq_vec;
    while ((softirq_bit = ffs(pending))) {
        h += softirq_bit - 1;
        h->action(h);
        h++;
        pending >>= softirq_bit;
    }
    local_irq_disable();

    // 检查是否重启
    pending = local_softirq_pending();
    if (pending) {
        if (time_before(jiffies, end) && !need_resched() &&
            --max_restart)
            goto restart;

        wakeup_softirqd();
    }
    __local_bh_enable(SOFTIRQ_OFFSET);
}

在处理软中断前会标记当前处于软中断上下文,然后开启中断,允许被中断抢占。处理阶段会遍历每一个在pending中的软中断,执行对应的软中断处理函数。每一轮处理结束时会关闭中断,检查在软中断处理过程中是否触发了新的软中断(硬中断处理函数内部可能会触发软中断),如果有则检查是否满足继续处理下去的条件,如果满足以下三个条件中的任意一个则停止处理,唤醒ksoftirqd线程参与调度。

  • 总的处理时间超过了2msMAX_SOFTIRQ_TIME
  • 设置了抢占标记TIF_NEED_RESCHED
  • 超过了处理10

标签:__,softirq,中断,上下文,pending,SOFTIRQ
From: https://www.cnblogs.com/wodemia/p/18145901

相关文章

  • 汇编语言简易教程(14):中断与恢复
    汇编语言简易教程(14):中断与恢复从一般意义上讲,中断是工作流的暂停或保持。例如,如果您正在打电话,门铃响了,则电话通话将处于暂停状态,门将应答。销售人员被送走后,电话交谈恢复(对话中断的地方)在计算机编程中,中断也是当前正在执行的过程的暂停或保持。通常,当前进程会中断,以便可......
  • STM32外部中断小记
    一、EXTI配置步骤//1.配置RCC时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOx,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//开启AFIO时钟,AFIO:GPIO复用/重映射功能//2.配置EXTIGPIO端口及工作模式(输入模式)//3.配置EXTI中断线、模式(上升沿、下降沿......
  • 第7章 中断系统
    第7章中断系统一、中断概述中断技术是在微机中广泛使用的一种资源共享技术。中断中断是指当CPU在正常执行程序时,由于外部或内部事件强迫CPU停止当前执行的程序,转去为事件服务(中断服务),服务完毕,又能自动返回到被中断的程序中继续执行。中断功能实现CPU与外设的速度匹......
  • FOC算法中为啥用PWM触发ADC中断
    在FOC(FieldOrientedControl,场向量控制)算法中,为什么要使用PWM(PulseWidthModulation,脉宽调制)触发ADC(Analog-to-DigitalConverter,模数转换器)中断呢?在FOC中,PWM被用来控制电机的相电流,以实现精确的控制。通过改变PWM信号的占空比,可以调节电机的转速和转矩。而为了实现精确的控......
  • 计算机怎么实现中断
    中断类型外部中断可屏蔽中断可屏蔽中断是通过INTR引脚进入CPU的,外部设备如硬盘、网卡等发出的中断都是可屏蔽中断可屏蔽的意思是此外部设备发出的的中断,CPU可以不理会,因为它不会导致系统宕机,所有可以通过eflags寄存器的IF位将所有这些外部设备的中断屏蔽不可屏蔽中断......
  • FreeRTOS中断管理以及实验
    FreeRTOS中断管理以及实验继续记录学习FreeRTOS的博客,参照正点原子FreeRTOS的视频。ARMCortex-M使用了8位宽的寄存器来配置中断的优先等级,这个寄存器就是中断优先级配置寄存器,STM32寄存器中并且这个寄存器只使用[7:4],所以具体表达优先级的位数如下图所示:STM32的中断优先......
  • 中断子系统(一)IRQ Domain
    前言在现代计算机系统中,中断模块的硬件越来越复杂,有可能有多个中断控制器(InterruptController,IC)之间进行级联从而拓展可以管理的中断源数量。这就会产生几个问题,每个IC上都连接着多个设备,IC会给irqline连接的每一个设备分配一个硬件中断请求号(HWinterruptnumber,hwirq),不同......
  • Cortex-M7中断向量表的重定向
    1前言    系统上电后,PC会指向复位向量,即向量表中的Reset_Handler,而系统就是通过VectorTableOffsetRegister(VTOR)的值加上4字节来找到复位向量的入口的。        因为地址0处应该存储引导代码,所以它通常映射到Flash或者是ROM器件,并且它们的值不......
  • RTOS--异常向量和中断向量
    目录1异常向量2中断向量3示例说明RTOS(实时操作系统)中的异常和中断向量是操作系统处理异常事件和中断请求的关键机制。这些向量是预定义的内存地址,当特定事件发生时,处理器会跳转到这些地址执行相应的处理程序。下面将详细介绍RTOS中的异常和中断向量,并通过示例来说明它......
  • 【ARM课】3-外部中断
    1.cubemx设置参考文章【STM32】HAL库——按键外部中断时钟设置将HCLK设置为最大频率72MHzGPIO设置按键引脚GPIO模式——下降沿触发上拉NVIC勾选以及如是填写中断抢占优先级,具体原因在“一个老是掉进去的坑”中叙述。LED引脚(同上一篇文章)输出电平——PA8......