【深度】韦东山:一文看尽 linux对中断处理的前世今生

作者:韦东山

前言:

本文,4200字,研究代码花了一天,写出来花了一天;

录视频估计又得花半天;

真怀念以前简单粗暴的生活啊:

拿起话筒就录视频,

先画好图?那是不需要的

文档?那是不存在的

真是洒脱.....

现在,要写文档,又要画流程图,十几、二十分钟的视频,

真是沤心沥血做出来的,

各位,别浪费了,欢迎享受

韦东山老师正在录本文配套的视频,明天发布。咱们先预习。

分为7点:

  1. Linux对中断的扩展:硬件中断,软件中断

  2. 中断处理原则1:不能嵌套

  3. 中断处理原则2:越快越好

  4. 要处理的事情实在太多:拆分为:上半部,下半部

  5. 下半部的事情耗时不是太长:tasklet

  6. 下半部要做的事情太多并且很复杂:工作队列

  7. 新技术:threaded irq

从2005年我接触Linux到现在15年了,Linux中断系统的变化并不大。比较重要的就是引入了threaded irq:使用内核线程来处理中断。

Linux系统中有硬件中断,也有软件中断。

对硬件中断的处理有2个原则:不能嵌套,越快越好。

参考资料:

https://blog.csdn.net/myarrow/article/details/9287169

01

Linux对中断的扩展:硬件中断、软件中断

Linux系统把中断的意义扩展了,对于按键中断等硬件产生的中断,称之为“硬件中断”(hard irq)。每个硬件中断都有对应的处理函数,比如按键中断、网卡中断的处理函数肯定不一样。

为方便理解,你可以先认为对硬件中断的处理是用数组来实现的,数组里存放的是函数指针:

注意:上图是简化的,Linux中这个数组复杂多了。当发生A中断时,对应的irq_function_A函数被调用。硬件导致该函数被调用。相对的,还可以人为地制造中断:软件中断(soft irq),如下图所示:

注意:上图是简化的,Linux中这个数组复杂多了。

问题来了:

a. 软件中断何时生产?

由软件决定,对于X号软件中断,只需要把它的flag设置为1就表示发生了该中断。

b. 软件中断何时处理?

软件中断嘛,并不是那么十万火急,有空再处理它好了。

什么时候有空?不能让它一直等吧?

Linux系统中,各种硬件中断频繁发生,至少定时器中断每10ms发生一次,那取个巧?

在处理写硬件中断后,再去处理软件中断?就这么办!

有哪些软件中断?

查内核源码include/linux/interrupt.h

怎么触发软件中断?最核心的函数是raise_softirq,简单地理解就是设置softirq_veq[nr]的标记位:

怎么设置软件中断的处理函数:

后面讲到的中断下半部tasklet就是使用软件中断实现的。

02

中断处理原则1:不能嵌套

官方资料:中断处理不能嵌套

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=e58aa3d2d0cc

中断处理函数需要调用C函数,这就需要用到栈。

中断A正在处理的过程中,假设又发生了中断B,那么在栈里要保存A的现场,然后处理B。

在处理B的过程中又发生了中断C,那么在栈里要保存B的现场,然后处理C。

如果中断嵌套突然暴发,那么栈将越来越大,栈终将耗尽。

所以,为了防止这种情况发生,也是为了简单化中断的处理,在Linux系统上中断无法嵌套:即当前中断A没处理完之前,不会响应另一个中断B(即使它的优先级更高)。

03

中断处理原则2:越快越好

妈妈在家中照顾小孩时,门铃响起,她开门取快递:这就是中断的处理。她取个快递敢花上半天吗?不怕小孩出意外吗?

同理,在Linux系统中,中断的处理也是越快越好。

在单芯片系统中,假设中断处理很慢,那应用程序在这段时间内就无法执行:系统显得很迟顿。

在SMP系统中,假设中断处理很慢,那么正在处理这个中断的CPU上的其他线程也无法执行。

在中断的处理过程中,该CPU是不能进行进程调度的,所以中断的处理要越快越好,尽早让其他中断能被处理──进程调度靠定时器中断来实现。

在Linux系统中使用中断是挺简单的,为某个中断irq注册中断处理函数handler,可以使用request_irq函数:

在handler函数中,代码尽可能高效。

但是,处理某个中断要做的事情就是很多,没办法加快。比如对于按键中断,我们需要等待几十毫秒消除机械抖动。难道要在handler中等待吗?对于计算机来说,这可是一个段很长的时间。

怎么办?

04

要处理的事情实在太多,拆分为:上半部、下半部

当一个中断要耗费很多时间来处理时,它的坏处是:在这段时间内,其他中断无法被处理。换句话说,在这段时间内,系统是关中断的。

如果某个中断就是要做那么多事,我们能不能把它拆分成两部分:紧急的、不紧急的?

在handler函数里只做紧急的事,然后就重新开中断,让系统得以正常运行;那些不紧急的事,以后再处理,处理时是开中断的。

中断下半部的实现有很多种方法,讲2种主要的:tasklet(小任务)、work queue(工作队列)。


05

下半部要做的事情耗时不是太长:tasklet

假设我们把中断分为上半部、下半部。发生中断时,上半部下半部的代码何时、如何被调用?

当下半部比较耗时但是能忍受,并且它的处理比较简单时,可以用tasklet来处理下半部。tasklet是使用软件中断来实现。

写字太多,不如贴代码,代码一目了然:

使用流程图简化一下:

假设硬件中断A的上半部函数为irq_top_half_A,下半部为irq_bottom_half_A。

使用情景化的分析,才能理解上述代码的精华。

a. 硬件中断A处理过程中,没有其他中断发生:

一开始,preempt_count = 0;

上述流程图①~⑨依次执行,上半部、下半部的代码各执行一次。

b. 硬件中断A处理过程中,又再次发生了中断A:

一开始,preempt_count = 0;

执行到第⑥时,一开中断后,中断A又再次使得CPU跳到中断向量表。

注意

这时preempt_count等于1,并且中断下半部的代码并未执行。

CPU又从①开始再次执行中断A的上半部代码:

在第①步preempt_count等于2;

在第③步preempt_count等于1;

在第④步发现preempt_count等于1,所以直接结束当前第2次中断的处理;

注意

重点来了,第2次中断发生后,打断了第一次中断的第⑦步处理。当第2次中断处理完毕,CPU会继续去执行第⑦步。

可以看到,发生2次硬件中断A时,它的上半部代码执行了2次,但是下半部代码只执行了一次。

所以,同一个中断的上半部、下半部,在执行时是多对一的关系。

c. 硬件中断A处理过程中,又再次发生了中断B:

一开始,preempt_count = 0;

执行到第⑥时,一开中断后,中断B又再次使得CPU跳到中断向量表。

注意

这时preempt_count等于1,并且中断A下半部的代码并未执行。

CPU又从①开始再次执行中断B的上半部代码:

在第①步preempt_count等于2;

在第③步preempt_count等于1;

在第④步发现preempt_count等于1,所以直接结束当前第2次中断的处理;

注意

重点来了,第2次中断发生后,打断了第一次中断A的第⑦步处理。当第2次中断B处理完毕,CPU会继续去执行第⑦步。

在第⑦步里,它会去执行中断A的下半部,也会去执行中断B的下半部。

所以,多个中断的下半部,是汇集在一起处理的。

总结

a. 中断的处理可以分为上半部,下半部

b. 中断上半部,用来处理紧急的事,它是在关中断的状态下执行的

c. 中断下半部,用来处理耗时的、不那么紧急的事,它是在开中断的状态下执行的

d. 中断下半部执行时,有可能会被多次打断,有可能会再次发生同一个中断

e. 中断上半部执行完后,触发中断下半部的处理

f. 中断上半部、下半部的执行过程中,不能休眠:中断休眠的话,以后谁来调度进程啊?

06

下半部要做的事情太多并且很复杂:工作队列

在中断下半部的执行过程中,虽然是开中断的,期间可以处理各类中断。但是毕竟整个中断的处理还没走完,这期间APP是无法执行的。

假设下半部要执行1、2分钟,在这1、2分钟里APP都是无法响应的。

这谁受得了?

所以,如果中断要做的事情实在太耗时,那就不能用中断下半部来做,而应该用内核线程来做:在中断上半部唤醒内核线程。内核线程和APP都一样竞争执行,APP有机会执行,系统不会卡顿。

这个内核线程是系统帮我们创建的,一般是kworker线程,内核中有很多这样的线程:

kworker线程要去“工作队列”(work queue)上取出一个一个“工作”(work),来执行它里面的函数。

那我们怎么使用work、work queue呢?

a. 创建work:

你得先写出一个函数,然后用这个函数填充一个work结构体。比如:

b. 要执行这个函数时,把work提交给work queue就可以了:

上述函数会把work提供给系统默认的work queue:system_wq,它是一个队列。

c. 谁来执行work中的函数?

不用我们管,schedule_work函数不仅仅是把work放入队列,还会把kworker线程唤醒。此线程抢到时间运行时,它就会从队列中取出work,执行里面的函数。

d. 谁把work提交给work queue?

在中断场景中,可以在中断上半部调用schedule_work函数。

总结

a. 很耗时的中断处理,应该放到线程里去

b. 可以使用work、work queue

c. 在中断上半部调用schedule_work函数,触发work的处理

d. 既然是在线程中运行,那对应的函数可以休眠。


07

新技术:threaded irq

使用线程来处理中断,并不是什么新鲜事。使用work就可以实现,但是需要定义work、调用schedule_work,好麻烦啊。

太懒了太懒了,就这2步你们都不愿意做。

好,内核是为懒人服务的,再杀出一个函数:

你可以只提供thread_fn,系统会为这个函数创建一个内核线程。发生中断时,内核线程就会执行这个函数。

说你懒是开玩笑,内核开发者也不会那么在乎懒人。

以前用work来线程化的处理内核,一个worker线程只能由一个CPU执行,多个中断的work都由同一个worker线程来处理,在单CPU系统中也只能忍着了。但是在SMP系统中,明明有那么多CPU空着,你偏偏让多个中断挤在这个CPU上?

新技术threaded irq,为每一个中断都创建一个内核线程;多个中断的内核线程可以分配到多个CPU上执行,这提高了效率。

☆ END ☆

扫码或长按关注

回复「 加群 」进入技术群聊

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

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

相关文章

数据结构(6)二叉树

文章目录二叉树二叉树三种遍历二叉树知中及先或后序,求二叉树二叉树 class Node(object):""""""def __init__(self, item):self.elem itemself.lchild Noneself.rchild Noneclass Tree(object):"""二叉树"&qu…

鼠标中间无法打开新标签_还记得鼠标有几个键?Win10环境鼠标中键的妙用

大家经常使用鼠标,但有多少人还能意识到,其实鼠标是有三个键的?除了常用的左键右键,鼠标中间的滚轮也可以按下去,然而鼠标的这个中键,却日常坐冷板凳,几乎被人所遗忘。其实,鼠标中键…

找不到问题的核心,你永远解决不了问题。

上学那会儿,我是个数学常拿60分的人(满分150分),我却喜欢做几何题。根据已知的条件,推导出所需的其他条件,所有需要的条件得出后,再根据正确的公式算出所求的值。这个不断求解的过程其实挺有意思…

浏览器访问pdf 的url怎么加密_怎么解决加密的pdf文件?

加密版的PDF文件我们每次打开时都需要输入密码才能进行阅读,而且一般不能直接进行复制和打印,这样会非常的麻烦。那如果我门想要解决这一问题呢就要将pdf文件的密码进行解除,可是如果是平民的我们不会解决怎么办呢?毕竟我们又不是…

Linux Storage入门学习

前言本文大量代码基于linux 0.11&#xff0c;因为早期linux的版本更加适合初学者入门。虽然代码比较早&#xff0c;但是不妨碍我们学习Linux Storage的精髓。一、hello world1.1 Demo#include<stdio.h> #include<unistd.h> #include<sys/types.h> #include&l…

地铁客流检测训练问题记录

一 报错&#xff1a; File "/home/jz/py-faster-rcnn/tools/../lib/datasets/pascal_voc.py", line 183, in _load_pascal_annotation tree ET.parse(filename) File "/usr/lib/python2.7/xml/etree/ElementTree.py", line 1182, in parse tree.parse(sou…

记一次和摄像头的摩擦经历

因为时间的原因&#xff0c;这次点亮摄像头的时间特别短&#xff0c;昨天下午模组到公司&#xff0c;今天下午点亮。几个人一起调试&#xff0c;发现的问题也很多&#xff0c;今天下午发现有一个怀疑的问题&#xff0c;我马上驱车几十公里去模组厂调试&#xff0c;回来的时候&a…

声明为指针,定义为数组,声明为数组,定义为指针

之前写发的那篇指针和数组的文章&#xff0c;有网友评论觉得不是很舒服&#xff0c;我自己看了&#xff0c;觉得很不满意。所以想再写写&#xff0c;尽量把这个东西讲清楚。#定义为数组&#xff0c;声明为指针1.c中#include "stdio.h" char array[] "abcd"…

f2fs存储结构初探

前言学习文件系统的第一步&#xff0c;先搞清楚文件系统在设备上的存储结构&#xff0c;先来简单了解一下。F2FS空间布局图和描述选自《F2FS技术拆解》https://mp.weixin.qq.com/s/k1ibtWF_TRQi8wbqUGjMrgF2FS空间布局F2FS空间布局整个存储空间被划分为6个区域&#xff1a;超级…

net.conn read 判断数据读取完毕_高并发:缓存模式以及缓存的数据一致性

缓存由于其高性能&#xff0c;支持高并发的特性&#xff0c;在高并发的项目中不可或缺。被大家广泛使用的有Redis&#xff0c;Memcached等。本文主要探讨几种常见的缓存的读写模式&#xff0c;以及如何来保证缓存和数据库的数据一致性。这里大家可以关注一下我的个人专栏《PHP进…

PHP配置环境中开启GD库

下配置好的PHP环境中&#xff0c;GD库不像windows那样可以直接用&#xff0c;而是默认关闭&#xff0c;需要把它打开&#xff0c;去到php.ini文件中 找到php_gd2.dll把分号去掉即可。(注&#xff1a;GD库跟绘制二维码等有关) 转载于:https://www.cnblogs.com/mrszhou/p/7421161…

USB协议普及文

#目录#USB#USB On-The-Go Supplement#技术指标#机械和电气标准#编码方式#软件架构#端点#HCD#USB 封包格式#设备分类#USB接头#电源#Storage#人机接口设备&#xff08;HID&#xff09;通用串行总线(Universal Serial Bus, USB&#xff09;是连接外部设备的一个串口总线标准&#…

mysql jdbc驱动_JDBC认识与实践

一、什么是JDBC&#xff1f;Java数据库连接&#xff0c;&#xff08;Java Database Connectivity&#xff0c;简称JDBC&#xff09;是Java语言中用来规范客户端程序如何来访问数据库的应用程序接口&#xff0c;提供了诸如查询和更新数据库中数据的方法。JDBC思维导图二、JDBC应…

画布实现拼图原理

1. 页面布局 1.1 bg: 背景提示图&#xff0c;使用半透明效果&#xff0c;移动图片后显示&#xff0c;层级最低&#xff0c;z-index:-1; 1.2 cvs: 当前画布&#xff0c;层级默认0&#xff1b; 1.3 content: 覆盖在画布之上 z-index:1; 1.3.1 currentCheckpoint: 显示当前关卡数…

晚归的码农老公

呃&#xff0c;我是本号主的贤内助&#xff0c;我们的儿子叫楠哥~由于疫情的影响&#xff0c;我们已经很久没有带楠楠回他外婆家玩了&#xff0c;只能用电话跟外婆聊聊家常。每一次通话结束&#xff0c;他外婆总会加问一句&#xff0c;启发回来了吧。我们每次的回答都是&#x…

Rockchip USB转485

#RS232 / RS485 简介#RS232#RS485#R485与RS232比较#开发#DTS配置#驱动开发#POSIX规范API#HAL层以上APP层#APK call JNI#APP#调试#log开启#RS232/RS485/RS422常见问题#RS232 / RS485 简介#RS232RS-232是美国电子工业联盟&#xff08;EIA&#xff09;制定的串行数据通信的接口标…

高嘌呤食物搜索引擎_“高嘌呤”的食物已发现,尿酸高的人,要尽量挑着吃!...

导语&#xff1a;在我们的生活中又出现了一种现象&#xff0c;越来越频发&#xff0c;也是越来越倾向于年轻化&#xff0c;那么就是高尿酸&#xff0c;现在高尿酸已经被列为了我们国家的第四高&#xff0c;身体内的嘌呤含量是一部分&#xff0c;那么另一部分也是来源于我们平时…

罗老师带货了

相对比其他事情&#xff0c;我对罗老师带货很感兴趣&#xff0c;也许这样说&#xff0c;我对罗老师这个人比较感兴趣。罗老师是一个经过大风大浪的男人&#xff0c;经过了各种风雨&#xff0c;终于找到自己的位置&#xff0c;也决定在这个位置上发光发热了。我觉得他会像冯提莫…

【Linux笔记】LED驱动程序

前言 上一篇我们分享了字符设备驱动框架&#xff1a;【Linux笔记】驱动基础篇&#xff0c;当时分享的是hello驱动程序。学STM32我们从点灯开始&#xff0c;学Linux驱动我们自然也要点个灯来玩玩&#xff0c;尽量在从这些基础例程中榨取知识&#xff0c;细抠、细抠&#xff0c;为…

oracle trigger 延迟执行_一文详解Spring任务执行和调度

一、概述Spring框架分别使用TaskExecutor和TaskScheduler接口提供异步执行和任务调度的抽象。Spring还提供了这些接口的实现&#xff0c;这些接口支持线程池或将其委托给应用服务器环境中的CommonJ。二、TaskExecutorSpring 2.0 开始引入的新的抽像。Executors 是线程池的Java …