首页 > 系统相关 >【深度】韦东山:一文看看尽linux对中断处理的前世今生

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

时间:2023-07-03 10:33:06浏览次数:54  
标签:今生 中断 work 处理 前世 半部 linux 线程 执行


交流群一:QQ群:869222007(鸿蒙开发/Linux/嵌入式/驱动/资料下载)
交流群二:QQ群:536785813(单片机-嵌入式)
公 众 号:百问科技


版本

日期

作者

说明

V1

2020

韦东山

技术文档

前言:

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

录视频估计又得花半天;

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

拿起话筒就录视频,

先画好图?那是不需要的

文档?那是不存在的

真是洒脱.....

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

真是沤心沥血做出来的,

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

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

分为7点:

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

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

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

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

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

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

新技术:threaded irq

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

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

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


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

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

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

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

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

当发生A中断时,对应的irq_function_A函数被调用。硬件导致该函数被调用。

相对的,还可以人为地制造中断:软件中断(soft irq),如下图所示:

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

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

问题来了:

a. 软件中断何时生产?

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

b. 软件中断何时处理?

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

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

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

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

有哪些软件中断?

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

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

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

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

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

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

后面讲到的中断下半部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函数:

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

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

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

怎么办?

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

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

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

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

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

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

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

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

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

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

使用流程图简化一下:

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

假设硬件中断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线程,内核中有很多这样的线程:

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

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

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

a. 创建work:

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

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

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

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

上述函数会把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步你们都不愿意做。

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

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

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

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

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

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

☆ END ☆



标签:今生,中断,work,处理,前世,半部,linux,线程,执行
From: https://blog.51cto.com/weidongshan/6609248

相关文章

  • Linux PWM 开发指南
    LinuxPWM开发指南1概述1.1编写目的介绍PWM模块的详细设计方便相关人员进行PWM模块的代码设计开发。1.2使用范围适用于Linux-3.10,linux-4.4和Linux-4.9内核,Linux-5.4内核。1.3相关人员PWM驱动的开发人员/维护人员等2术语及概念2.1术语定义及缩略语术语解释说明Sun......
  • Linux NOR 开发指南
    LinuxNOR开发指南1简介编写目的此文档描述SunxiNOR模块的使用方法,为相关人员调试提供指导适用范围boot0:适用于brandy-2.0u-boot:适用于u-boot-2018kernel:适用于linux-4.9/linux-5.4内核BSP的开发人员、测试人员2模块介绍2.1模块功能介绍Linux中SPINOR体系结构......
  • Tina_Linux_系统裁剪_开发指南
    文章目录Tina_Linux_系统裁剪_开发指南1概述2Tina系统裁剪简介2.1boot0裁剪2.2uboot裁剪2.3内核裁剪2.3.1删除不使用的功能2.3.2删除不使用的驱动2.3.3修改内核源代码2.3.3.1size工具.2.3.3.2ksize.py脚本2.3.3.3nm命令2.3.3.4kernel压缩方式.2.4文件系统裁剪.2.4.1......
  • 全志Tina Linux SPINAND UBI 离线烧录 开发指南 支持百问网T113 D1-H哪吒 DongshanPI-
    1概述编写目的:介绍SunxiSPINand烧写时的数据布局2名词解释词义UBIunsortedblockimagePEBphysicaleraseblockLEBlogicaleraseblockPEB和logicalblock关系1PEB=1logicalblock1logicalblock=2physicalblocks3总体数据布局ubi方案FLASH上的数据布局sys_pa......
  • 全志 Linux 系统启动优化 启动优化速度方式 优化启动流程 优化uboot 优化kernel等
    文章目录1概述2启动速度优化简介2.1启动流程2.2测量方法2.2.1printktime2.2.2initcall_debug2.2.3bootgraph.2.2.4bootchart2.2.5gpio+示波器.2.2.6grabserial.2.3优化方法2.3.1boot0启动优化2.3.1.1非安全启动.2.3.1.2安全启动2.3.2uboot启动优化2.3.2.1完全去......
  • 关于Linux系统下-zabbix-agent-的安装
    本文主要讲解关于Zabbix-Agent在Linux系统下的安装可以针对于如下一些系统AmazonLinux1/2 RedHatEnterpriseLinuxServerrelease6.XRedHatEnterpriseLinuxServerrelease7.X1、到Zabbix官方,下载并安装软件包(AmazonLinux1使用RHEL6的安装包,AmazonLinux2使......
  • 在 Linux 上使用的五个超级神奇的 Shell 别名
    导读在这篇文章中,我想告诉你一些作为工程师我每天都在使用的缩写,这些缩写是绝对的生命救星如果你还没有充分利用Shell缩写,那么你正在浪费宝贵的时间。一遍又一遍地重复输入相同的内容是无聊、单调而且效率低下的。为什么要花时间记住冗长的命令,当你可以简化它们呢?缩写是......
  • [FAQ] 对于 Puppeteer 和 Chromium 在 Linux 上的安装,需要安装哪些依赖库
     比如puppeteer/chrome/linux-114.0.5735.133/chrome-linux64/chrome到底要装哪些依赖。 一般根据报错提示,安装缺少的即可,以下是一般需要的:$sudoapt-getinstalllibatk1.0-0libatk-bridge2.0-0libcups2libxkbcommon0libxcomposite1libxdamage1libxfixes3libxr......
  • [GPT] Linux 如何查看 crontab 的运行记录
     要查看crontab的运行记录,可以使用以下命令: $grepCRON/var/log/syslog或者 $tail /var/log/syslog 这将在/var/log/syslog文件中查找包含"CRON"关键字的日志条目,其中包含有关crontab任务运行的信息。请注意,这个命令假设你的系统日志文件位于/var/log/syslog......
  • linux 中 shell脚本实现提取gff文件中的最长转录本
     001、数据和脚本[root@PC1test2]#lsGCF_001704415.1_ARS1_genomic.gffrecord.sh 002、脚本[root@PC1test2]#catrecord.sh##脚本内容#!/bin/bas##step1:eliminatetheinfluenceofpseudogenegrep-v"^#"GCF_001704415.1_ARS1_genomic.gff|......