linux 软中断入门

news2024/12/23 22:51:56

在 linux 中,任务执行的载体有很多,包括线程,中断,软中断,tasklet,定时器等。但是从本质上来划分的话,任务执行的载体只有两个:线程和中断。软中断和 tasklet 的执行可能在中断中,也可能在线程中,定时器的执行可能在中断、软中断或者线程中。

在讨论中断和软中断的时候,经常以网卡收包为例子来理解。如下是网卡收包的主要环节,分别说明每个环节:

① 网卡从链路上收包,网卡的作用是成帧,网卡在链路上收到的是字节流,根据前导码,帧开始界定符,帧间隙来界定一个帧,这就是成帧。

② 网卡界定一帧报文之后,将这一帧报文通过 dma 保存到内存中。

③ 网卡触发中断,收包中断有对应的处理程序,对于现在的大多网卡来说,在中断处理程序中做的事情是触发收包软中断。

④ 收包的主要工作是在软中断中处理,而不是在硬中断中处理。

中断是硬件和软件进行通信的一种方式。中断具有异步,响应快的特点。在 linux 中,中断可以打断当前正在运行的程序,并且在处理一个中断的时候,往往需要关闭本地中断。

在网络收包的过程中,从网卡驱动,到链路层代码,再到 ip 层,tcp 层,报文的处理过程比较长,所需要花费的时间往往是比较长的。如果在中断处理程序中把收包的工作都做了,那么很可能会导致本地关中断时间太长,进而影响系统的响应。所以在网卡收包时,往往采用中断和软中断结合的方式,在中断处理程序中做的工作比较简单,只是触发一个软中断,后边的处理过程在软中断中进行。中断处理程序中触发软中断之后立即返回,此时,就可以打开本地中断了。

软中断处理函数是 __do_softirq(),从这个函数中也可以看出来,在处理软中断之前调用函数 local_irq_enable(),也就说在处理软中断的时候,是开中断的。

asmlinkage __visible void __softirq_entry __do_softirq(void)
{
    ...

    local_irq_enable();

    h = softirq_vec;

    while ((softirq_bit = ffs(pending))) {
        unsigned int vec_nr;
        int prev_count;

        h += softirq_bit - 1;

        vec_nr = h - softirq_vec;
        prev_count = preempt_count();

        kstat_incr_softirqs_this_cpu(vec_nr);

        trace_softirq_entry(vec_nr);
        h->action(h);
        trace_softirq_exit(vec_nr);
        if (unlikely(prev_count != preempt_count())) {
            pr_err("huh, entered softirq %u %s %p with preempt_count %08x, exited with %08x?\n",
                   vec_nr, softirq_to_name[vec_nr], h->action,
                   prev_count, preempt_count());
            preempt_count_set(prev_count);
        }
        h++;
        pending >>= softirq_bit;
    }

    if (__this_cpu_read(ksoftirqd) == current)
        rcu_softirq_qs();
    local_irq_disable();
    ...
}

1 软中断种类

如下是软中断的种类,当前定义了 10 种软中断,其中 NET_TX_SOFTIRQ 和 NET_RX_SOFTIRQ  用于网卡发包和收包。从定义可以看出来,软中断的种类是静态的,确定的,不能动态改变。从注释可以看出来,不到万不得已,不要新增软中断,大部分情况下使用 tasklet 已经足够了。

/* PLEASE, avoid to allocate new softirqs, if you need not _really_ high
   frequency threaded job scheduling. For almost all the purposes
   tasklets are more than enough. F.e. all serial device BHs et
   al. should be converted to tasklets, not to softirqs.
 */

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
};

 

2 开启软中断和触发软中断

以网卡软中断为例来说明。

通过函数 open_softirq() 来打开软中断,也叫注册这个软中断,在内核初始化的时候会调用函数 net_dev_init() 完成网络相关的初始化,在该函数中打开了网络收发包软中断。

static int __init net_dev_init(void)
{
    ...

	open_softirq(NET_TX_SOFTIRQ, net_tx_action);
	open_softirq(NET_RX_SOFTIRQ, net_rx_action);
    ...
}

软中断使用一个数组来维护,数组是 softirq_vec,数组的下标是软中断的中断号,数组的元素struct softirq_action 类型,这个结构体中只有一个成员,就是软中断的处理函数。网卡发包和收包软中断的处理函数分别是 net_tx_action 和 net_rx_action。

struct softirq_action
{
	void	(*action)(struct softirq_action *);
};

static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp;

在网卡中断处理程序中,一般会调用函数 __raise_softirq_irqoff() 触发软中断。触发软中断就是在一个全局的变量中设置一个标志,标志有软中断了。在 linux 中,这个全局变量是 per cpu 的。

void __raise_softirq_irqoff(unsigned int nr)
{
    lockdep_assert_irqs_disabled();
    trace_softirq_raise(nr);
    or_softirq_pending(1UL << nr);
}

3 处理软中断

3.1 中断返回时

中断返回时,最终会调用到函数 __irq_exit_rcu(),在这个函数中会进行判断,如果当前不是处于中断上下文并且有软中断需要处理,那么就会调用 invoke_softirq() 来处理软中断。

static inline void __irq_exit_rcu(void)
{
#ifndef __ARCH_IRQ_EXIT_IRQS_DISABLED
    local_irq_disable();
#else
    lockdep_assert_irqs_disabled();
#endif
    account_irq_exit_time(current);
    preempt_count_sub(HARDIRQ_OFFSET);
    if (!in_interrupt() && local_softirq_pending())
        invoke_softirq();

    tick_irq_exit();
}

在函数 invoke_softirq() 中进行判断,如果软中断处理线程当前正在运行,那么直接返回。如果 force_irqthreads 是 false 的话,那么就调用 __do_softirq() 或者 do_softirq_onwn_stack() 处理软中断;如果强制让软中断处理线程来处理软中断,那么意思是在中断返回的时候不处理软中断,会将线程唤醒。

static inline void invoke_softirq(void)
{
    if (ksoftirqd_running(local_softirq_pending()))
        return; 

    if (!force_irqthreads) {
#ifdef CONFIG_HAVE_IRQ_EXIT_ON_IRQ_STACK
        /*
         * We can safely execute softirq on the current stack if
         * it is the irq stack, because it should be near empty
         * at this stage.
         */
        __do_softirq();
#else
        /*
         * Otherwise, irq_exit() is called on the task stack that can
         * be potentially deep already. So call softirq in its own stack
         * to prevent from any overrun.
         */
        do_softirq_own_stack();
#endif
    } else {
        wakeup_softirqd();
    }
}

3.2 ksoftirqd

除了在中断返回的时候处理软中断,还有专门的线程来处理。在内核中,每个 cpu 核上都会创建一个 ksoftirqd 线程用来处理这个核上的软中断。

ksoftirqd 的信息维护在 softirq_threads 中。

static struct smp_hotplug_thread softirq_threads = {
    .store            = &ksoftirqd,
    .thread_should_run    = ksoftirqd_should_run,
    .thread_fn        = run_ksoftirqd,
    .thread_comm        = "ksoftirqd/%u",
};

 

在 ksoftirqd 中,最终也是调用函数 __do_softirq() 来处理软中断。

static void run_ksoftirqd(unsigned int cpu)
{
    local_irq_disable();
    if (local_softirq_pending()) {
        /*
         * We can safely run softirq on inline stack, as we are not deep
         * in the task stack here.
         */
        __do_softirq();
        local_irq_enable();
        cond_resched();
        return;
    }
    local_irq_enable();
}

什么时候在中断返回时处理软中断 ?

中断返回时会进行判断,如果当前不是处于中断上下文,并且有软中断需要处理,并且这个时候 ksofrirqd 没有在运行,那么这个时候就会处理软中断。

什么时候在 ksoftirqd 中处理软中断 ?

① 中断返回的时候, 判断 force_irqthreads 为 true,那么只能在 ksoftirqd 中处理软中断,这个时候会通过 wakeup_softirqd() 来唤醒 ksoftirqd。

② 使用 raise_softirq_irqoff() 触发软中断时,如果当前不是处于中断上下文,也会唤醒 ksoftirqd

inline void raise_softirq_irqoff(unsigned int nr)
{
    __raise_softirq_irqoff(nr);

    /*
     * If we're in an interrupt or softirq, we're done
     * (this also catches softirq-disabled code). We will
     * actually run the softirq once we return from
     * the irq or softirq.
     *
     * Otherwise we wake up ksoftirqd to make sure we
     * schedule the softirq soon.
     */
    if (!in_interrupt())
        wakeup_softirqd();
}

4 软中断处理时为什么不能睡眠

软中断的处理,可能在中断返回的时候,也可能在 ksoftirq 线程中。在中断返回的时候,虽然通过 in_interrupt() 判断,当前不是处于中断上下文,但是这个时候中断处理程序还没有真正返回,还不是进程上下文,所以此时也是不能睡眠。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1559833.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

云服务器8核32G配置报价大全,腾讯云、阿里云和京东云

8核32G云服务器租用优惠价格表&#xff0c;云服务器吧yunfuwuqiba.com整理阿里云8核32G服务器、腾讯云8核32G和京东云8C32G云主机配置报价&#xff0c;腾讯云和京东云是轻量应用服务器&#xff0c;阿里云是云服务器ECS&#xff1a; 阿里云8核32G服务器 阿里云8核32G服务器价格…

9.动态规划——2.最大序列和

例题——最大序列和 找状态 思路一&#xff08;&#xff09; 定义一个状态 d p [ i ] dp[i] dp[i]&#xff0c;计为前i个数构成子序列和的最大值 此法状态转移比较困难&#xff0c;即若 d p [ i ] dp[i] dp[i]与 d p [ i − 1 ] dp[i-1] dp[i−1]没有明确的关系&#xff0c;有…

获取电商数据的几种方法分享

在数字化时代&#xff0c;电商数据已经成为企业决策的重要依据。无论是市场趋势的洞察、用户行为的分析&#xff0c;还是产品优化和营销策略的制定&#xff0c;都离不开电商数据的支持。本文将分享几种获取电商数据的有效方法&#xff0c;力求在干货满满的同时&#xff0c;也不…

PyCharm中出现Microsoft Defender配置建议

原因 Windows安全中心的病毒和威胁防护会自动扫描电脑中的文件夹&#xff0c;我们的项目文件夹和IDE文件夹也会被扫描&#xff0c;而PyCharm认为这会降低IDE性能。 解决方法 直接点击提示框里的自动。 或是手动给扫描添加排除项&#xff0c;步骤如下&#xff1a; 1、先打开…

Sui原生功能如何改变链上游戏体验

从zkLogin到可编程交易区块&#xff08;PTB&#xff09;&#xff0c;Sui的原生功能为游戏开发人员提供了工具&#xff0c;最终利用了Web3的力量&#xff0c;给玩家带来了新的体验和参与度。之前的区块链在支持链上游戏方面存在技术上的局限&#xff0c;但是Sui提供了开发人员所…

受益于边缘计算的三个关键应用

边缘计算和 5G 网络正在改变物联网&#xff0c;增强跨多个领域的广泛应用的功能&#xff0c;并催生大量新兴应用。我们通过研究三个突出的用例来说明边缘计算的强大功能。 工业4.0智能工厂 工业 4.0 为制造商提供了基于灵活的工业环境提高生产力和盈利能力的愿景&#xff0c;…

AR-Net网络(图像篡改检测)

AR-Net网络 摘要AbstractAR-Net1. 文献摘要2. 研究背景3. 创新点4. AR-Net 网络架构5. 实验6. 结论总结 摘要 AR-Net使用自适应注意力机制来融合位置和通道维度的特征&#xff0c;使网络能够充分利用不同维度的被篡改特征&#xff0c;此外&#xff0c;AR-Net 改进了预测掩模&a…

牛客NC92 最长公共子序列(二)【中等 动态规划 Java,Go,PHP】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/6d29638c85bb4ffd80c020fe244baf11 思路 https://blog.csdn.net/qq_36544411/article/details/120021203 思路 动态规划法&#xff0c; 我们以dp[i][j]表示在s1中以第i个元素结尾&#xff0c;s2中以第j个元素结…

【JavaSE】初识线程,线程与进程的区别

文章目录 ✍线程是什么&#xff1f;✍线程和进程的区别✍线程的创建1.继承 Thread 类2.实现Runnable接口3.匿名内部类4.匿名内部类创建 Runnable ⼦类对象5.lambda 表达式创建 Runnable ⼦类对象 ✍线程是什么&#xff1f; ⼀个线程就是⼀个 “执行流”. 每个线程之间都可以按…

常见微服务的组件?

注册中心&#xff1a;就是一个服务注册的地方&#xff0c;我们可以把拆分的服务注册到注册中心&#xff0c;这样注册中心就能管理这些服务&#xff0c;服务之间的调用就会很方便&#xff0c;通过服务名就能相互调用。 负载均衡&#xff1a;被调用放的负载均衡&#xff0c;比如…

人工智能产业应用--具身智能

五、下一个浪潮 (一) 跳出缸中脑——虚实结合 在探索人工智能的边界时&#xff0c;“跳出缸中脑——虚实结合”这一概念提出了一个引人深思的视角&#xff0c;尤其是在具身智能的领域。具身智能是一种思想&#xff0c;强调智能体通过与其环境的直接物理互动来实现智能行为。然…

[MSSQL]理解SQL Server AlwaysOn AG的备份

AG提供了以下几种备份策略 下面来看看各项的解释 Prefer Secondary(首选辅助副本) 应在辅助副本上执行此可用性组的自动备份。如果没有可用的辅助副本,将在主副本上执行备份。 这个选项只是概念上的选项。基本上,用户可以从任何复制节点上执行备份命令。 我们可以在主副本…

Django DRF视图

文章目录 一、DRF类视图介绍APIViewGenericAPIView类ViewSet类ModelViewSet类重写方法 二、Request与ResponseRequestResponse 参考 一、DRF类视图介绍 在DRF框架中提供了众多的通用视图基类与扩展类&#xff0c;以简化视图的编写。 • View&#xff1a;Django默认的视图基类&…

数据结构堆

前言&#xff1a; 在前面我们已经学习了数据结构的基础操作&#xff1a;顺序表和链表及其相关内容&#xff0c;今天我们来学一点有些难度的知识——数据结构中的二叉树&#xff0c;今天我们先来学习二叉树中堆的知识&#xff0c;这部分内容还是非常有意思的&#xff0c;下面我们…

设计模式学习笔记 - 设计模式与范式 -行为型:2.观察者模式(下):实现一个异步非阻塞的EventBus框架

概述 《1.观察者模式&#xff08;上&#xff09;》我们学习了观察者模式的原理、实现、应用场景&#xff0c;重点节介绍了不同应用场景下&#xff0c;几种不同的实现方式&#xff0c;包括&#xff1a;同步阻塞、异步非阻塞、进程内、进程间的实现方式。 同步阻塞最经典的实现…

【 书生·浦语大模型实战营】学习笔记(一):全链路开源体系介绍

&#x1f389;AI学习星球推荐&#xff1a; GoAI的学习社区 知识星球是一个致力于提供《机器学习 | 深度学习 | CV | NLP | 大模型 | 多模态 | AIGC 》各个最新AI方向综述、论文等成体系的学习资料&#xff0c;配有全面而有深度的专栏内容&#xff0c;包括不限于 前沿论文解读、…

【Linux】TCP网络套接字编程+守护进程

文章目录 日志类&#xff08;完成TCP/UDP套接字常见连接过程中的日志打印&#xff09;单进程版本的服务器客户端通信多进程版本和多线程版本守护进程化的多线程服务器 日志类&#xff08;完成TCP/UDP套接字常见连接过程中的日志打印&#xff09; 为了让我们的代码更规范化&…

鸿蒙实战开发-如何使用声明式UI编程框架的基础组件

介绍 在本教程中&#xff0c;我们将通过一个简单的样例&#xff0c;学习如何使用声明式UI编程框架的基础组件。本篇Codelab将会使用Image组件、Slider组件、Text组件共同实现一个可调节的风车动画&#xff0c;实现效果如图所示 相关概念 Text组件&#xff1a;文本组件&#x…

9.Python类与对象

1 面向对象 类和对象都是面向对象中的重要概念。面向对象是一种编程思想&#xff0c; 即按照真实世界的思维方式构建软件系统。 例如&#xff0c;在真实世界的校园里有学生和老师&#xff0c;学生有学号、姓名、所 在班级等属性&#xff08;数据&#xff09;&#xff0c;还有…

MAC的Safari浏览器没有声音解决办法

有一段时间没打开电脑&#xff0c;也不知道是系统自动更新或是什么缘故&#xff0c;所有浏览器都无法正常发声。 现象如下&#xff1a; 首先&#xff0c;Safari浏览器无法自动播放声音&#xff0c;下载的360浏览器现象一致&#xff0c;但是播放其他音乐播放软件和视频软件都正…