linux-中断下半部

news/2024/4/20 4:29:05/文章来源:https://blog.csdn.net/vertor11/article/details/129250420

引用

  • preempt

  • 宋宝华: 是谁关闭了Linux抢占,而抢占又关闭了谁?

  • Linux用户抢占和内核抢占详解(概念, 实现和触发时机)--Linux进程的管理与调度(二十)

  • 内核抢占实现(preempt)

  • Linux中的preempt_count - 知乎 (zhihu.com)

  • linux 中断子系统 - linux 内核中的上下文判断 - 知乎 (zhihu.com)

  • softirq

  • linux kernel的中断子系统之(八):softirq

  • Linux Interrupt - 魅族内核团队

  • sofirq和tasklet - LoyenWang

  • tasklet

  • linux kernel的中断子系统之(九):tasklet

  • 高并发的中断下半部tasklet实例解析

  • workqueue

  • Linux Workqueue- 魅族内核团队

  • Concurrency Managed Workqueue - wowo

  • Linux中断子系统(四)-Workqueue - LoyenWang

  • 任务工厂 - Linux 中的 workqueue 机制 [一] - 知乎 (zhihu.com)

  • Linux Workqueue 机制分析 - 博客 - binsite (binss.me)

  • Linux中断管理 (3)workqueue工作队列 - ArnoldLu - 博客园 (cnblogs.com)

  • timer

  • Linux时间子系统之(二):软件架构

  • Linux kernel之内核定时器

  • Linux内核定时器和工作队列的总结和实例

  • Linux内核高精度定时器hrtimer的使用

  • Linux 应用层的时间编程

  • 硬件和 GLibC 库的细节

  • Linux 内核的工作 - timer

  • Linux 内核的工作-hrtimer


一. 为什么要有上下半部

中断分成上下半部处理可以提高中断的响应能力,在上半部处理完成后便将cpu中断打开(通常上半部处理越快越好),这样就可以响应其他中断了,等到中断退出的时候再进行下半部的处理。

二. preempt_count

  • task_struct结构体中的thread_info.preempt_count用于记录当前任务所处的context状态;

  • PREEMPT_BITS

  • 用于记录禁止抢占的次数,禁止抢占一次该值就加1,使能抢占该值就减1;

  • SOFTIRQ_BITS

  • 用于同步处理,关掉下半部的时候加1,打开下半部的时候减1;

  • HARDIRQ_BITS

  • 用于表示处于硬件中断上下文中;

  • in_softirq和in_serving_softirq都表示处于softirq上下文,但并不意味着程序正在执行软中断,区别是:

  • in_serving_softirq表示 当前一定有软中断处于执行状态。(bit8 - SOFTIRQ_OFFSET)

  • in_softirq 除了可以表示当前有软中断处于执行状态,还有可能表示当前的context只是disable软中断的thread上下文。(例如:local_bh_disable()下的context)

  • 中断上下文 - interrupt context

  • 我们将 NMI, HARDIRQ, SOFTIRQ 上下文 统称为中断上下文。

  • 可用 in_interrupt() 判断

  • 进程上下文 - process context

  • 与中断上下文相对应。

  • 可用 in_task() 判断

  • 原子上下文 - atomic context

  • 不能发生进程睡眠或者调度的上下文。

  • 处于中断上下文,或者显示地禁止了调度,preempt_count()的值都不为0,都不允许睡眠/调度的发生,这两种场景被统称为atomic上下文。

  • 可用 in_atomic() 来判断当前cpu是否处于atomic上下文。

  • 也就是非 preempt_count 非 0 时,都属于 atomic 上下文,其中包括中断、软中断等中断上下文,还包括进程或者内核线程运行时关中断或者关抢占。

  • 由于该接口在有些场景下不能精确检测,所以 不推荐在driver中使用。

  • 三种上下文的关系

三. softirq

softirq是静态的,不支持动态分配。

  • 相关数据结构

/* 支持的软中断类型,可以认为是软中断号, 其中从上到下优先级递减 */
enum
{HI_SOFTIRQ=0,       /* 最高优先级软中断 */TIMER_SOFTIRQ,      /* Timer定时器软中断 */NET_TX_SOFTIRQ,     /* 发送网络数据包软中断 */NET_RX_SOFTIRQ,     /* 接收网络数据包软中断 */BLOCK_SOFTIRQ,      /* 块设备软中断 */IRQ_POLL_SOFTIRQ,   /* 块设备软中断 */TASKLET_SOFTIRQ,    /* tasklet软中断 */SCHED_SOFTIRQ,      /* 进程调度及负载均衡的软中断 */HRTIMER_SOFTIRQ, /* Unused, but kept as tools rely on thenumbering. Sigh! */RCU_SOFTIRQ,    /* Preferable RCU should always be the last softirq, RCU相关的软中断 */NR_SOFTIRQS
};/* 软件中断描述符,只包含一个handler函数指针 */
struct softirq_action {void	(*action)(struct softirq_action *);
};/* 软中断描述符表,实际上就是一个全局的数组 */
static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp;/* CPU软中断状态描述,当某个软中断触发时,__softirq_pending会置位对应的bit */
typedef struct {unsigned int __softirq_pending;unsigned int ipi_irqs[NR_IPI];
} ____cacheline_aligned irq_cpustat_t;/* 每个CPU都会维护一个状态信息结构 */
irq_cpustat_t irq_stat[NR_CPUS] ____cacheline_aligned;/* 内核为每个CPU都创建了一个软中断处理内核线程 */
DEFINE_PER_CPU(struct task_struct *, ksoftirqd);
  • 数据结构关系图

  • softirq_vec[]数组,类比硬件中断描述符表irq_desc[],通过软中断号可以找到对应的handler进行处理,比如图中的tasklet_action就是一个实际的handler函数;

  • 软中断可以在不同的CPU上并行运行,在同一个CPU上只能串行执行;(即软中断不保证重入问题

  • 每个CPU维护irq_cpustat_t状态结构,当某个软中断需要进行处理时,会将该结构体中的__softirq_pending字段或上1UL << XXX_SOFTIRQ

  • 软中断的触发点

  • raise_softirq()/raise_softirq_irqoff() 会设置当前本地cpu的irq_stat中的 __softirq_pending字段,并将相应的软中断号置位,即表明该软中断有处理请求。

  • 软中断执行点

  • 中断处理后;

  • bottom-half enable后;

  • 思考

为什么在使能Bottom-half时要进行软中断处理呢?

==》

在并发处理时,可能已经把Bottom-half进行关闭了,如果此时中断来了后,软中断不会被处理,在进程上下文中打开Bottom-half时,这时候就会检查是否有软中断处理请求了;

四. tasklet

tasklet是软中断的一种类型,那么两者有啥区别呢?

  • 软中断类型内核中都是静态分配,不支持动态分配,而tasklet支持动态和静态分配,也就是驱动程序中能比较方便的进行扩展;

  • 软中断可以在多个CPU上并行运行,因此需要考虑可重入问题,而tasklet会绑定在某个CPU上运行,运行完后再解绑,不要求重入问题,当然它的性能也就会下降一些;

  • DEFINE_PER_CPU(struct tasklet_head, tasklet_vec)为每个CPU都分配了tasklet_head结构,该结构用来维护struct tasklet_struct链表,需要放到该CPU上运行的tasklet将会添加到该结构的链表中,内核中为每个CPU维护了两个链表tasklet_vectasklet_vec_hi,对应两个不同的优先级,本文以tasklet_vec为例;

  • struct tasklet_structtasklet的抽象,几个关键字段如图所示,通过next来链接成链表,通过state字段来标识不同的状态以确保能在CPU上串行执行,func函数指针在调用task_init()接口时进行初始化,并在最终触发软中断时执行;

  • 接口

/* 静态分配tasklet */
DECLARE_TASKLET(name, func, data)/* 动态分配tasklet */
void tasklet_init(struct tasklet_struct *t, void (*func)(unsigned long), unsigned long data);/* 禁止tasklet被执行,本质上是增加tasklet_struct->count值,以便在调度时不满足执行条件 */
void tasklet_disable(struct tasklet_struct *t);/* 使能tasklet,与tasklet_diable对应 */
void tasklet_enable(struct tasklet_struct *t);/* 调度tasklet,通常在设备驱动的中断函数里调用 */
void tasklet_schedule(struct tasklet_struct *t);/* 杀死tasklet,确保不被调度和执行, 主要是设置state状态位 */
void tasklet_kill(struct tasklet_struct *t);

五. workqueue/delay wrokqueue

  • linux workqueue机制有多个woker 线程?

  • 有多个worker_pool,管理多个worker。

  • 针对bound绑定类型的工作队列,worker_pool是Per-CPU创建,每个CPU都有两个worker_pool,对应不同的优先级,nice值分别为0和-20;

  • 针对un-bound非绑定类型的工作队列,worker_pool创建后会添加到unbound_pool_hash哈希表中;

  • 每个worker_pool至少有一个worker。

  • worker内核线程是在每个worker_pool中由一个初始的空闲工作线程创建的,并根据需要动态创建和销毁;

  • create_worker函数中,创建的内核线程名字为kworker/XX:YY或者kworker/uXX:YY,其中XX表示worker_pool的编号,YY表示worker的编号,u表示unbound;

  • bound和un-bound workqueue的区别?

  • bound:绑定处理器的工作队列,其会被bound的worker_pool服务,该worker_pool创建的worker内核线程会被绑定到特定的CPU上运行;

  • unbound:不绑定处理器的工作队列,其会被un-bound的worker_pool服务,创建的时候需要指定WQ_UNBOUND标志,内核线程可以在处理器间迁移;

  • 何时创建更多的worker?

  • 内核线程执行worker_thread函数时,如果没有空闲的worker,会调用manage_workers接口来创建更多的worker来处理工作;

  • 何时销毁多余的worker?

  • 一个worker被创建后首先进入worker_enter_idle(),里面启动了pool->idle_timer,定时IDLE_WORKER_TIMEOUT即300HZ。如果一个worker进入idle超过300HZ,即会执行idle_worker_timeout(),会根据情况进行销毁多余的worker。

  • 如何解决一个work阻塞或者死锁了,导致其他的work得不到执行,即各种work之间的互相影响?

  • 在worker线程执行时,会尝试进行worker_pool管理工作,即会检查worker_pool中是否有至少一个idle状态的worker,如果没有,则创建一个新的worker。

  • 系统每100ms启动pool->mayday_timer定时器,检查当前workerpool中是否存在allocation deadlock异常,如果指定了WQ_MEM_RECLAIM,则会启动rescuer worker 进行处理。

  • 管理worker_pool的内核线程池时,如果有PENDING状态的work,并且发现没有正在运行的工作线程(worker_pool->nr_running == 0),唤醒空闲状态的内核线程,或者动态创建内核线程;

  • 如果work已经在同一个worker_pool的其他worker中执行,不再对该work进行处理(防重入处理);

  • schedule_work 是如何工作的?

  • schedule_work完成的工作是将work添加到对应的链表中,而在添加的过程中,首先是需要确定pool_workqueue;

  • pool_workqueue对应一个worker_pool,因此确定了pool_workqueue也就确定了worker_pool,进而可以将work添加到工作链表中;

  • pool_workqueue的确定分为三种情况:

  • bound类型的工作队列,直接根据CPU号获取(可指定cpu,如果没有指定,则用当前cpu。);

  • unbound类型的工作队列,根据node号获取,针对unbound类型工作队列,pool_workqueue的释放是异步执行的,需要判断refcnt的计数值,因此在获取pool_workqueue时可能要多次retry;

  • 根据缓存热度,优先选择正在被执行的worker_pool(所以,指定了cpu也不一定生效);

  • WORK_STRUCT_PENDING_BIT何时被设置以及被清0?

  • 当一个work已经加入到workqueue队列中,schedule_work()->queue_work()->queue_work_on()时被设置。

  • 当一个work在工作线程里马上要执行(即执行work之前),worker_thread()->process_on_work()->set_work_pool_and_clear_pend是清0。

  • 上述设置和清0都是在关闭本地中断情况下执行的。

  • 如何判断一个workqueue中的work已经全部执行完成?

  • 添加一个flush work到对应的workqueue中,如果该flush work都已经被执行了,那它之前的work也应该被执行完成了。

  • 如何指定一个work跑到特定的cpu上?

  • 寻找合适的pool_workqueue,优先选择本地CPU对应的pool_workqueue;如果该work正在另一个CPU工作线程池中运行,则优先选择此工作线程池。

  • 如果一个work正在上一次的worker_pool中执行,则本次work也会被放到上次执行的worker_pool中,是为了利用缓存,提高效率。 (即work_queue_on() 不一定能够指定运行的cpu。)

  • 如何指定一个workqueue中,不同work被同时执行的个数?

  • 判断当前pool_workqueue的work活跃数量,如果少于最高限值,就加入pending状态链表worker_pool->worklist,否则加入delayed_works链表中。

  • 如果一个worker_pool中的worker要进入睡眠了,如何保证其余的work能够被执行?

  • __schedule() 函数中,会进行处理。

  • 当一个工作线程要被调度器换出时,调用wq_worker_sleeping()看看是否需要唤醒同一个线程池中的其它内核线程。

  • workqueue lockup机制是怎么样的?

  • 会用一个timer 检查一个work执行了 30s都没有退出,就会大于warning log。

  • WQ_MEM_RECLAIM 标志表示什么意思?

  • 创建工作队列时,如果设置了WQ_MEM_RECLAIM标志,则会新建rescuer worker,对应rescuer_thread内核线程。

  • 目的是当内存紧张时,新创建worker可能会失败,这时候由rescuer来处理这种情况;

  • 数据结构

  • work_struct:工作队列调度的最小单位,work item

  • workqueue_struct:工作队列,work item都挂入到工作队列中;

  • workerwork item的处理者,每个worker对应一个内核线程;

  • worker_poolworker池(内核线程池),是一个共享资源池,提供不同的worker来对work item进行处理;

  • pool_workqueue:充当桥梁纽带的作用,用于连接workqueueworker_pool,建立链接关系;

六. timer

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

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

测试行业干了5年,从只会点点点到了现在的测试开发,总算是证明了自己

测试不止是点点点 我感觉我是一个比较有发言权的人吧&#xff0c;我在测试行业摸爬滚打5年&#xff0c;以前经常听到开发对我说&#xff0c;天天的点点点有意思没&#xff1f; 和IT圈外的同学、朋友聊起自己的工作&#xff0c;往往一说自己是测试&#xff0c;无形中也会被大家…

再度盈利,搜狐稳了?

2016年在宣布要用3年时间回归互联网舞台中心之后&#xff0c;很长一段时间内张朝阳积极活跃在各种社交媒体上&#xff0c;完全是一派“积极出山”的姿态。而后畅游从美股退市&#xff0c;搜狗“卖身”腾讯&#xff0c;一系列的收缩动作又似乎是在逐渐远离喧嚣。而在最近三年&am…

华为OD机试模拟题 用 C++ 实现 - 自动曝光(2023.Q1)

最近更新的博客 【华为OD机试模拟题】用 C++ 实现 - 最多获得的短信条数(2023.Q1)) 文章目录 最近更新的博客使用说明自动曝光题目输入输出描述示例一输入输出说明示例二输入输出说明Code使用说明 参加华为od机试,一定要注意不要完全背诵代码,需要理解之后模仿写出࿰

SRC挖掘之Access验证校验的漏洞挖掘

漏洞已修复&#xff0c;感觉某大佬的知识分享 任意用户密码重置->可获取全校师生个人min感信息 开局就是信息收集 对于挖掘edu的信息收集 1.可尝试谷歌搜索语法&#xff0c;获取学号信息 1. 旁站的渗透获取 2. 学校的贴吧获取(大部分都是本校学生) 当然我就是闲着蛋疼,进了…

让马斯克反悔的毫米波雷达,被国产雷达头部厂商木牛科技迭代到了5D时代

近日&#xff0c;特斯拉或将在其HW4.0硬件系统配置一枚高精度4D毫米波雷达的消息在外网刷屏。据分析&#xff0c;“纯视觉”信仰者马斯克之所以做出这样的决定&#xff0c;一方面是减配了雷达的特斯拉自动驾驶&#xff0c;表现不尽如人意&#xff1b;另一方面也跟毫米波雷达的技…

有趣的HTML实例(十四) 窗边风景动画(css+js)

不要憎恨你的敌人&#xff0c;那会影响你的判断力。 ——《教父》 目录 一、前言 二、往期作品回顾 三、作品介绍 四、本期代码介绍 五、效果显示 六、编码实现 index.html style.css script.js 七、获取源码 公众号获取源码 获取源码&#xff1f;私信&#xff1…

Javascript的API基本内容(四)

一、日期对象 获取时间戳的方法&#xff0c;分别为 getTime 和 Date.now 和 new Date() // 1. 实例化const date new Date()// 2. 获取时间戳console.log(date.getTime()) // 还有一种获取时间戳的方法console.log(new Date())// 还有一种获取时间戳的方法console.log(Date.n…

【微信小程序-原生开发+TDesign】通用功能页封装——地点搜索(含腾讯地图开发key 的申请方法)

效果预览 核心技能点 调用腾讯地图官方的关键字地点搜索功能&#xff0c;详见官方文档 https://lbs.qq.com/miniProgram/jsSdk/jsSdkGuide/methodGetsuggestion 完整代码实现 地点输入框 <t-input value"{{placeInfo.title}}" bindtap"searchPlace" dis…

硬件工程师——门控开关的设计

假设自己是一个工程师&#xff0c;那么我们怎么介绍自己呢&#xff1f; 我们首先需要可以自己独立设计项目&#xff0c;需要每一个工程师在开始阶段可以独立做项目&#xff0c;从而提高薪水 那么我们怎么提高做项目的能力呢&#xff1f; 通过项目来积累经验&#xff0c;在短…

推荐几个实用的在线教程(建议收藏)

hello&#xff0c;大家好&#xff0c;我是木荣君。作为一名技术人员&#xff0c;工作中熟练使用各种管理及设计工具是一项必备的技能。今天给大家分享一下我工作中经常查看和学习的几个在线教程地址。供大家参考学习&#xff0c;建议收藏&#xff0c;真的很实用&#xff01; Gi…

InnoDB数据页结构__盛放记录的大盒子

一、不同类型的页简介 前边我们简单提了一下页的概念&#xff0c;它是InnoDB管理存储空间的基本单位&#xff0c;一个页的大小一般是16KB。InnoDB为了不同的目的而设计了许多种不同类型的页&#xff0c;比如存放空间头部信息的页&#xff0c;存放Insert Buffer信息的页&#xf…

MySQL 横表和竖表相互转换

一 竖表转横表 1. 首先创建竖表 create table student ( id varchar(32) primary key, name varchar (50) not null, subject varchar(50) not null, result int); 2. 插入数据 insert into student (id, name, subject, result) values (0001, 小明, 语文, 83); insert into…

一文透视宝莱特CRM如何建设与落地

宝莱特集团成立于1993年&#xff0c;是一家国家高新技术企业&#xff0c;始终坚持在医疗器械领域的深耕细作&#xff0c;坚持患者第一、临床优先&#xff0c;业务板块涵盖生命信息与支持、肾病医疗和大健康医疗三大领域。自成立以来&#xff0c;宝莱特推出了多个中国第一的自主…

八通道触摸芯片GTC08L Pin to Pin替代启攀微八通道触摸芯片

能Pin to Pin替代启攀微八通道触控芯片的电容式触摸芯片-GTC08L是一款非常适用于小功率音箱上超稳定超抗干扰低功耗八通道电容式触摸IC&#xff1b;可通过触摸实现各种逻辑功能控制&#xff1b;可以在发动机运行下进行8通道电容传感&#xff1b;对电磁兼容、电磁干扰、温湿度变…

Java的运算操作

个人主页&#xff1a;平行线也会相交 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 平行线也会相交 原创 收录于专栏【JavaSE_primary】 文章目录算术运算符增量运算符注意自增自减运算符关系运算符逻辑运算符逻辑与&&逻辑或||逻辑非&#xff01;…

华为OD机试题,用 Java 解【考古学家】问题

最近更新的博客 华为OD机试题,用 Java 解【停车场车辆统计】问题华为OD机试题,用 Java 解【字符串变换最小字符串】问题华为OD机试题,用 Java 解【计算最大乘积】问题华为OD机试题,用 Java 解【DNA 序列】问题华为OD机试 - 组成最大数(Java) | 机试题算法思路 【2023】使…

VB6遍历目录(文件夹和文件)

日期&#xff1a;2023年2月27日 作者&#xff1a;Commas 签名&#xff1a;(ง •_•)ง 积跬步以致千里,积小流以成江海…… 注释&#xff1a;如果您觉得有所帮助&#xff0c;帮忙点个赞&#xff0c;也可以关注我&#xff0c;我们一起成长&#xff1b;如果有不对的地方&#xf…

产业链金融对接央行征信过程

2018年10月底&#xff0c;央行二代征信系统上线试运行。2018年10月至2019年4月&#xff0c;央行二代征信系统 进行试运行&#xff0c;全国16家试点机构参与全业务试运行&#xff0c;14家机构进行查询试点。2019年6月&#xff0c;央行二 代征信系统正式上线。2019年6月至2020年1…

计算机信息系统安全服务等级证

计算机信息系统安全服务等级评定是规范行业服务、提升企业诚信度、保证工程质量、市场准入控制的重要保证&#xff0c;是安全服务机构从事信息网络安全服务能力的等级证明&#xff0c;为我省信息化建设使用单位在选择网络安全服务机构时提供参考依据。 等级划分 安全服务机构等…

Java中常用的七种队列你了解多少?

文章目录Java中常用的七种队列你了解多少?ArrayBlockingQueue队列如何使用&#xff1f;添加元素到队列获取队列中的元素遍历队列LinkedBlockingQueue队列如何使用&#xff1f;1. 创建SynchronousQueue对象2. 添加元素到队列3. 获取队列中的元素4. 遍历队列SynchronousQueue队列…