【Linux驱动】八、<异常>与<中断>的概念及处理流程
【Linux驱动】八、<异常>与<中断>的概念及处理流程
本文最后更新于331 天前,其中的信息可能已经过时,如有错误请发送邮件到273925452@qq.com

CPU中异常与中断

CPU 在运行的过程中,也会被各种“异常”打断。这些“异常”有:

  1. SWI(软中断)
  2. 快中断
  3. 中断(中断也属于一种“异常”,导致中断发生的情况有很多,比如:)
  • 按键
  • 定时器
  • ADC 转换完成
  • UART 发送完数据、收到数据

这些众多的“中断源”,汇集到“中断控制器”,由“中断控制器”选择优先级最高的中断并通知 CPU。


中断的处理流程

arm 对异常(中断)处理过程:

  1. 初始化:
    1. 设置中断源,让它可以产生中断
    2. 设置中断控制器(可以屏蔽某个中断,优先级)
    3. 设置 CPU 总开关(使能中断)
  2. 执行其他程序:正常程序
  3. 产生中断:比如按下按键—>中断控制器—>CPU
  4. CPU 每执行完一条指令都会检查有无中断/异常产生
  5. CPU 发现有中断/异常产生,开始处理。
  6. 这些函数做什么事情?
    1. 软件做的:
  • 保存现场(各种寄存器)
  • 处理异常(中断):分辨中断源,再调用不同的处理函数
  • 恢复现场

异常向量表

/* _start标签定义了程序的入口点 */
_start :
    b reset                                 /* 复位处理程序的跳转地址 */    
    ldr pc, _undefined_instruction          /* 未定义指令处理程序的跳转地址 */    
    ldr pc, _software_interrupt             /* 软件中断处理程序的跳转地址 */    
    ldr pc, _prefetch_abort                 /* 预取指令异常处理程序的跳转地址 */  
    ldr pc, _data_abort                      /* 数据异常处理程序的跳转地址 */    
    ldr pc, _not_used                        /* 未使用处理程序的跳转地址 */    
    ldr pc, _irq                            /* 中断请求(IRQ)处理程序的跳转地址 */  
    ldr pc, _fiq                              /* 快速中断请求(FIQ)处理程序的跳转地址 */

Linux 系统对中断的处理

进程、线程、中断的核心:栈

中断中断,中断谁?
中断当前正在运行的进程、线程。
进程、线程是什么?内核如何切换进程、线程、中断?
要理解这些概念,必须理解栈的作用。

ARM 处理器程序运行的过程

ARM 芯片属于精简指令集计算机(RISC:Reduced Instruction SetComputing),它所用的指令比较简单,有如下特点:
1.对内存只有读、写指令
2.对于数据的运算是在 CPU 内部实现
3.使用 RISC 指令的 CPU 复杂度小一点,易于设计
4.比如对于 a=a+b 这样的算式,需要经过下面 4 个步骤才可以实现:
细看这几个步骤,有些疑问:
1.读 a,那么 a 的值读出来后保存在 CPU 里面哪里?
2.读 b,那么 b 的值读出来后保存在 CPU 里面哪里?
3.a+b 的结果又保存在哪里?
cpu
CPU 运行时,先去取得指令,再执行指令:
1.把内存 a 的值读入 CPU 寄存器 R0
2.把内存 b 的值读入 CPU 寄存器 R1
3.把 R0、R1 累加,存入 R0
4.把 R0 的值写入内存 a

程序被中断时,怎么保存现场

保存现场
从上图可知,CPU 内部的寄存器很重要,如果要暂停一个程序,中断一个程序,就需要把这些寄存器的值保存下来:这就称为保存现场。
保存在哪里?内存,这块内存就称之为栈。
程序要继续执行,就先从栈中恢复那些 CPU 内部寄存器的值。
这个场景并不局限于中断,下图可以概括程序 A、B 的切换过程,其他情况是类似的:
  1. 函数调用
    1. 在函数 A 里调用函数 B,实际就是中断函数 A 的执行。
    2. 那么需要把函数 A 调用 B 之前瞬间的 CPU 寄存器的值,保存到栈里;
    3. 函数 B 返回之后,就从栈中恢复函数 A 对应的 CPU 寄存器值,继续执行
  2. 中断处理
    1. 进程 A 正在执行,这时候发生了中断。
    2. CPU 强制跳到中断异常向量地址去执行,
    3. 这时就需要保存进程 A 被中断瞬间的 CPU 寄存器值,
    4. 可以保存在进程 A 的内核态栈,也可以保存在进程 A 的内核结构体中。
    5. 中断处理完毕,要继续运行进程 A 之前,恢复这些值。
  3. 进程切换
    1. 在所谓的多任务操作系统中,我们以为多个程序是同时运行的。
    2. 如果我们能感知微秒、纳秒级的事件,可以发现操作系统时让这些程序依次执行一小段时间,进程 A 的时间用完了,就切换到进程 B。
    3. 怎么切换?
    4. 切换过程是发生在内核态里的,跟中断的处理类似。
    5. 进程 A 的被切换瞬间的 CPU 寄存器值保存在某个地方;
    6. 恢复进程 B 之前保存的 CPU 寄存器值,这样就可以运行进程 B 了。

所以,在中断处理的过程中,伴存着进程的保存现场、恢复现场。进程的调度也是使用栈来保存、恢复现场:


进程、线程的概念

进程、线程
在 Linux 中:资源分配的单位是进程,调度的单位是线程。也就是说,在一个进程里,可能有多个线程,这些线程共用打开的文件句柄、全局变量等等。
而这些线程,之间是互相独立的,“同时运行”,也就是说:每一个线程,都有自己的栈。如下图示:

中断处理原则

1:不能嵌套

在 Linux 系上中断无法嵌套:即当前中断 A 没处理完之前,不会响应另一个中断 B(即使它的优先级更高)。

2:越快越好

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


💡商业转载请联系作者获得授权,非商业转载请注明出处。
协议(License):署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)。
使用这些免费资源的时候应遵守版权意识,切勿非法利用,售卖,尊重原创内容知识产权。未经允许严禁转载。

评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇