CPU响应中断

CPU从中断控制器取得中断向量,然后根据具体的中断向量从中断向量表IDT中找到对应的表项,而该表项应该是一个中断门。这样,CPU就根据中断门的设置而到达了该通道的总服务程序入口。

由于中断是当CPU在用户空间运行时发生的,运行级别CPL为3,而中断服务程序属于内核,其运行级别CPL为0。所以CPU要从寄存器TR所指向的当前TSS中取出用于内核0级的堆栈指针,并把堆栈切换到内核堆栈,即当前进程的系统空间堆栈。

每次从系统空间返回到用户空间时堆栈指针一定回到其原点,“堆栈底部”。也就是说,当CPU从TSS中取出内核堆栈指针并切换到内核堆栈时,这个堆栈一定是空的。

所有公用中断请求的服务程序总入口是由gcc在预处理阶段生成的。它将一个中断请求号相关的数值压入堆栈(中断请求号减去256使其变成负数!)。

系统堆栈中的这个位置在因系统调用而进入内核时要用来存放系统调用号,而系统调用又与中断服务共用一部分子程序。这样,要有个手段来加以区分。

将一个整数装入一个通用寄存器之后,要判断它是否大于等于0很方便的,只要一条寄存器指令就可以了。而如果要与另一个常数相比较,就至少要多访问一次内存。

公共得跳转目标common_interrupt()是在include/asm-i386/hw_irq.h中定义的:

#define BUILD_COMMON_IRQ() \

asmlinkage void call_do_IRQ(void); \

__asm__( \

"\n" __ALIGN_STR"\n" \

"common_interrupt:\n\t" \

SAVE_ALL \

"pushl $ret_from_intr\n\t" \

SYMBOL_NAME_STR(call_do_IRQ)":\n\t" \

"jmp "SYMBOL_NAME_STR(do_IRQ));

其中SAVE_ALL,是保存现场,见arch/i386/kernel/entry.S中

#define SAVE_ALL \

cld; \

pushl %es; \

pushl %ds; \

pushl %eax; \

pushl %ebp; \

pushl %edi; \

pushl %esi; \

pushl %edx; \

pushl %ecx; \

pushl %ebx; \

movl $(__KERNEL_DS),%edx; \

movl %edx,%ds; \

movl %edx,%es;

在SAVE_ALL以后,又将一个程序标号入口ret_from_intr压入堆栈,并通过jmp指令转入另一段程序do_IRQ()。common_interrupt本质上不是函数,它们都没有与return相关得指令,所以从common_interrupt不能方回到某一个interrupt,可是,do_IRQ()却是一个函数。在通过jmp指令转入do_IRQ()之前将返回地址ret_from_intr压入堆栈就模拟了一次函数调用,仿佛对do_IRQ()的调用就发生在CPU进入ret_from_intr的的一条指令前夕一样。这样,当从do_IRQ()返回时就会“返回”到ret_from_intr继续执行。

do_IRQ()是在arch/i386/kernel/irq.c中定义的,

asmlinkage unsigned int do_IRQ(struct pt_regs regs)

调用参数是一个pt_regs数据结构,主要这是一个数据结构,不是指向数据结构得指针!也就是说,在堆栈中的返回地址以上的位置应该是一个数据结构的映象。

数据结构struct pt_regs是在include/asm-i386/ptrace.h中定义的:

struct pt_regs {

long ebx;

long ecx;

long edx;

long esi;

long edi;

long ebp;

long eax;

int xds;

int xes;

long orig_eax;

long eip;

int xcs;

long eflags;

long esp;

int xss;

};

(对照前面的SAVE_ALL来看这个结构!)

前面所做得一切,包括CPU在进入中断时自动做的,实际上都是在为do_IRQ()建立一个模拟得子程序调用环境,使得在do_IRQ()中既可以方便地知道进入中断前夕各个寄存器得内容,又可以在执行完毕后返回到ret_from_intr,并且从那里执行中断放回。

对系统堆栈的这种安排不光用于中断,还用于系统调用。

在IRQ0xXX_interrupt中把中断请求号相关的数值减去256压入堆栈的目的是使得在公共的中断处理程序中可以知道中断的来源。以IRQ3为例,3-265

= -253 =

0xffffff03,通过regs.orig_eax读出来并且把高位屏蔽掉,又得到0x03。由于do_IRQ()仅用于中断服务,所以不需要顾及系统调用时的情况。

do_IRQ()中调用spin_lock()加锁,是为了多处理器的情况而设置的。

desc->status的标志位IRQ_PENDING和IRQ_INPROGRESS是为了同一个CPU不允许中断服务套嵌,不同CPU不允许并发地进入同一个中断服务程序而设计的。

handle_IRQ_event()函数依次执行对列中的各个中断服务程序,让它们辨认本次中断请求是否来自各自的服务对象,即中断源,如果是就提供相应的服务。

action->flags标志位SA_INTERRUPT表示这个中断服务程序应该在开启中断的情况下执行。_sti()为开中断,_cli为关中断。标志位SA_SAMPLE_RANDOM表示服务程序要为系统引入一些随机性。

handle_IRQ_event()返回值status最低位必须为1。Force the "do bottom

halves" bit.

当do_IRQ()返回时,返回到ret_from_intr处,见

arch/i386/kernel/entry.S

ENTRY(ret_from_intr)

GET_CURRENT(%ebx)

ret_from_exception:

movl EFLAGS(%esp),%eax # mix EFLAGS and CS

movb CS(%esp),%al

testl $(VM_MASK | 3),%eax # return to VM86 mode or

non-supervisor?

jne ret_from_sys_call

jmp restore_all

即使跳转到ret_from_sys_call,最终还是会到restore_all

#define RESTORE_ALL \

popl %ebx; \

popl %ecx; \

popl %edx; \

popl %esi; \

popl %edi; \

popl %ebp; \

popl %eax; \

1: popl %ds; \

2: popl %es; \

addl $4,%esp; \

3: iret; \

这与SAVE_ALL是相互对应的。addl

$4,%esp将堆栈指针的当前值加4,是为了跳过ORIG_EAX,那是在进入中断之初压入堆栈的经过变形的中断请求号。

当CPU到达iret指令时,系统堆栈又恢复到刚进入中断门时的状态,而iret则使CPU从中断返回。

linux内核响应,读书笔记——Linux内核源代码情景分析——3.4  中断的响应和服务...相关推荐

  1. 鸟哥的Linux私房菜读书笔记:Linux磁盘与文件系统管理

    系统管理员很重要的任务之一就是管理好自己的磁盘文件系统, 每个分区不可太大也不能太小, 太大会造成磁盘容量的浪费, 太小则会产生文件无法储存的困扰. 前面谈到的文件权限与属性中, 这些权限与属性分别记 ...

  2. Linux内核源代码情景分析笔记

    Linux内核源代码情景分析笔记 好吧,首先我承认我要是读者的话,这篇文章我看着也头疼,因为写的太长太泛(其主要部分集中在内存管理,进程管理,文件系统)!原本是想按自己理解的精简精简的,按照操作系统中 ...

  3. Linux内核探秘读书笔记

    (Linux内核探秘(高剑林著 )读书笔记) Linux 文件系统探秘 一 文件系统基本概念 1.1 VFS     Linux内核通过虚拟文件系统(VFS)管理文件系统.     对每个具体文件系统 ...

  4. Linux 内核源代码情景分析(二)

    系列文章目录 Linux 内核设计与实现 深入理解 Linux 内核 Linux 设备驱动程序 Linux设备驱动开发详解 深入理解Linux虚拟内存管理 Linux 内核源代码情景分析(一) Lin ...

  5. Linux内核源代码情景分析-内存管理

    用户空间的页面有下面几种: 1.普通的用户空间页面,包括进程的代码段.数据段.堆栈段.以及动态分配的"存储堆". 2.通过系统调用mmap()映射到用户空间的已打开文件的内容. 3 ...

  6. Linux内核源代码情景分析-nanosleep()和pause()

    我们介绍nanosleep()和pause()两个系统调用. 系统调用nanosleep()在内核中的实现为sys_nanosleep(),代码如下: asmlinkage long sys_nano ...

  7. linux设备驱动读书笔记

    linux设备驱动读书笔记 设备驱动简介 机制:提供什么能力 策略:如何使用这些能力 在编写驱动时, 程序员应当编写内核代码来存取硬件, 但是不能强加特别的策略给用户, 因为不同的用户有不同的需求. ...

  8. linux设备驱动读书笔记(转)

    linux设备驱动读书笔记 设备驱动简介 机制:提供什么能力 策略:如何使用这些能力 在编写驱动时, 程序员应当编写内核代码来存取硬件, 但是不能强加特别的策略给用户, 因为不同的用户有不同的需求. ...

  9. [转]JFFS2源代码情景分析Beta2

    转载自:http://blog.csdn.net/flylonginsky/article/details/6321018 声明 你可以自由地随意修改本文档的任何文字内容及图表,但是如果你在自己的文档 ...

最新文章

  1. 年后跳槽BAT必看:10种数据结构、算法和编程课助你面试通关
  2. labview曲线上两点画延长线_教你用直尺画各种几何图形
  3. 正则表达式简明使用手册
  4. 网易云信阙杭宁:通过IM云让开发者共享网易经验
  5. 在Java 8 Lambda中创建自己的循环结构
  6. Signals Slots(Qt5)
  7. C语言------指针
  8. 新建远程仓库并推送项目
  9. Qemu架构解析(二)
  10. 大学计算机基础字母缩写大全,大学计算机基础缩写词.pdf
  11. 什么样的作品才能上抖音热门?
  12. 内存泄露分析工具linux,Linux下性能分析工具和内存泄露检测工具的简介(Valgrind和gprof)...
  13. pycharm的 crtl + r 使用正则表达式进行 request-header格式更改
  14. 数码宝贝 皇家骑士团(奥米加兽、金甲龙兽、红莲骑士兽、杜纳斯兽、剑皇兽、究极V龙兽、阿尔法兽、颅骨兽、八足马兽、芳香兽、艾可萨兽、顽固兽、杰斯兽)
  15. IT行业大致工作方向
  16. 读书笔记软件调试之道 :问题的核心-诊断
  17. torch.roll() 用法解读
  18. oracle增加表空间大小
  19. D4RL Benchmark 安装教程Ubuntu20.04
  20. web3.js 实现调用狐狸钱包完成用户登录

热门文章

  1. pypypy python_聊聊Python中的pypy
  2. 如何解决java乱码_java如何解决乱码
  3. 如何保证战略落地_战略如何规划落地?值得借鉴
  4. php超链接如何隐藏参数,php如何去除超链接
  5. 什么是线程?与进程又有什么区别,为什么要使用它,等对线程进行详细介绍
  6. Python字符串必须知道的7个函数
  7. python的init函数里参数的作用
  8. Python中的测试工具
  9. visual studio解决方案是什么?
  10. CUDA和cuDNN到底是啥关系?(cuDNN是基于CUDA的深度学习GPU加速库)