操作系统(三)中断----软中断

news2024/9/20 14:26:35

软中断与硬中断很像

软中断是纯软件实现的,宏观效果看上去和中断差不多的一种方式。
什么叫宏观效果呢?意思就是说,中断在宏观层面看来,就是打断当前正在运行的程序,转而去执行中断处理程序,执行完之后再返回原程序
从这个层面看,硬中断可以达到这个效果,软中断也可以达到这个效果,所以说宏观效果一样。
那微观层面呢?就是我们需要了解的原理啦。
硬中断的微观层面,就是 CPU 在每一个指令周期的最后,都会留一个 CPU 周期去查看是否有中断,如果有,就把中断号取出,去中断向量表中寻找中断处理程序,然后跳过去。
这个在上面那篇文章里讲的很清楚啦。
软中断的微观层面,简单说就是有一个单独的守护进程,不断轮询一组标志位,如果哪个标志位有值了,那去这个标志位对应的软中断向量表数组的相应位置,找到软中断处理函数,然后跳过去。
你看,微观层面其实也和硬中断差不多。
接下来我们具体说来看看,以 Linux-2.6.0 内核为例,扒开它的外套。

开启内核软中断处理的守护进程

不想之前有断档,我们直接从开机开始讲起。
知识都是想通的,学了从不会浪费,如果你对开机启动流程有很直观的了解,那这块那就完全可以自己跟源码知道内核软中断处理线程是怎么从零到一开始的了。

这个是我之前在讲解自制操作系统时的图,放在这里完全没有问题,这就是 Linux 的启动过程,文件名都一样。
唯一不同的是,我们这里内核主方法叫 kernel_start,Linux-2.6.0 里叫 start_kernel,我也懒得改了。
接下来看这个入口方法。

asmlinkage void __init start_kernel(void) {
    ...
        trap_init();
    sched_init();
    time_init();
    ...
        rest_init();
}

省略了很多部分,但可以看出这个方法里就是各种初始化
接着看 rest_init() 这个方法。

static void rest_init(void) {
    kernel_thread(init, NULL, CLONE_KERNEL);
} 

static int init(void * unused) {
    do_pre_smp_initcalls();
}

static void do_pre_smp_initcalls(void) {
    spawn_ksoftirqd();
}

看到一个 spawn_ksoftirqd(),翻译过来就是 spawn kernel soft irt daemon,开启内核软中断守护进程,这名字太直观了,都不用我讲了!
再往里跟。很长,但有用的信息很少。

__init int spawn_ksoftirqd(void) {
    cpu_callback(&cpu_nfb, CPU_ONLINE, (void *)(long)smp_processor_id());
    register_cpu_notifier(&cpu_nfb);
    return 0;
}

static int __devinit cpu_callback(...) {
    kernel_thread(ksoftirqd, hcpu, CLONE_KERNEL);
}

static int ksoftirqd(void * __bind_cpu) {
    for (;;) {
        while (local_softirq_pending()) {
            do_softirq();
            cond_resched();
        }
    }
}

asmlinkage void do_softirq(void) {
    h = softirq_vec;
    pending = local_softirq_pending();
    do {
        if (pending & 1) {
            h->action(h);
            h++;
            pending >>= 1;
        } while (pending);
    }

前面的不用管,直接看最后一个方法,do_softirq(),这个方法展示了软中断处理守护进程所做的事情的精髓,我给翻译一下。

// 这就是软中断处理函数表(软中断向量表)
// 和硬中断的中断向量表一样
static struct softirq_action softirq_vec[32];

asmlinkage void do_softirq(void) {
    // h = 软中断向量表起始地址指针
    h = softirq_vec;
    // 这个是软中断标志位们,一次性拿到所有的软中断标志位
    pending = local_softirq_pending();
    do {
        // 此时的软中断标志位有值(说明有软中断)
        if (pending & 1) {
            // 去对应的软中断向量表执行对应的处理函数
            h->action(h);
            // 软中断向量表指针向后移动
            h++;
            // 同时软中断处理标志位也向后移动
            pending >>= 1;
        } while (pending);
    }

这翻译还没看明白,那我来几个图你就懂了。
首先 h 代表软中断向量表 softirq_vec,和硬中断的中断向量表的存在是一个目的,就是个数组嘛,然后里面的元素存储着软中断处理程序的地址指针,在 action 中。

然后 pending 代表
软中断标志位(们)

这里完全由于 Linux 里用了好多 C 语言的宏定义搞得很绕,我先放出来,别担心。

typedef struct {
    unsigned int __softirq_pending;
    unsigned long idle_timestamp;
    unsigned int __nmi_count;   /* arch dependent */
    unsigned int apic_timer_irqs;   /* arch dependent */
} irq_cpustat_t;

extern irq_cpustat_t irq_stat[];    /* defined in asm/hardirq.h */
#define __IRQ_STAT(cpu, member) (irq_stat[cpu].member)
#define __IRQ_STAT(cpu, member) ((void)(cpu), irq_stat[0].member)
#define softirq_pending(cpu)  __IRQ_STAT((cpu), __softirq_pending)
#define local_softirq_pending() softirq_pending(smp_processor_id())

pending = local_softirq_pending();

把这些宏定义都翻译过来,再去掉多处理器的逻辑,就当只有一个核心,就变得很简单了。

pending = irq_stat[0].__softirq_pending;

它就是个 **int **值而已,32 位。
回过头看之前的,**pending(软中断标志位)**与 **h(软中断向量表)**的向后移动的步长。

// 软中断向量表指针向后移动
h++;
// 同时软中断处理标志位也向后移动
pending >>= 1;

可以看出软中断标志位的一位对应着软中断向量表中的一个元素,这就不难理解为什么中断向量表这个数组大小是 32 位了。

好了,这样这个内核软中断处理这个守护进程做的事,就完全搞懂了。
就是不断遍历 pending 这个软中断标志位的每一位,如果是 0 就忽略,如果是 1,那从上面的 h 软中断向量表中找到对应的元素,然后执行 action 方法,action 就对应着不同的软中断处理函数

而且也能看到,内核软中断处理守护进程,在 Linux 启动后,会自动跑起来,那也就代表了,软中断机制生效了。
如果让你使用这个内核功能,做软中断的事情,那不难想象,很简单。
第一步,注册软中断向量表,其实就是把软中断向量表的每个 action 变量赋值,相当于硬中断中注册中断向量表的过程。
第二步,触发一个软中断,其实就是修改 pending 的某个标志位,触发一次软中断,相当于硬中断中由外部硬件、异常、或者 INT 指令来触发硬中断一样。
而实际上,Linux 就是这样做的,和我们猜的一样,我们一步步看。

注册软中断向量表

就是给 softirq_vec 这个软中断向量表,也是一个数组,里面的每一个元素的 action 附上值,赋的就是软中断处理函数的函数地址。
这代码很容易就可以想到,太好写了,就这样呗。

softirq_vec[0].action = NULL;
softirq_vec[1].action = run_timer_softirq;
softirq_vec[2].action = net_tx_action;
...
    softirq_vec[31].action = xxx;

没错,就是这样,不要以为 Linux 有啥神奇的操作,也是得这样老老实实给他们赋值。
比如,网络子系统的初始化,有一步就需要注册网络的软中断处理函数

subsys_initcall(net_dev_init);

static int __init net_dev_init(void) {
    ...
        // 网络发包的处理函数
        open_softirq(NET_TX_SOFTIRQ, net_tx_action, NULL);
    // 网络收包的处理函数
    open_softirq(NET_RX_SOFTIRQ, net_rx_action, NULL);
    ...
    }

void open_softirq(int nr, void (*action)(struct softirq_action*), void *data)
{
    softirq_vec[nr].data = data;
    // 简直完全一样
    softirq_vec[nr].action = action;
}

这和我们写的不能说是相似,简直完全是一样呀,只是多包装了一层函数叫 **open_softirq **方便调用罢了。
**NET_TX_SOFTIRQ **这些是枚举值,具体看这些枚举也会发现 Linux-2.6.0 中也不多。

enum
{
    HI_SOFTIRQ=0,
    TIMER_SOFTIRQ,
    NET_TX_SOFTIRQ,
    NET_RX_SOFTIRQ,
    SCSI_SOFTIRQ,
    TASKLET_SOFTIRQ
    };

好奇翻了下 Linux-5.11,发现也不多

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

触发一次软中断

同上,这代码也很容易就可以想到,就这样呗。
你看,表示软中断标志位的 p 不是这样取值的么。

pending = local_softirq_pending();

取出来的是个 32 位的 int 值。
那只需要**把 local_softirq_pending() 对应的标志位改成 1 **就触发了软中断了,比如我们想触发一个 2 号软中断,就像这样。

代码这么写就行了。

local_softirq_pending() |= 1UL << 2;

而 Linux 居然也是这么做的,我们看网络数据包到来之后,有一段代码。

#define __raise_softirq_irqoff(nr) \
do { local_softirq_pending() |= 1UL << (nr); } while (0)

static inline void __netif_rx_schedule(struct net_device *dev) {
    list_add_tail(&dev->poll_list, &__get_cpu_var(softnet_data).poll_list);
    // 发出软中断
    __raise_softirq_irqoff(NET_RX_SOFTIRQ);
}

如果把 do while(0) 这种 C 语言宏定义的一种玩法去掉,其实就和我们的完全一样了,这回可真的是完全一样。

static inline void __netif_rx_schedule(struct net_device *dev) {
    list_add_tail(&dev->poll_list, &__get_cpu_var(softnet_data).poll_list);
    // 发出软中断
    local_softirq_pending() |= 1UL << (NET_RX_SOFTIRQ)
    }

所以我之前总是说,当你真的去接触这个东西的时候,一个个细节逐步拨开后,会发现一点也不难,而且都是顺理成章,和我们猜测的也一样。

总结

软中断没什么神奇的骚操作,就是一组一位一位的软中断标志位,对应着软中断向量表中一个一个的中断处理函数,然后有个内核守护进程不断去循环判断调用,而已。
然后,由各个子系统调用 **open_softirq **负责把软中断向量表附上值。
再由各个需要触发软中断的地方调用 **raise_softirq_irqoff **修改中断标志位的值。
后面的工作就交给内核那个软中断守护进程,去触发这个软中断了,其实就是个遍历并查找对应函数的简单过程。


记住上面这张图,就可以了。

好了,上篇文章的硬中断,和本篇文章的软中断,它们最基本的原理,和他们的异同点,你整明白了么?
软中断是 Linux 处理一个中断的下半部的主要方式,比如 Linux 某网卡接收了一个数据包,此时会触发一个硬中断,由于处理数据包的过程比较耗时,而硬中断资源又非常宝贵,如果占着硬中断函数不返回,会影响到其他硬中断的相应速度,比如点击鼠标、按下键盘等。
所以一般 Linux 会把中断分成上下两半部分执行,上半部分处理最简单的逻辑,下半部分直接丢给一个软中断异步处理。
比如网卡收到了一个数据包,假如这个网卡型号是 e1000,那对应的硬中断处理函数是,e1000_intr,我们看看它做了什么事情。

static irqreturn_t e1000_intr(int irq, void *data, struct pt_regs *regs) {
    __netif_rx_schedule(netdev);
}

static inline void __netif_rx_schedule(struct net_device *dev) {
    list_add_tail(&dev->poll_list, &__get_cpu_var(softnet_data).poll_list);
    __raise_softirq_irqoff(NET_RX_SOFTIRQ);
}

看到没,后面直接 __raise_softirq_irqoff 丢给软中断就不管了。

转自:https://mp.weixin.qq.com/s/g9rGKRQofAlWjdq8lDTTkQ

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

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

相关文章

【Python机器学习】使用Matplotlib注解绘制树形图

通过数据集可以创建树&#xff0c;但是字典的表示形式非常不易于理解&#xff0c;而且直接绘制图形也比较困难。但是通过Matplotlib库可以绘制树形图。 决策树的主要优点就是直观、易于理解&#xff0c;如果不能将其直观的显示出来&#xff0c;就无法发挥其优势。 Matplotlib…

y=λsin(πx)分岔的研究

使用如下的迭代格式&#xff0c;λ为可变的参数 用如下代码对收敛的λ的值进行探究&#xff0c;这里的r代表λ %通过观察是否凝聚在同一个点来判断是否收敛 clear;clf; axis([0,4,0,4]); grid; hold on for r0:0.3:3.9x[0.1];for i2:150x(i)r*sin(pi*x(i-1));endpause(0.5);fo…

心动小站Ⅶ--人工智能的虚假承诺

前言 1770 年&#xff0c;匈牙利作家兼发明家 Wolfgang von Kempelen 推出了一款名为“土耳其机器人”的自动国际象棋机器。该机器在欧洲各地展示了其自动化国际象棋大师技能&#xff0c;在与人类对手的比赛中频频获胜。据说它甚至击败了拿破仑和本杰明富兰克林等著名人物。土…

Prometheus之数据类型和函数

前言&#xff1a; 在了解Prometheus数据类型前&#xff0c;我们先了解下面几个统计学名词概念&#xff1a; 平均数&#xff08;Mean&#xff09;&#xff1a; 平均数是所有数据加起来除以数据个数得到的结果。它表示数据的中心趋势。 最大值&#xff08;Maximum&#xff09…

Hadoop集群安装配置

文章目录 Hadoop部署配置集群配置历史服务器配置日志的聚集分发Hadoop群起集群Hadoop群起脚本 准备工作&#xff1a;需要3台虚拟机&#xff0c;每台虚拟机搭建好JDK并配置环境变量 Hadoop部署 1&#xff09;集群部署规划 注意&#xff1a;NameNode和SecondaryNameNode不要安…

Vue中el的两种写法

大家好我是前端寄术区博主PleaSure乐事。今天了解到了Vue当中有关el的两种写法&#xff0c;记录下来与大家分享&#xff0c;希望对大家有所帮助。 方法一 解释 第一种方法我们直接用new创建并初始化一个新的 Vue 实例&#xff0c;并定义了 Vue 实例的数据对象&#xff0c;在给…

数组算法--二分查找

目录 一.前言 二.算法的核心思路 三.算法的核心代码以注释详解 一.前言 二分查找也叫折中查找&#xff0c;为什么会这样叫呢&#xff1f;就是因为我们二分查找的核心逻辑就是每查找完一次&#xff0c;都能将查找的范围给缩小一半&#xff0c;也就是折中。但使用二分查找又有个…

宏VB的1004问题,方法作用于对象错误,如何解决?

&#x1f3c6;本文收录于《CSDN问答解惑-专业版》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收…

RV1126 Linux 系统,接外设,时好时坏(二)排查问题的常用命令

在 RV1126 Linux 系统中,排查外设连接问题时,可以使用多种命令来诊断和调试。以下是一些常用的命令和工具: 1. 查看系统日志 dmesg: 显示内核环形缓冲区的消息,通常包含设备初始化、驱动加载和错误等信息。 dmesg | grep <设备名或相关关键字>journalctl: 查看系统…

windows 下删除一个文件夹及其子文件夹下相同后缀名的文件

问题 我有一个工作目录&#xff0c;沉积了四五年的工作 文件。其中有一个相同格式的中间过程文件暂用很大体积&#xff0c;也不需要保留&#xff0c;并且可以通过其他文件生成。因此想一次删除这个工作目录下的所有相同后缀的文件。 解决方法 在工作目录的地址栏输入“cmd”…

leaflet【九】使用天地图改变地图底色

本文将详细探讨如何在Leaflet地图框架中集成天地图&#xff0c;并介绍如何通过调整背景色和滤镜来改变地图的显示效果。首先&#xff0c;我们将解释如何在Leaflet中配置天地图作为底图&#xff0c;包括API密钥的获取与使用。接下来&#xff0c;文章将展示如何通过CSS和JavaScri…

高等数学 第六讲 一元微分学的应用(二)_中值定理,微分等式,微分不等式

高等数学 第6讲 中值定理 微分等式 微分不等式 文章目录 高等数学 第6讲 中值定理 微分等式 微分不等式1.涉及函数的中值定理1.1 有界与最值定理1.2 介值定理1.3 平均值定理1.4 零点定理 2.涉及导数(微分)的中值定理2.1 导数零点定理2.2 罗尔定理2.3 拉格朗日中值定理2.4 柯西中…

如何利用Jenkins自动化管理、部署数百个应用

目录 1. Jenkins 安装与部署步骤 1.1 系统要求 1.2 安装步骤 1.2.1 Windows 系统 1.2.2 CentOS 系统 1.3 初次配置 2. Gradle 详细配置方式 2.1 安装 Gradle 2.1.1 Windows 系统 2.1.2 CentOS 系统 2.2 配置 Jenkins 中的 Gradle 3. JDK 详细配置方式 3.1 安装 JD…

【MSP430】DriverLib库函数,UCS函数分析

MSP430F5xx_6xx_DriverLib_Users_Guide-2_91_13_01(函数库手册).pdf 在MSP430单片机中&#xff0c;UCS&#xff08;User Clock System&#xff09;模块提供了一组函数用于配置和管理时钟源&#xff0c;包括外部和内部振荡器以及时钟信号的路由和控制。这些函数对于确保系统在正…

layui改造优化ITtools技术笔记01—layui.js重要修正

问题现象&#xff1a; ittools教学平台自动生成的单选按钮渲染后无法切换选项。 故障排查&#xff1a; input[name xxx]&#xff0c;其中xxx含有特殊字符&#xff0c;如$等&#xff0c;导致layui渲染时&#xff0c;表达式出错&#xff0c;无法及时渲染。 解决方案&#xff1…

IO流综合练习

IO流综合练习 文章目录 IO流综合练习制造假数据需求利用糊涂包制造假数据&#xff0c;并写入文件中 随机点名器Student标准JavaBean类实现代码names.txt文件中的内容 登录 制造假数据 需求 制造假数据也是开发中的一个能力&#xff0c;在各个网上爬取数据&#xff0c;是其中一…

js动画插件-vue

分享一个动画插件 学习 动画插件 是进入大厂的必备技能 首先我们需要先学会 去使用js 动画 封装好的 GreenSock 动画平台 &#xff08;GSAP&#xff09; greensock.com/gsap/ 就是这个插件 我现在分享一个用例 其实很简单 但是 具体的属性 和很多 内容需要慢慢使用 慢慢看…

java面试题,有synchronized锁,threadlocal、数据可以设置默认值、把redis中的json转为对象

有面试题&#xff0c;有synchronized锁&#xff0c;threadlocal 一、面试题小记二、加锁synchronized1. 先看代码2. synchronized 讲解2.1. 同步代码块2.2. 同步方法2.3. 锁的选择和影响2.4. 注意事项2.5 锁的操作&#xff0c;手动释放锁&#xff0c;显式地获取锁&#xff08;属…

震惊,刷新我的认知,医疗信息数据库sqlserver中计算年龄的sql函数写了200行...

创作不易 只因热爱!! 热衷分享&#xff0c;一起成长! “你的鼓励就是我努力付出的动力” sqlserver中年龄计算,HIS系统中年龄计算函数 呈现的结果要求: 1周岁内显示"几月几天",1周岁以上显示"几岁" CREATE FUNCTION dbo.FUN_GETBRNL( birth varchar(…

汇昌联信拼多多电商有哪些热门话题?

随着互联网技术的飞速发展&#xff0c;电子商务已经成为人们日常生活的一部分。作为中国领先的电商平台之一&#xff0c;拼多多凭借其独特的商业模式和营销策略迅速崛起&#xff0c;吸引了大量消费者的关注。那么&#xff0c;在汇昌联信拼多多电商领域&#xff0c;有哪些热门话…