Linux 之软中断softirq

2023-11-12

版权声明:本文为博主原创文章,未经博主允许不得转载。
https://blog.csdn.net/huangweiqing80/article/details/83274095
softirq驱动开发人员一般都不会用到,到内核代码中会用到softirq机制,如在timer定时器中有用到softirq机制。。。

下面我们来简单了解一下Linux中的软中断的使用。

  1. 注册软中断处理函数open_softirq
void open_softirq(int nr, void (*action) (struct softirq_action *));
/**
 * @nr: 软中断的索引号
 * @action: 软中断的处理函数
 */
 
void open_softirq(int nr, void (*action) (struct softirq_action *))
{
    softirq_vec[nr].action = action;
}

  1. 触发软中断

调用raise_softirq()来触发软中断。

void raise_softirq(unsigned int nr);
void raise_softirq(unsigned int nr)
{
    unsigned long flags;
    local_irq_save(flags);
    raise_softirq_irqoff(nr);
    local_irq_restore(flags);
}

在软中断示例中只需要使用上面的两个API就可以了。
kernel/kernel/timer.c

/*
 * This function runs timers and the timer-tq in bottom half context.
 */
static void run_timer_softirq(struct softirq_action *h)
{
	struct tvec_base *base = __this_cpu_read(tvec_bases);

	hrtimer_run_pending();

	if (time_after_eq(jiffies, base->timer_jiffies))
		__run_timers(base);
	printk_tick();
	mt_printk_tick();
}
void __init init_timers(void)
{
	int err = timer_cpu_notify(&timers_nb, (unsigned long)CPU_UP_PREPARE,
				(void *)(long)smp_processor_id());

	init_timer_stats();

	BUG_ON(err != NOTIFY_OK);
	register_cpu_notifier(&timers_nb);
	open_softirq(TIMER_SOFTIRQ, run_timer_softirq);   //注册软中断,处理函数为run_timer_softirq,中断号为TIMER_SOFTIRQ
}

void run_local_timers(void)
{
	hrtimer_run_queues();
	raise_softirq(TIMER_SOFTIRQ);                     //触发软中断
}

下面我们来分析为什么我们在软中断的应用中,使用这两个API就可以使用一个软中断了。

(1) 定义
Linux内核软中断是内核已经定义好的了,内核定义了10种软中断。也就是说,软中断是一组静态定义的下半部接口,可以在所有处理器上同时执行,即使两个类型相同也可以。
但一个软中断不会抢占另一个软中断,唯一可以抢占软中断的是硬中断。

软中断由softirq_action结构体表示:

struct softirq_action {
    void (*action) (struct softirq_action *); /* 软中断的处理函数 */
};

目前已注册的软中断有10种,定义为一个全局数组:

static struct softirq_action softirq_vec[NR_SOFTIRQS];
enum {
    HI_SOFTIRQ = 0, /* 优先级高的tasklets */
    TIMER_SOFTIRQ, /* 定时器的下半部 */
    NET_TX_SOFTIRQ, /* 发送网络数据包 */
    NET_RX_SOFTIRQ, /* 接收网络数据包 */
    BLOCK_SOFTIRQ, /* BLOCK装置 */
    BLOCK_IOPOLL_SOFTIRQ,
    TASKLET_SOFTIRQ, /* 正常优先级的tasklets */
    SCHED_SOFTIRQ, /* 调度程序 */
    HRTIMER_SOFTIRQ, /* 高分辨率定时器 */
    RCU_SOFTIRQ, /* RCU锁定 */
    NR_SOFTIRQS /* 10 */
};

(2) 注册软中断处理函数

/**
 * @nr: 软中断的索引号
 * @action: 软中断的处理函数
 */
 
void open_softirq(int nr, void (*action) (struct softirq_action *))
{
    softirq_vec[nr].action = action;
}

可以看出注册软中断其实就是给软中断的处理函数赋值

(3) 触发软中断

void raise_softirq(unsigned int nr)
{
	unsigned long flags;
 
	local_irq_save(flags); //保存中断状态,禁止中断
	raise_softirq_irqoff(nr); //挂起相应的中断类型
	local_irq_restore(flags); //恢复中断。
}
/* This function must run with irqs disabled */
inline void rasie_softirq_irqsoff(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(void); /* 唤醒ksoftirqd/n进程 */
}

先是通过__raise_softirq_irqoff设置cpu的软中断pending标志位(irq_stat[NR_CPUS] ),然后通过in_interrupt判断现在是否在中断上下文中,或者软中断是否被禁止,如果都不成立,则唤醒软中断的守护进程,在守护进程中执行软中断的回调函数。否则什么也不做,软中断将会在中断的退出阶段被执行。

(4)软中断的执行

基于上面所说,软中断的执行既可以守护进程中执行,也可以在硬中断的退出阶段执行。实际上,软中断更多的是在硬中断的退出阶段执行(do_irq --> irq_exit),以便达到更快的响应,加入守护进程机制,只是担心一旦有大量的软中断等待执行,会使得内核过长地留在中断上下文中。

而不管是用什么方法唤起执行,软中断都要在do_softirq()中执行。如果有待处理的软中断,do_softirq()会循环遍历每一个,调用它们的相应的处理程序。在中断处理程序中触发软中断是最常见的形式。中断处理程序执行硬件设备的相关操作,然后触发相应的软中断,最后退出。内核在执行完中断处理程序以后,马上就会调用do_softirq(),于是软中断开始执行中断处理程序完成剩余的任务。

下面来看下do_softirq()的具体实现。

asmlinkage void do_softirq(void)
{
    __u32 pending;
    unsigned long flags;
 
    /* 如果当前已处于硬中断或软中断中,直接返回 */
    if (in_interrupt()) 
        return;
 
    local_irq_save(flags);
    pending = local_softirq_pending();
    if (pending) /* 如果有激活的软中断 */
        __do_softirq(); /* 处理函数 */
    local_irq_restore(flags);
}
/* We restart softirq processing MAX_SOFTIRQ_RESTART times,
 * and we fall back to softirqd after that.
 * This number has been established via experimentation.
 * The two things to balance is latency against fairness - we want
 * to handle softirqs as soon as possible, but they should not be
 * able to lock up the box.
 */
asmlinkage void __do_softirq(void)
{
    struct softirq_action *h;
    __u32 pending;
    /* 本函数能重复触发执行的次数,防止占用过多的cpu时间 */
    int max_restart = MAX_SOFTIRQ_RESTART;
    int cpu;
 
    pending = local_softirq_pending(); /* 激活的软中断位图 */
    account_system_vtime(current);
    /* 本地禁止当前的软中断 */
    __local_bh_disable((unsigned long)__builtin_return_address(0), SOFTIRQ_OFFSET);
    lockdep_softirq_enter(); /* current->softirq_context++ */
    cpu = smp_processor_id(); /* 当前cpu编号 */
 
restart:
    /* Reset the pending bitmask before enabling irqs */
    set_softirq_pending(0); /* 重置位图 */
    local_irq_enable();
    h = softirq_vec;
    do {
        if (pending & 1) {
            unsigned int vec_nr = h - softirq_vec; /* 软中断索引 */
            int 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())) {
                printk(KERN_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());
            }
            rcu_bh_qs(cpu);
        }
        h++;
        pending >>= 1;
    } while(pending);
 
    local_irq_disable();
    pending = local_softirq_pending();
    if (pending & --max_restart) /* 重复触发 */
        goto restart;
 
    /* 如果重复触发了10次了,接下来唤醒ksoftirqd/n内核线程来处理 */
    if (pending)
        wakeup_softirqd(); 
 
    lockdep_softirq_exit();
    account_system_vtime(current);
    __local_bh_enable(SOFTIRQ_OFFSET);
}
  1. 首先调用local_softirq_pending函数取得目前有哪些位存在软件中断。
  2. 调用__local_bh_disable关闭软中断,其实就是设置正在处理软件中断标记,在同一个CPU上使得不能重入__do_softirq函数。
  3. 重新设置软中断标记为0,set_softirq_pending重新设置软中断标记为0,这样在之后重新开启中断之后硬件中断中又可以设置软件中断位。
  4. 调用local_irq_enable,开启硬件中断。
  5. 之后在一个循环中,遍历pending标志的每一位,如果这一位设置就会调用软件中断的处理函数。在这个过程中硬件中断是开启的,随时可以打断软件中断。这样保证硬件中断不会丢失。
  6. 之后关闭硬件中断(local_irq_disable),查看是否又有软件中断处于pending状态,如果是,并且在本次调用__do_softirq函数过程中没有累计重复进入软件中断处理的次数超过max_restart=10次,就可以重新调用软件中断处理。如果超过了10次,就调用wakeup_softirqd()唤醒内核的一个进程来处理软件中断。设立10次的限制,也是为了避免影响系统响应时间。
  7. 调用_local_bh_enable开启软中断。

(5) ksoftirqd内核线程调用do_softirq

内核不会立即处理重新触发的软中断。当大量软中断出现的时候,内核会唤醒一组内核线程来处理。这些线程的优先级最低(nice值为19),这能避免它们跟其它重要的任务抢夺资源。但它们最终肯定会被执行,所以这个折中的方案能够保证在软中断很多时用户程序不会因为得不到处理时间而处于饥饿状态,同时也保证过量的软中断最终会得到处理。每个处理器都有一个这样的线程,名字为ksoftirqd/n,n为处理器的编号。

static int run_ksoftirqd(void *__bind_cpu)
{
    set_current_state(TASK_INTERRUPTIBLE);
    current->flags |= PF_KSOFTIRQD; /* I am ksoftirqd */
 
    while(! kthread_should_stop()) {
        preempt_disable();
 
        if (! local_softirq_pending()) { /* 如果没有要处理的软中断 */
            preempt_enable_no_resched();
            schedule();
            preempt_disable():
        }
 
        __set_current_state(TASK_RUNNING);
 
        while(local_softirq_pending()) {
            /* Preempt disable stops cpu going offline.
             * If already offline, we'll be on wrong CPU: don't process.
             */
             if (cpu_is_offline(long)__bind_cpu))/* 被要求释放cpu */
                 goto wait_to_die;
 
            do_softirq(); /* 软中断的统一处理函数 */
 
            preempt_enable_no_resched();
            cond_resched();
            preempt_disable();
            rcu_note_context_switch((long)__bind_cpu);
        }
 
        preempt_enable();
        set_current_state(TASK_INTERRUPTIBLE);
    }
 
    __set_current_state(TASK_RUNNING);
    return 0;
 
wait_to_die:
    preempt_enable();
    /* Wait for kthread_stop */
    set_current_state(TASK_INTERRUPTIBLE);
    while(! kthread_should_stop()) {
        schedule();
        set_current_state(TASK_INTERRUPTIBLE);
    }
 
    __set_current_state(TASK_RUNNING);
    return 0;
}

三 . 总结
如果我们需要在中断下半部使用软中断,那么流程是
中断处理程序----》raise_softirq()触发软中断----》ksoftirqd线程调用softirq_pending(),发现有待处理的软中断时(返回1)----》ksoftirqd线程调用do_softirq()----》local_softirq_pending()返回软中断的32位位图-----》依次访问等待处理的软中断(位图中相应位值为1)的软中断处理函数

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux 之软中断softirq 的相关文章

  • 进程页表

    我有兴趣更好地了解虚拟内存和页面机制 特别是 Windows x86 系统 根据我从各种在线资源收集的信息 包括在 SO 上发布的其他问题 1 每个进程的单独页表位于同一进程的内核地址空间内 2 每个进程只有一个页表 包含虚拟页到物理页 或
  • 内核和用户模式之间共享内存。如何共享句柄?

    我正在尝试在用户进程和内核之间使用共享内存 选项一 让内核创建部分并让用户模式应用程序通过名称 Global my mem 打开内存 它仅在只读模式下工作 当我尝试使用 FILE MAP WRITE 打开部分时 它会拒绝访问 5 不确定如何
  • 待传输数据包skb分配的空间量到底是如何确定和分配的?

    据我了解 当内核堆栈 在 Tx 路径上 分配套接字缓冲区 skb 时 头 和 尾 指针指向保留内存空间的开始和结束 我的问题是 到底是什么决定了这个数量 尾 头 八位字节 是最大 MTU 吗 看起来不像 因为一些实验表明字节数 尾部 头部
  • 以内核模式运行的进程和以 root 身份运行的进程之间的区别?

    我知道在用户模式下运行的进程和在内核模式下运行的进程之间的区别 基于访问限制 对硬件的访问等 但出于好奇 以内核模式运行的进程与以 root 身份运行的进程有什么区别 内核模式和根是两个独立的概念 彼此之间并不真正相关 以 root 身份运
  • 定位并删除 jupyter 笔记本中的隐藏内核

    我试图找出我的 mac 中的 anaconda 内核在哪里 因为应用程序报告了不同的事情 如果我运行 jupyter 内核规范列表 I get 可用内核 python2 用户 用户 anaconda 共享 jupyter kernels p
  • Linux 中的内存区域标志:为什么需要 VM_WRITE 和 VM_MAYWRITE?

    Mel Gorman 的 Understanding the Linux Virtual Memory Manager 2007 年 这是本书章节链接 https www kernel org doc gorman html underst
  • 如何以编程方式动态管理 iptables 规则?

    我需要查询现有规则 以及能够轻松添加和删除规则 我还没有找到任何 API 来执行此操作 我缺少什么吗 我最接近的解决方案是使用iptables save iptables xml用于查询并手动调用 iptables 命令本身来添加 删除规则
  • 如何测量 Linux 中的真实 CPU 使用率?

    我知道有类似的工具top and ps用于测量 CPU 使用率 但他们测量 CPU 使用率的方法是测量空闲任务未运行的时间 因此 例如 即使 CPU 由于缓存未命中而出现停顿 这些工具仍然会认为 CPU 被占用 然而 我想要的是分析工具在停
  • 如何指定使用 bitbake/yocto 构建哪个内核

    我正在努力使用 yocto daisy 生成新的 BSP 当我构建图像时 我收到以下警告 NOTE Resolving any missing task queue dependencies NOTE multiple providers
  • Windows 驱动程序时间戳函数

    我正在修改现有的 Windows 内核设备驱动程序 并且需要在其中捕获时间戳 我打算使用 time h 库并调用 Clock 函数来实现这一点 但是在 Windows Visual Studio 下 链接失败 所以我把它作为我需要在驱动程序
  • Linux 内核驱动程序的探测函数何时被调用?

    我正在尝试更新Android的内核驱动程序 我添加了一些printk来调试它 调用了 init函数 但没有调用probe函数 我缺少什么 何时 如何调用探测函数 该代码可在以下位置获取 https github com lamegopint
  • 由于 system.currentTimeMillis() 导致系统 CPU 使用率较高

    我正在我们的 Storm Supervisor Wheezy 机器 上调试高系统 CPU 使用率 不是用户 CPU 使用率 以下是观察结果 相关进程的 perf 输出 Events 10K cpu clock 16 40 java kern
  • Linux内核中的模块间通信

    我有两个 Linux 内核模块 其中一个可以为另一个提供一些功能 但使用该功能并不是必需的 即使第一个模块不存在 第二个模块也可以 并且应该 工作 如果我只是从第一个模块导出函数并在第二个模块中使用它 则第二个模块依赖于该符号 并且在没有第
  • 如何从内核空间读取/写入 linux /proc 文件?

    我正在编写一个由用户程序和内核模块组成的程序 内核模块需要收集数据 然后将其 发送 到用户程序 这必须通过 proc 文件来完成 现在 我创建了文件 一切都很好 并且花了很长时间在互联网上寻找答案 但仍然找不到 如何读 写 proc 文件f
  • 如何在Linux内核中启用CONFIG_PREEMPT选项?

    我是 Linux 内核编程的新手 尝试在 x86 64 上使用旧内核 Linux 2 6 32 我想启用其中的 CONFIG PREEMPT 选项 但找不到有关如何执行此操作的信息 我可以使用我的首选选项编译新内核 但不知道在这种情况下我需
  • 操作系统如何知道缺失页面的磁盘地址?

    分页充当虚拟地址空间和物理地址空间之间的间接层 给定一个地址 操作系统 OS 内存管理单元 MMU 将其转换为主内存位置 我的问题是 主内存中不存在该页面的情况 操作系统如何知道在磁盘上哪里可以找到该页面 它在哪里存储1的信息 它不存储在页
  • 如何“安装”自定义 Windows 驱动程序?

    我计划用 C 语言编写一个基本的 Windows 注册表过滤器 该过滤器的目的是挂钩所有 用户和内核特权 注册表调用 以便我可以在我的程序中使用它们 我基本上是复制 Mark Rusinovich 的 regmon 进程监视器 但更基本 我
  • 在执行期间访问.eh_frame数据

    我正在尝试访问以下内容 eh frame正在运行的程序的一部分 具体来说 该程序是 Linux 内核 2 6 34 8 这 eh frame包含用于异常处理的有用数据 我想在内核代码内部使用它 该部分已经由以下人员编写gcc readelf
  • 尝试映射大页面 (1GB) 时 mmap 失败

    我做了什么 使用 root 启用大页 我的系统支持 1MB 大页 echo 20 gt proc sys vm nr hugepages 将大页文件系统挂载到 mnt hugepages mount t hugetlbfs nodev mn
  • Windows 内存映射文件

    我正在尝试研究 Windows 内核在内存映射文件 虚拟内存方面的行为 具体来说 我感兴趣的是确定内存映射文件的内容 由 Windows 刷新到磁盘的频率以及 Windows 使用什么标准来决定是时候这样做 我在网上做了一些研究 除了 MS

随机推荐

  • java发送邮件报SSL安全异常解决

    用公司阿里云企业邮箱发送邮件 一直报javax net ssl SSLHandshakeException PKIX path building failed sun security provider 异常 顾名思义是安全验证问题 以下是
  • R语言-地图绘制的思路

    R中的画地图的思路有三种 一种是利用包里GIS方面的数据 在R中直接画出来 第二种是从其他地方拿到数据 在R中通过某些包解析后再展现成 第三种就是直接把别人的图拿过来 自己再添加或去掉自己需要或不需要的东西 这三种方法只是数据来源不同 具体
  • BGP实验--联邦以及反射器

    实验明细 实验拓扑 实验要求 实验内容 实验拓扑 实验要求 1 R2 R7每台路由器均存在一个环回接口用于建立邻居 同时还存在一个环回来代表连接用户的接口 最终这些连接用户的接口网络需要可以和R1 8的环回通讯 2 AS2网段地址为172
  • 【C语言】扫雷小游戏的实现(爆炸展开)

    需要云服务器等云产品来学习Linux的同学可以移步 gt 腾讯云 lt gt 阿里云 lt gt 华为云 lt 官网 轻量型云服务器低至112元 年 新用户首次下单享超低折扣 各位朋友们大家好呀 今天又又是游戏整活环节 先介绍一下这个版本的
  • 【JavaScript】运算符及其优先级

    目录 一 算术运算符 1 常用的算术运算符 2 算术运算符的注意事项 二 递增和递减运算符 1 前置递增 递减 运算符 2 后置递增 递减 运算符 3 前缀和后缀的区别 三 比较运算符 关系运算符 四 逻辑运算符 1 运算中的短路现象 2
  • MATLAB车牌识别技术实现

    目 录 一 课程设计任务11 二 课程设计原理及设计方案22 1 系统简述22 2 图像预处理33 2 1灰度变换33 2 2边缘提取44 3 车牌定位55 4 字符分割55 5 字符识别66 三 课程设计的步骤和结果88 四 设计总结22
  • 关于深度学习中concat和eltwise两种特征融合方式用处的猜想

    在对网络不同地方的特征进行融合时 尤其是在深层网络融合浅层网络的特征的时候我们需要用到这两种融合方式 这两者的使用有有什么区别吗 在实际中 直接使用eltwise将当前的深层特征与浅层的特征融合时效果并不好 应该在eltwise前加若干层网
  • jmeter基本使用

    1 测试工具 jmeter压力测试相关工具 1 apache jmeter 3 1 2 JMeterPlugins Standard 1 4 0 JMeterPlugins Extras 1 4 0 3 ServrerAgent 2 2 1
  • windows azure系统简介

    Windows Azure Storage 云存储系统 提供给用户 貌似无限容量的数据存储 貌似可保存任意长的时间 数据按副本存放 本地副本 容忍设备故障 广域副本 容忍地域灾难 强一致性 待看 Azure存储系统的元素包括三种 Blobs
  • 随机抽奖程序(每天一个python小项目)

    import random num 用于存放抽奖码 print 欢迎来到抽奖小程序 while True usernum input 请输入参与抽奖的人数 reward input 请输入中奖人数 if usernum isdigit Tr
  • Python+Selenium-20-图片验证码处理

    前言 有些注册页面会含有验证码 本篇描述在selenium中对验证码的处理实例 需求 打开万维易源注册页面 https www showapi com auth reg 这个是第三方api平台易源接口 后面验证码识别用的就是他的 在注册页面
  • 【HBZ分享】ES索引分片的写入原理 及 流程

    当一条数据写到ES要经历哪些过程 当插入一条新的数据时 数据会进入Translog 和 MemoryBuffer两个内存中 并添加了事务日志 此时该文档不可查询 当translog大到一定程度时 会发生一个commit操作 也就是全量提交
  • python数据清洗 —— re.split()划分字符串

    需求 对于一行字符串 route views6 routeviews org 141694 2a0c b641 24f fffe 7 184891 CN apnic OTAKUJAPAN AS Otaku Limited CN 要将其划分成
  • CMD 命令行实现 Windows 下复制文件到文件夹下的所有文件夹

    目录 前言 1 学习 xcopy 2 展示命令行 前言 提示 这里可以添加本文要记录的大概内容 整件事情真是花了我大半天的时间 几个小时啊 终于从错误中尝试出了正确的做法 赶紧分享一下 1 学习 xcopy Win R 调出运行 键入 cm
  • ElasticSearch简介

    ElasticSearch是Java开发并且是当前最流行的开源的企业级搜索引擎 能够达到近实时搜索 稳定可靠快速安装使用方便 客户端支持Java net各种编程语言 ElasticSearch通Lucene的比较 Lucene只能在Java
  • MAC安装LLVM指导

    首先克隆llvm github工程代码 下载有时出现中断失败 git clone https github com llvm llvm project git 安装依赖软件 安装 ninja 也可以通过编译方式安装 brew install
  • 微信公众号内下载pdf等文件,受微信所限制,安卓和IOS不同处理方式(最最最优版)

    继上一篇文章微信公众号内下载pdf等文件 受微信所限制 安卓和IOS不同处理方式 后觉得还有更好的解决办法 这次真的找到更加优化版本 一定需要后台配合才行 后台接口返回Blob 后端设置response setHeader Content
  • OPC通信从入门到精通_1_OPC基础知识及简单C#程序编写(OPCDA,OPCUA简介;OPC通信数据流框架图;C#程序编写)

    文章目录 1 OPC基础知识 OPCDA OPCUA 1 1 OPC基础知识 1 2 OPC通信读写方式 2 OPC通信仿真 2 1 上位机与PLC通过ModbusTCP直接通信 2 2 OPC通信介绍及实例 2 2 1 OPC通信与Mod
  • TCP报文格式

    TCP报文格式 文章目录 TCP报文格式 TCP首部 三次握手 四次挥手 TCP首部 源端口和目的端口 各占16bit 序号 SEQ序号 给发送的每个数据包标上序号 确认号 ACK序号 是指即将接收的数据包序号 注意 这里指的是序号不是标志
  • Linux 之软中断softirq

    版权声明 本文为博主原创文章 未经博主允许不得转载 https blog csdn net huangweiqing80 article details 83274095 softirq驱动开发人员一般都不会用到 到内核代码中会用到soft