浅谈linux内核栈(基于3.16-rc4)

在3.16-rc4内核源码中,内核给每个进程分配的内核栈大小为8KB。这个内核栈被称为异常栈,在进程的内核空间运行时或者执行异常处理程序时,使用的都是异常栈,看下异常栈的代码(include/linux/sched.h):

1 union thread_union {
2     struct thread_info thread_info;
3     unsigned long stack[THREAD_SIZE/sizeof(long)];
4 };

THREAD_SIZE值为8KB,因此内核为进程的异常栈(内核栈)分配了两个页框大小(页框大小4KB)。另外,进程的thread_info结构体保存在栈顶部。

此外,内核为每个cpu分配一个硬中断栈和一个软中断栈(这两个栈也是内核栈),用来执行中断服务例程和下半部(软中断),看看代码(arch/x86/kernel/irq_32.c)。这两个栈属于cpu,不属于进程,这和异常栈是有区别的。

1 DEFINE_PER_CPU(struct irq_stack *, hardirq_stack);
2 DEFINE_PER_CPU(struct irq_stack *, softirq_stack);

定义了两个数组hardirq_stack和softirq_stack,每个数组元素对应一个cpu,指向了该cpu的硬中断栈或者软中断栈。再来看下struct irq_stack结构体(arch/x86/include/asm/processor.h):

1 struct irq_stack {
2     u32                     stack[THREAD_SIZE/sizeof(u32)];
3 } __aligned(THREAD_SIZE);

可见,硬中断栈和软中断栈的大小均为8KB。

内核在执行中断处理程序时,在do_IRQ函数中会调用handle_irq函数,在handle_irq函数中要进行堆栈切换,代码如下(arch/x86/kernel/irq_32.c):

 1 bool handle_irq(unsigned irq, struct pt_regs *regs)
 2 {
 3     struct irq_desc *desc;
 4     int overflow;
 5
 6     overflow = check_stack_overflow();
 7
 8     desc = irq_to_desc(irq);
 9     if (unlikely(!desc))
10         return false;
11
12     if (user_mode_vm(regs) || !execute_on_irq_stack(overflow, desc, irq)) {
13         if (unlikely(overflow))
14             print_stack_overflow();
15         desc->handle_irq(irq, desc);
16     }
17
18     return true;
19 }

第12行中执行execute_on_irq_stack函数来判断是否需要堆栈切换,如果不需要,则执行if体,即在当前堆栈中执行中断服务例程,如果需要切换堆栈,则不执行if体。下面看下execute_on_irq_stack代码(arch/x86/kernel/irq_32.c):

 1 static inline int
 2 execute_on_irq_stack(int overflow, struct irq_desc *desc, int irq)
 3 {
 4     struct irq_stack *curstk, *irqstk;
 5     u32 *isp, *prev_esp, arg1, arg2;
 6
 7     curstk = (struct irq_stack *) current_stack();
 8     irqstk = __this_cpu_read(hardirq_stack);
 9
10     /*
11      * this is where we switch to the IRQ stack. However, if we are
12      * already using the IRQ stack (because we interrupted a hardirq
13      * handler) we can‘t do that and just have to keep using the
14      * current stack (which is the irq stack already after all)
15      */
16     if (unlikely(curstk == irqstk))
17         return 0;
18
19     isp = (u32 *) ((char *)irqstk + sizeof(*irqstk));
20
21     /* Save the next esp at the bottom of the stack */
22     prev_esp = (u32 *)irqstk;
23     *prev_esp = current_stack_pointer;
24
25     if (unlikely(overflow))
26         call_on_stack(print_stack_overflow, isp);
27
28     asm volatile("xchgl    %%ebx,%%esp    \n"
29              "call    *%%edi        \n"
30              "movl    %%ebx,%%esp    \n"
31              : "=a" (arg1), "=d" (arg2), "=b" (isp)
32              :  "0" (irq),   "1" (desc),  "2" (isp),
33             "D" (desc->handle_irq)
34              : "memory", "cc", "ecx");
35     return 1;
36 }

第7行获取当前堆栈的指针,第8行获取本地cpu的硬中断栈指针,第16行对二者进行比较,如果相等,则不需要切换堆栈(说明当前堆栈就是硬中断栈,也说明是在中断处理程序中时又发生了中断)。如果不相等,就要进行堆栈切换,第22-23行将当前堆栈指针保存在将要切换到的堆栈中(用于返回)。第28-34行,在内联汇编中进行堆栈切换并执行相应的中断服务例程,第33行将中断服务例程函数名存放在%edi中,第29行跳转到中断服务例程中。

浅谈linux内核栈(基于3.16-rc4),布布扣,bubuko.com

时间: 2024-08-25 19:46:50

浅谈linux内核栈(基于3.16-rc4)的相关文章

[]转帖] 浅谈Linux下的五种I/O模型

浅谈Linux下的五种I/O模型 https://www.cnblogs.com/chy2055/p/5220793.html  一.关于I/O模型的引出 我们都知道,为了OS的安全性等的考虑,进程是无法直接操作I/O设备的,其必须通过系统调用请求内核来协助完成I/O动作,而内核会为每个I/O设备维护一个buffer.如下图所示: 整个请求过程为: 用户进程发起请求,内核接受到请求后,从I/O设备中获取数据到buffer中,再将buffer中的数据copy到用户进程的地址空间,该用户进程获取到数

(转)浅谈 Linux 内核无线子系统

前言 Linux 内核是如何实现无线网络接口呢?数据包是通过怎样的方式被发送和接收呢? 刚开始工作接触 Linux 无线网络时,我曾迷失在浩瀚的基础代码中,寻找具有介绍性的材料来回答如上面提到的那些高层次的问题. 跟踪探索了一段时间的源代码后,我写下了这篇总结,希望在 Linux 无线网络的工作原理上,读者能从这篇文章获得一个具有帮助性的概览. 1.全局概览 在开始探索 Linux 无线具体细节之前,让我们先来把握一下 Linux 无线子系统整体结构.如图1,展示了 Linux 无线子系统各个模

浅谈Linux系统中的7种运行级别及其原理

浅谈Linux系统中的7种运行级别 Linux系统7个运行级别(runlevel): 运行级别0: 系统停机状态,系统默认运行级别不能设为0,否则不能正常启动. 运行级别1: 单用户工作状态,root权限,用于系统维护,禁止远程登陆. 运行级别2: 多用户状态(没有NFS). 运行级别3: 完全的多用户状态(有NFS),登陆后进入控制台命令行模式. 运行级别4: 系统未使用,保留. 运行级别5: X11控制台,登陆后进入图形GUI模式. 运行级别6: 系统正常关闭并重启,默认运行级别不能设为6,

浅谈Linux下Makefile编写

浅谈Linux下Makefile的编写 前言:本文简要介绍Makefile文件的编写规范,结合具体项目中的应用进行讲解. 具体代码地址: https://github.com/AnSwErYWJ/DogFood/blob/master/Makefile 简介 Make工具最主要也是最基本的功能就是通过makefile文件来描述源程序之间的相互关系并自动维护编译工作.而makefile 文件需要按照某种语法进行编写,文件中需要说明如何编译各个源文件并连接生成可执行文件,并要求定义源文件之间的依赖关

浅谈Linux中的信号机制(二)

首先谢谢 @小尧弟 这位朋友对我昨天夜里写的一篇<浅谈Linux中的信号机制(一)>的指正,之前的题目我用的“浅析”一词,给人一种要剖析内核的感觉.本人自知功力不够,尚且不能对着Linux内核源码评头论足.以后的路还很长,我还是一步一个脚印的慢慢走着吧,Linux内核这座山,我才刚刚抵达山脚下. 好了,言归正传,我接着昨天写下去.如有错误还请各位看官指正,先此谢过. 上篇末尾,我们看到了这样的现象:send进程总共发送了500次SIGINT信号给rcv进程,但是实际过程中rcv只接受/处理了1

浅谈linux中shell变量$#,[email&#160;protected],$0,$1,$2,$?的含义解释

浅谈linux中shell变量$#,[email protected],$0,$1,$2,$?的含义解释 下面小编就为大家带来一篇浅谈linux中shell变量$#,[email protected],$0,$1,$2的含义解释.小编觉得挺不错的,现在就分享给大家,也给大家做个参考.一起跟随小编过来看看吧 摘抄自:ABS_GUIDE 下载地址:http://www.tldp.org/LDP/abs/abs-guide.pdf linux中shell变量$#,[email protected],$

Java网络编程和NIO详解7:浅谈 Linux 中NIO Selector 的实现原理

Java网络编程和NIO详解7:浅谈 Linux 中NIO Selector 的实现原理 转自:https://www.jianshu.com/p/2b71ea919d49 本系列文章首发于我的个人博客:https://h2pl.github.io/ 欢迎阅览我的CSDN专栏:Java网络编程和NIO https://blog.csdn.net/column/details/21963.html 部分代码会放在我的的Github:https://github.com/h2pl/ 浅谈 Linux

浅谈Linux协议和组成

根据自己学习Linux方面的知识,浅谈下linux的协议和组成,那首先了解下计算机的组成和功能: 计算机系统由硬件(hardware)系统和软件(software)系统两大部分组成: 1.硬件系统由主机和外部设备组成: 主机由中央处理器CPU(运算器ALU和控制器CU)和内存储器(ROM和RAM) 外部设置由输入设备和输出设备等其他设备组成 2.软件系统由系统软件和应用软件组成 接下来了解Linux的内核功能和作用:我们学习Linux的最重要就是内核(kernel),内核是什么.我用自己理解的方

浅谈Linux的远程连接

大部分情况下,我们不可能每台服务器都配置一台显示器,也不可能时刻在服务器旁边,但是我们要操作服务器,就要使用远程连接了,本篇就浅谈下如何进行远程连接Linux服务器. 环境介绍:vmware中Centsos6.5 x86_64一台,防火墙及Selinux已关闭. 一.最简单的工具 Putty 下载安装后直接运行,输入IP与保存名称即可 输入账号密码 二.最简洁的工具 Xshell 下载后直接运行,输入 ssh ip,然后输入账号.密码即可 三.最强大的工具 SecurtCRT 下载后解压,将Se