Linux内核中的中断栈与内核栈的补充说明【转】
转自:http://blog.chinaunix.net/uid-12461657-id-3487463.html
原文地址:Linux内核中的中断栈与内核栈的补充说明 作者:MagicBoy2010
本贴在这个基础上对内核栈与中断栈的话题做些补充,讨论基于x86 32位系统,因为64位系统下Linux内核关于栈的支持原理上是相同的,不过也有些特性属于64位特有的,比如IST(Interrupt Stack Table),如果可能将来会在processor版块发个帖子专门讨论。
1. x86下内核栈与中断栈是否共享的问题
我们知道Linux系统下每个用户进程都有个task_struct对象来表示,同时在处理器层面还对应一个TSS(Task State Segment),当中断发生时,用户进程或者处于用户态(ring 3)或者处于内核态(ring 0),如果是在用户态,那么会发生栈的切换问题,也就是会切换到内核态的栈,如果是在内核态,那么就没有栈切换的问题。但是x86处理器在ring 0上只有一个ESP,这意味着中断发生后,只能使用一个栈,这个栈就是内核栈(kernel stack)。处理器的硬件逻辑会将被中断进程的下条指令(CS,EIP)以及EFLAG压入栈,当然如果发生用户态栈向内核态栈的切换,处理器还会把用户态的(SS, ESP)也压入栈,此时使用的就是内核栈。这个行为属于处理器的硬件逻辑范畴,不是系统软件的行为。
至于x86下内核栈与中断栈是否共享的问题,其实是个内核设计的问题,换言之,中断栈可与内核栈共享,也可重新分配一个独立的中断栈。2.4的内核版本似乎采用中断栈与内核栈共享的设计,因为这种设计的好处是代码相对简单,如前所述,直接使用ESP0就可以了,但是负面因素是中断栈如果发生嵌套,可能破坏内核栈的一些数据,因为毕竟共享,所以栈空间有时候难免会捉襟见肘。所以在2.5内核版本开发中,来自IBM的一位大侠曾提交过一个补丁(详见http://lwn.net/Articles/21846/),试图在中断发生时,从内核栈switch到一个独立的中断栈中,后来也不知道被内核社区采纳了没有,总之我现在在3.2的内核源码中没有看到那位仁兄的补丁代码了,当然也可能是那个补丁已经长成现在的代码样子了。
现在的Linux内核中采用的是内核栈与中断栈分离的设计,下面我们从源码层面来看一看这种分离是如何完成的。
内核栈与中断栈分离的核心代码发生在do_IRQ() --> handle_irq() --> execute_on_irq_stack()
最后一个函数字面上的意思大约是在中断栈中执行中断处理例程,也就是说中断的处理函数会在独立于被中断进程的上下文中执行。execute_on_irq_stack的函数实现为:
<arch/x86/kernel/irq_32.c>
- static inline int
- execute_on_irq_stack(int overflow, struct irq_desc *desc, int irq)
- {
- union irq_ctx *curctx, *irqctx;
- u32 *isp, arg1, arg2;
- curctx = (union irq_ctx *) current_thread_info();
- irqctx = __this_cpu_read(hardirq_ctx);
- /*
- * this is where we switch to the IRQ stack. However, if we are
- * already using the IRQ stack (because we interrupted a hardirq
- * handler) we can't do that and just have to keep using the
- * current stack (which is the irq stack already after all)
- */
- if (unlikely(curctx == irqctx))
- return 0;
- /* build the stack frame on the IRQ stack */
- isp = (u32 *) ((char *)irqctx + sizeof(*irqctx));
- irqctx->tinfo.task = curctx->tinfo.task;
- irqctx->tinfo.previous_esp = current_stack_pointer;
- /*
- * Copy the softirq bits in preempt_count so that the
- * softirq checks work in the hardirq context.
- */
- irqctx->tinfo.preempt_count =
- (irqctx->tinfo.preempt_count & ~SOFTIRQ_MASK) |
- (curctx->tinfo.preempt_count & SOFTIRQ_MASK);
- if (unlikely(overflow))
- call_on_stack(print_stack_overflow, isp);
- asm volatile("xchgl %%ebx,%%esp \n"
- "call *%%edi \n"
- "movl %%ebx,%%esp \n"
- : "=a" (arg1), "=d" (arg2), "=b" (isp)
- : "0" (irq), "1" (desc), "2" (isp),
- "D" (desc->handle_irq)
- : "memory", "cc", "ecx");
- return 1;
- }
代码中的curctx=(union irq_ctx *) current_thread_info()用来获得当前被中断进程的上下文,irqctx = __this_cpu_read(hardirq_ctx)用来获得hardirq的上下文,其实就是获得独立的中断栈起始地址。中断栈的大小与layout与内核栈是完全一样的。接下来isp指向中断栈栈顶,最后的堆栈切换发生在那段汇编代码中:当前进程的内核栈ESP指针保存在EBX中,而中断栈的isp则赋值给了ESP,这样接下来的代码就将使用中断栈了。call语句负责调用desc->handle_irq()函数,这里会进行中断处理,设备驱动程序注册的中断处理函数会被调用到。当中断处理例程结束返回时,ESP将重新指向被中断进程的内核栈。(此处我们应该注意到内核栈中还保留着中断发生时处理器硬件逻辑所压入的CS, EIP等寄存器,所以在内核栈中做中断返回是完全正确的)。
2. 中断栈的分配
独立的中断栈所在内存空间的分配发生在arch/x86/kernel/irq_32.c的irq_ctx_init函数中(如果是多处理器系统,那么每个处理器都会有一个独立的中断栈),函数使用__alloc_pages在低端内存区分配2个物理页面(2的THREAD_ORDER次方),也就是8KB大小的空间。有趣的是,这个函数还会为softirq分配一个同样大小的独立堆栈,如此说来,softirq将不会在hardirq的中断栈上执行,而是在自己的上下文中执行。
总结一下,系统中每个进程都会拥有属于自己的内核栈,而系统中每个CPU都将为中断处理准备了两个独立的中断栈,分别是hardirq栈和softirq栈。草图如下:
最后,关于设备驱动程序的中断处理例程中调用可能引起阻塞函数的问题,可以简单归结为在中断处理上下文中能否进行调度的问题。现实中,绝对不应该这样做,因为这会引起很多问题。但是从理论实现的角度,如果调度器愿意,它找到被中断进程的上下文并不存在技术上的障碍,这意味着在中断处理函数中如果发生进程切换,被中断进程被再次调度是可能的,如果调度器愿意这么做的话。
(原文首发:http://www.embexperts.com/forum.php/forum.php?mod=viewthread&tid=499&extra=page%3D1,略有改动)
Linux内核中的中断栈与内核栈的补充说明【转】的更多相关文章
- Linux内核中的中断
http://blog.csdn.net/weiqing1981127/article/details/8298585 中断处理程序是被内核调用来响应中断的,它运行在中断上下文,中断处理程序是上半部, ...
- 向linux内核中添加外部中断驱动模块
本文主要介绍外部中断驱动模块的编写,包括:1.linux模块的框架及混杂设备的注册.卸载.操作函数集.2.中断的申请及释放.3.等待队列的使用.4.工作队列的使用.5.定时器的使用.6.向linux内 ...
- linux内核中打印栈回溯信息 - dump_stack()函数分析【转】
转自:http://blog.csdn.net/jasonchen_gbd/article/details/45585133 版权声明:本文为博主原创文章,转载请附上原博链接. 目录(?)[-] ...
- linux内核分析笔记----中断和中断处理程序【转】
转自:http://www.cnblogs.com/hanyan225/archive/2011/07/17/2108609.html 中断还是中断,我讲了很多次的中断了,今天还是要讲中断,为啥呢?因 ...
- linux内核分析笔记----中断和中断处理程序
中断还是中断,我讲了很多次的中断了,今天还是要讲中断,为啥呢?因为在操作系统中,中断是必须要讲的.. 那么什么叫中断呢, 中断还是打断,这样一说你就不明白了.唉,中断还真是有点像打断.我们知道linu ...
- TCP/IP协议栈在Linux内核中的运行时序分析
网络程序设计调研报告 TCP/IP协议栈在Linux内核中的运行时序分析 姓名:柴浩宇 学号:SA20225105 班级:软设1班 2021年1月 调研要求 在深入理解Linux内核任务调度(中断处理 ...
- Linux内核中的fastcall和asmlinkage宏
代码中看见:#define _fastcall 所以了解下fastcall -------------------------------------------------------------- ...
- Linux内核中的GPIO系统之(3):pin controller driver代码分析
一.前言 对于一个嵌入式软件工程师,我们的软件模块经常和硬件打交道,pin control subsystem也不例外,被它驱动的硬件叫做pin controller(一般ARM soc的datash ...
- Linux内核中影响tcp三次握手的一些协议配置
在Linux的发行版本中,都存在一个/proc/目录,有的也称它为Proc文件系统.在 /proc 虚拟文件系统中存在一些可调节的内核参数.这个文件系统中的每个文件都表示一个或多个参数,它们可以通过 ...
随机推荐
- [Bzoj2286]消耗战(虚树+DP)
Description 题目链接 Solution 在虚树上跑DP即可 Code #include <cstdio> #include <algorithm> #include ...
- 8,实例化Flask的参数 及 对app的配置
Flask 是一个非常灵活且短小精干的web框架 , 那么灵活性从什么地方体现呢? 有一个神奇的东西叫 Flask配置 , 这个东西怎么用呢? 它能给我们带来怎么样的方便呢? 首先展示一下: from ...
- cocos2d-x 3.0的入门程序:helloworld
看过了这么多不同方向的应用,发现很多程序入门都是helloworldhelloworld是所有程序员的绝对初恋 先看一下程序的运行结果吧 然后就是他的工程代码 工程的目录有两个 Classes:程序中 ...
- Android 快捷方式的创建与查询 快捷方式问题大全 获取快捷方式在Launcher数据库中的信息 Failed to find provider info for com.android.la
/** * 创建添加快捷方式 * 其中需要设置的有: * 1. 快捷方式的标题 * 2. 快捷方式的图标 * 3. 点击快捷方式后的跳转 */ public static void createSho ...
- web项目中获取spring的bean对象
Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架,如何在程序中不通过注解的形式(@Resource.@Autowired)获取Spring配置的bean呢? Bean工厂(c ...
- Percona-Tookit工具包之pt-heartbeat
Preface Replication delay is a common issue in MySQL replications.Especially in those replic ...
- 关于在虚拟机上安装iOS所遇到的问题
先配一张成功的图片: 要想实现上图所示的,就要安装 unlocker206, 安装完unlocker206 不用改里面的内容!!!看到**经验上说还要改内容,结果我试了很多次,根本不行,其实问题不在改 ...
- selenium初识(一)
Selenium是一个开源的便携式的自动化软件测试工具,用于测试web应用程序.有能力在不同浏览器和操作系统运行.它是一套工具,帮助我们有效地给予web应用程序的自动化. Selenium分为以下几个 ...
- MySQL、MongoDB、Redis 数据库之间的区别与使用(本章迭代更新)
MySQL.MongoDB.Redis 数据库之间的区别与使用 MySQL.MongoDB.Redis 数据库之间的区别与使用(本章迭代更新) update:2019年2月20日 15:21:19(本 ...
- Flask 教程精简版之一(系列片)
Flask 教程精简版之一(系列片) 现在连教程都有精简版 准备 1.要学会 Flask 之前必须掌握 Python 基本使用. 2.会使用简单的 HTML 效果更加 3.若想练气功必须先自暴自弃 简 ...