Linux中的中断机制 2

2023-11-09

Linux中的中断机制  2009-06-11 23:03:44

分类: LINUX

Linux中的中断机制

X86里面中断发生时CPU控制单元工作流程(也就是硬件需要做的事情),在CPU执行下一条instruction之前,首先判断有没有发生异常和中断。如果发生了,那么进行一下步骤:

1.       查看所发生的中断(异常)向量i。(0-255)

2.       根据idtr寄存器读取IDT中第i个入口地址。

3.       根据gdtr寄存器读取GDT中关于IDT的段描述符,这个段描述符指定了中断(异常)程序的基地址。

(IDT表项是64位的,这64位里面包含了一些属性信息和一个48位的指针,48位的指针中16位是一个段选择子,另外32位是段内偏移,系统会用这16位的段选择子在GDT中找到对应的段,然后把段首地址跟IDT中的32位偏移相加就得到了中断处理函数的入口地址。ULK3中所说的GDTDPL就是用那16位段选择子在GDT中找到的段描述符的DPL

也就是说IDT表项里面的这个段选择子标识着这个中断服务程序的运行级别,用这个级别来对比当前cs中的运行级别。)

4.       证明中断是可信源发出的,检查CPL与GDT里面对应的DPL,如果CPL<DPL,就发出一个通用保护异常,这可以阻止用户进程访问指定的中断。

5.       如果优先级发生改变了,也即是说CPL和DPL不一致的情况下,那么控制单元就会使用新的优先级的堆栈:(1)通过tr寄存器,读取TSS段描述符(2)通过TSS,读取对应的ss和esp。(3)在新的堆栈里面存储老优先级对应的堆栈ss和esp。

6.       如果fault发生,那么会去除cs和eip,再执行一次。

7.       将eflags,cs,eip入栈。

8.       如果异常产生了一个硬件错误码,那么将其入栈。

9.       在IDT对应的门中,取出cs和eip,并且执行。

 

当中断处理完成,硬件控制器的工作如下:

1.       将cs,eip,eflags出栈。(如果有硬件错误码,则必须在iret指令之前pop出去,软件实行)。

2.       检查中断程序的CPL和当前cs(注意这里已经是以前老得代码段cs)是否一致,如果一致,直接执行iret。否则跳到下一步。

3.       将ss和esp出栈,从而将堆栈置为以前的优先级。

4.       检查ds,es,fs和gs,如果优先级高,则清零这些寄存器,为了防止用户进程进入内核空间。

 

如何设置IDT里面的中断门:

1.       函数set_intr_gate:

/*将中断处理函数地址加入到IDT的第n个入口处*/

void set_intr_gate(unsigned int n, void *addr)

{

       _set_gate(idt_table+n,14,0,addr,__KERNEL_CS);

}

2.       宏_set_gate:

/*第一个参数是IDT里面第n个入口的地址

第二个参数是中断门里面的type字段的设置

第三个参数是dpl值

第四个参数是函数地址

第五个参数是指定段,都为__KERNEL_CS */

#define _set_gate(gate_addr,type,dpl,addr,seg) \

do { \

  int __d0, __d1; \

  __asm__ __volatile__ ("movw %%dx,%%ax\n\t" \

       "movw %4,%%dx\n\t" \

       "movl %%eax,%0\n\t" \

       "movl %%edx,%1" \

       :"=m" (*((long *) (gate_addr))), \

        "=m" (*(1+(long *) (gate_addr))), "=&a" (__d0), "=&d" (__d1) \

       :"i" ((short) (0x8000+(dpl<<13)+(type<<8))), \

        "3" ((char *) (addr)),"2" ((seg) << 16)); \

} while (0)

 

 

当进入中断处理程序以后了,linux会怎么做呢,请看下面的代码:

/*

 * Build the entry stubs and pointer table with

 * some assembler magic.

 */

/*中断入口处,interrupt[n]*/

 

.data

ENTRY(interrupt)

.text

 

vector=0

ENTRY(irq_entries_start)

.rept NR_IRQS

       ALIGN

1:     pushl $vector-256               /*将中断号进行压栈到内核栈中*/

       jmp common_interrupt          

.data

       .long 1b

.text

vector=vector+1

.endr

 

       ALIGN

common_interrupt:

       SAVE_ALL                  /*压栈寄存器的值,注意eflags,cs,eip在前面已经压栈了*/

       movl %esp,%eax              /*将堆栈栈顶地址作为参数传给do_IRQ函数*/

       call do_IRQ      

       jmp ret_from_intr

 

每个中断向量程序都对应着一个数据结构,里面包含着处理中断程序的各个流程和状态的转换。这个数据结构如下:

/*

 * This is the "IRQ descriptor", which contains various information

 * about the irq, including what kind of hardware handling it has,

 * whether it is disabled etc etc.

 *

 * Pad this out to 32 bytes for cache and indexing reasons.

 */

typedef struct irq_desc {

       hw_irq_controller *handler;  /*对应PIC对象,用于处理IRQ line上的各种交互*/

       void *handler_data;

       struct irqaction *action; /* IRQ action list */  /*ISR链表的第一个地址*/

       unsigned int status;        /* IRQ status */  /*对应IRQ line的状态*/

       unsigned int depth;        /* nested irq disables */ /*如果没有嵌套,则为0*/

       unsigned int irq_count;          /* For detecting broken interrupts */

       unsigned int irqs_unhandled;

       spinlock_t lock;

} ____cacheline_aligned irq_desc_t;

 

extern irq_desc_t irq_desc [NR_IRQS];

 

下面我们来看看字段status的各个状态的意义,理解这些状态对理解状态转换的过程和整个中断处理流程有很大的关系。

/*

 * IRQ line status.

 */

#define IRQ_INPROGRESS  1     /* IRQ handler active - do not enter! */

                                                 /*说明IRQ handler正在运行*/

#define IRQ_DISABLED       2     /* IRQ disabled - do not enter! */

                                                 /*说明IRQ是关闭的*/

#define IRQ_PENDING 4     /* IRQ pending - replay on enable */

/*说明IRQ出现在line上,并且已经发了ACK,但是内核并没有对此IRQ进行服务*/

#define IRQ_REPLAY    8     /* IRQ has been replayed but not acked yet */

/*IRQ line被禁止了,但是上一个IRQ还没有给PIC发送ACK*/

#define IRQ_AUTODETECT 16    /* IRQ is being autodetected */

                                                 /*内核正使用IRQ line来检查硬件设备*/

#define IRQ_WAITING 32    /* IRQ not yet seen - for autodetection */

#define IRQ_LEVEL      64    /* IRQ level triggered */

#define IRQ_MASKED  128  /* IRQ masked - shouldn't be seen again */

#define IRQ_PER_CPU  256  /* IRQ is per CPU */

 

IRQ_DISABLED与字段里面的depth用来觉得IRQ是开启还是关闭的。

参看disable_irq()和enable_irq()函数。

可以通过depth的值来通知状态是否为IRQ_DISABLED,并且告知PIC是否开启或者关闭IRQ。

下面又看看action这个字段,这个是一个irqaction类型。

struct irqaction {

       irqreturn_t (*handler)(int, void *, struct pt_regs *); /*指向特定I/O设备ISR的指针*/

       unsigned long flags; /*描述IRQ line和I/O设备的关系,参加下面介绍*/

       cpumask_t mask;       /*not used*/

       const char *name;  /*IO设备的名字*/

       void *dev_id;     

       struct irqaction *next;    /**/

       int irq;            /*IRQ line*/

       struct proc_dir_entry *dir;

};

 

 

下面来看看__do_IRQ函数的处理方式

/*

 * do_IRQ handles all normal device IRQ's (the special

 * SMP cross-CPU interrupts have their own specific

 * handlers).

 */

fastcall unsigned int __do_IRQ(unsigned int irq, struct pt_regs *regs)

{

       irq_desc_t *desc = irq_desc + irq;

       struct irqaction * action;

       unsigned int status;

 

       spin_lock(&desc->lock);         /*锁住这个IRQ*/

       desc->handler->ack(irq);         /*给PIC发送这个IRQ line的ACK*/

       /*

        * REPLAY is when Linux resends an IRQ that was dropped earlier

        * WAITING is used by probe to mark irqs that are being tested

        */

       status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING);

/*发送了ACK但没有处理,这个情况下,应置status为pending*/

       status |= IRQ_PENDING; /* we _want_ to handle it */

 

       /*

        * If the IRQ is disabled for whatever reason, we cannot

        * use the action we have.

        */

    /*如果中断正在被处理或者被禁止了,那么就退出*/

       action = NULL;

       if (likely(!(status & (IRQ_DISABLED | IRQ_INPROGRESS)))) {

              action = desc->action;

              status &= ~IRQ_PENDING; /* we commit to handling */

              status |= IRQ_INPROGRESS; /* we are handling it */

       }

       desc->status = status;

 

       /*

        * If there is no IRQ handler or it was disabled, exit early.

        * Since we set PENDING, if another processor is handling

        * a different instance of this same irq, the other processor

        * will take care of it.

        */

       if (unlikely(!action))

              goto out;

 

       /*

        * Edge triggered interrupts need to remember

        * pending events.

        * This applies to any hw interrupts that allow a second

        * instance of the same irq to arrive while we are in do_IRQ

        * or in the handler. But the code here only handles the _second_

        * instance of the irq, not the third or fourth. So it is mostly

        * useful for irq hardware that does not mask cleanly in an

        * SMP environment.

        */

       for (;;) {

              irqreturn_t action_ret;

/*在处理IRQ_evnet的时候,把锁打开,这样其他的处理器也可以处理同样类型的中断,不过这些中断最后还是要被当前处理器处理完成*/

              spin_unlock(&desc->lock);

 

              action_ret = handle_IRQ_event(irq, regs, action);

 

              spin_lock(&desc->lock);

              if (!noirqdebug)

                     note_interrupt(irq, desc, action_ret);

if (likely(!(desc->status & IRQ_PENDING)))  /*如果中断状态不为pending的话就退出,其他处理器试图去处理中断的话就会置这个中断状态为pending,这样就会在此处理器中继续处理。*/

                     break;

              desc->status &= ~IRQ_PENDING;

       }

       desc->status &= ~IRQ_INPROGRESS;

 

out:

       /*

        * The ->end() handler has to deal with interrupts which got

        * disabled while the handler was running.

        */

       desc->handler->end(irq);

       spin_unlock(&desc->lock);

 

       return 1;

}

 

下面来看handle_IRQ_event函数,这个函数的目的是处理IRQ上面的各个ISR

/*

 * Have got an event to handle:

 */

fastcall int handle_IRQ_event(unsigned int irq, struct pt_regs *regs,

                            struct irqaction *action)

{

       int ret, retval = 0, status = 0;

 

       if (!(action->flags & SA_INTERRUPT))

              local_irq_enable();

 

       do {

              ret = action->handler(irq, action->dev_id, regs);

              if (ret == IRQ_HANDLED)

                     status |= action->flags;

              retval |= ret;

              action = action->next;

       } while (action);

 

       if (status & SA_SAMPLE_RANDOM)

              add_interrupt_randomness(irq);

       local_irq_disable();

 

       return retval;

}

 

现在我们来看看设备怎么动态的获取到IRQ lines:

int request_irq(unsigned int irq,

              irqreturn_t (*handler)(int, void *, struct pt_regs *),

              unsigned long irqflags, const char * devname, void *dev_id)

这个函数,irq是中断号,handler是中断服务程序,用来将handler注册到这个IRQ line上。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux中的中断机制 2 的相关文章

  • 01信号学习之信号的概念于机制

    1 信号的相关认知 1 信号的概念 传播信息的方法 所以它是信息发送的标志 2 信号的机制 A给B发送信号 B收到信号之前执行自己的代码 收到信号后 不管执行到程序的什么位置 都要暂停运行 去处理信号 处理完毕再继续执行 与硬件中断类似 异
  • make编译内核驱动模块

    make编译内核驱动模块 方法一 obj m hello world o KDIR home july7 prj Firefly RK3308 kernel 内核源码路径 PWD shell pwd 获取当前目录的变量 all make C
  • 解析 Linux 内核可装载模块的版本检查机制

    解析 Linux 内核可装载模块的版本检查机制 王 华东 系统工程师 自由职业者 简介 为保持 Linux 内核的稳定与可持续发展 内核在发展过程中引进了可装载模块这一特性 内核可装载模块就是可在内核运行时加载到内核的一组代码 通常 我们会
  • linux下如何清理缓存

    手头的路由本身内存就比较少 上面又跑了一个nginx php的环境 简直慢的爆炸 check的时候发现内存经常被占了很多 linux的虚拟内存机制 很多时候回导致内存得不到及时释放 有时候内存很少了 kill了很多进程 但是内存还是没有释放
  • 操作系统课程设计3_系统调用

    一 实验目的 1 学习怎样重新编译 Linux 内核 2 理解 掌握 Linux 标准内核和发行版本内核的区别 二 实验内容 1 通过重新编译Linux来实现系统调用 2 通过增加模块来实现系统调用 三 实验步骤和结果 一 通过重新编译内核
  • Linux mmap系统调用视角看缺页中断

    问题 1 mmap具体是怎么实现比read write少一次内存copy的 2 mmap共享映射和私有映射在内核实现的时候到底有什么区别 3 mmap的文件映射和匿名映射在内核实现的时候到底有什么区别 4 父子进程的COW具体怎么实现的 概
  • Linux中的中断机制 2

    Linux中的中断机制 2009 06 11 23 03 44 分类 LINUX Linux中的中断机制 X86里面中断发生时CPU控制单元工作流程 也就是硬件需要做的事情 在CPU执行下一条instruction之前 首先判断有没有发生异
  • redis的多路复用原理

    redis服务端对于命令的处理是单线程的 但是在I O层面却可以同时面对多个客户端并发的提供服务 并发到内部单线程的转化通过多路复用框架实现 一个IO操作的完整流程是数据请求先从用户态到内核态 也就是操作系统层面 然后再调用操作系统提供的a
  • Source Insight给Linux内核创建工程

    所有文档请关注公众号 一口Linux 后台回复 ubuntu linux驱动视频同步更新到 https live bilibili com 22719960 一 Source Insight安装 1 预先准备好 Source Insight
  • Linux的IO端口和IO内存

    Linux的IO端口和IO内存 分类 linux编程 2011 01 14 13 22 866人阅读 评论 1 收藏 举报 io linux linux内核 struct 平台 x86 CPU对外设端口物理地址的编址方式有两种 一种是IO映
  • 关于 Linux 之父,你可能不知道的 7 件事

    如果让你现在说出三个程序员的名字 Linus 很可能就在其中 作为世界上最著名的电脑程序员 黑客之一 Linus Benedict Torvalds 写出了 Linux 内核 1 0 版 发起了开源运动 开发了代码管理工具 Git 这三个成
  • uboot以tag方式给内核传参

    1 tag方式传参 1 struct tag tag是一个数据结构 在uboot和linux kernel中都有定义tag数据机构 而且定义是一样的 2 tag header和tag xxx tag header中有这个tag的size和类
  • 一文讲透!Windows内核 & x86中断机制详解

    搞内核研究的经常对中断这个概念肯定不陌生 经常我们会接触很多与中断相关的术语 按照软件和硬件进行分类 硬件CPU相关 IRQ IDT cli sti 软件操作系统相关 APC DPC IRQL 一直以来对中断这一部分内容弄的一知半解 操作系
  • jffs2:You cannot use older JFFS2 filesystems with newer kernels错误

    jffs2 You cannot use older JFFS2 filesystems with newer kernels错误 原因 可能在于交叉编译linux内核时 没有打开jffs2系统的使能开关 解决方法 在内核文件中执行make
  • Linux用户空间与内核空间

    Linux用户空间与内核空间 2012 08 30 15 39 1969人阅读 评论 1 收藏 举报 linux linux内核 struct user system allocation Linux 操作系统和驱动程序运行在内核空间 应用
  • late_initcall和module_init

    late initcall和module init 分类 linux驱动程序设计 2012 11 04 15 14 3680人阅读 评论 0 收藏 举报 所有的 init函数在区段 initcall init中还保存了一份函数指针 在初始化
  • MS-RTOS 内核模块动态装载

    1 MS RTOS 内核模块动态装载简介 MS RTOS 支持内核模块动态装载功能 用户可以根据需要 在不需要对内核重新编译的情况下 使用 insmod rmmod 等命令动态地将模块加入或移出内核 这样可以提高 MS RTOS 的灵活性
  • 字符设备驱动相关函数

    Linux内核中 a 使用cdev结构体来描述字符设备 b 通过其成员dev t来定义设备号 分为主 次设备号 以确定字符设备的唯一性 c 通过其成员file operations来定义字符设备驱动提供给VFS的接口函数 如常见的open
  • 深入解决Linux内存管理之page fault处理

    说明 Kernel版本 4 14 ARM64处理器 Contex A53 双核 使用工具 Source Insight 3 5 Visio 1 概述 内核实现只是在进程的地址空间建立好了vma区域 并没有实际的虚拟地址到物理地址的映射操作
  • 基于树莓派博通BCM2835芯片手册导读写编简单引脚驱动代码编译和测试(树莓派)

    编写引脚驱动代码 这边写的是17引脚的驱动代码代码 IO口控制的代码在下面 这边只是简单的代码 驱动代码 include

随机推荐

  • C语言交换 a与b 的值的 3种方法

    第一种方法 给定两个整形变量的值 创建两个临时变量将两个值的内容进行交换 int a 2 int b 3 int c 0 printf 原来的 na d b d n a b c a a b b c printf 现在的 na d b d a
  • Unity中如何用代码实现场景切换

    Unity中如何用代码实现场景切换 创建场景 场景切换 Unity3D创建游戏可以这么理解 一款完整的游戏就是一个Project 项目工程 游戏中不同的地图对应的是项目下面的不同场景 Scene 一款游戏可以包含很多地图 因此一个项目工程下
  • 浏览器及手机版本型号判断

    Navigator 对象 包含有关浏览器的信息 所有浏览器都支持该对象 对象属性参考 https www w3school com cn jsref dom obj navigator asp 属性 描述 appCodeName 返回浏览器
  • 【一些用得到的概念】

    C语言的四种变量类型 C C 四种变量类型 变量声明 包括const变量能被其他文件使用的操作 关于变量声明 多文件的例子 Linux下使用生成 so并调用 关于 so调用出现错误的解释
  • 老卫带你学---华为机试(7.取近似值)

    华为机试 7 取近似值 问题 题目描述 写出一个程序 接受一个正浮点数值 输出该数值的近似整数值 如果小数点后数值大于等于5 向上取整 小于5 则向下取整 输入描述 输入一个正浮点数值 输出描述 输出该数值的近似整数值 示例1 输入 5 5
  • typora的基本使用技巧汇总

    原文链接 https www jianshu com p 380005c8f104 Typora是一款所见即所得的Markdown文本编辑工具 在你输入相应的标记符号后 系统会自动对所标记的文本进行渲染 设置成相应的格式 因此 写作过程与渲
  • matlab由频率响应计算差分方程,现代线性系统:使用MATLAB

    中译本出版者的话 译者的话 出版者的话 符号一览表 全书内容简介 前 言 第1章 信号与序列 概述 基本概念与解说题 信号 序列和系统 IP1 1 描述连续时间信号 IP1 2 序列表述 连续和离散信号之间的转换 采样定理 本书梗概 兼学习
  • PyTorch深度学习实战(6)——神经网络性能优化技术

    PyTorch深度学习实战 6 神经网络性能优化技术 0 前言 1 数据准备 1 1 数据集分析 1 2 数据集加载 2 使用 PyTorch 训练神经网络 2 1 神经网络训练流程 2 2 PyTorch 神经网络训练 3 缩放数据集 4
  • 可以白嫖的语音识别开源项目whisper的搭建详细过程

    原文来自我个人的博客 1 前提条件 服务器为GPU服务器 点击这里跳转到我使用的GPU服务器 我搭建 whisper 选用的是 NVIDIA A 100显卡 4GB显存 Python版本要在3 8 3 11之间 输入下面命令查看使用的Pyt
  • origin图上显示数据标签_origin中如何在图中添加标签

    展开全部 方法步骤如下 1 首先打开计算机 在计算机内进入origin 在里面新建一个三维表面图与标注数据点的初始数据 62616964757a686964616fe58685e5aeb931333431353866 2 选中所有数据 执行
  • element ui el-date-picker 组件默认值

    element ui el date picker 组件默认当前月份 1 html 代码
  • 已解决(Python爬虫requests库报错 请求异常SSL错误,证书认证失败问题)requests.exceptions.SSLError: HTTPSConnectionPool

    成功解决 Python爬虫requests库报错 请求异常 SSL错误 证书认证失败问题 requests exceptions SSLError HTTPSConnectionPool host httpbin org port 443
  • [html+css+js] 小米官网首页制作

    实现效果 源码及图片素材地址 https gitee com jie shao1112 xiaomihttps gitee com jie shao1112 xiaomi 这里进行一些说明 在index html里引入了三个css文件 第一
  • QT 界面强制刷新

    针对qt的界面刷新显示 在qwidget中可以使用 show exec setup repaint paintEvent 等都可以实现界面刷新和重新绘制 但是如果需要在控件外进行刷新就需要下面方法 include
  • HDMI CEC协议简介

    一 概述 1 HDMI HDMI High Definition Multimedia Interface 高清多媒体接口 是一种专用的音频 视频接口 用于发送未压缩的视频数据和压缩 未压缩的音频数据 HDMI是模拟视频标准的数字替代品 H
  • STM32 W5500 MQTT Client 发布订阅及断线重连

    使用STM32 W5500做MQTT Client 使得数据上传broker 并接收broker传来的消息 并支持断网 拔网线再插入网线能够重新连接broker这样的功能 需要具备以下条件 1 STM32 W5500基础入网配置 使能PC电
  • IT界大佬告诉你,程序员接私活的7大平台利器

    今天小编在网上汇集了一些国内程序员接私活的平台 希望对大家能够有用 1 程序员客栈 程序员的经纪人 如果有想学习java的程序员 可来我们的java学习扣qun 94311 1692免费送java的视频教程噢 我整理了一份适合18年学习的j
  • Haproxy负载均衡和动静分离配置

    Haproxy Haproxy安装 yum y install haproxy 配置负载均衡 frontend ds 80 定义前端服务器名为ds 监听地址为所有ip的80端口 default backend webservers 默认请求
  • SSRF(服务端请求伪造)

    SSRF Server side Request Forge 服务端请求伪造 1 概念 它是一种由攻击者构造形成由服务端发起请求的一个安全漏洞 一般情况下 SSRF攻击的目标是从外网无法访问的内部系统 正是因为它是由服务端发起的 所以它能够
  • Linux中的中断机制 2

    Linux中的中断机制 2009 06 11 23 03 44 分类 LINUX Linux中的中断机制 X86里面中断发生时CPU控制单元工作流程 也就是硬件需要做的事情 在CPU执行下一条instruction之前 首先判断有没有发生异