您的位置:首页 > 运维架构 > Linux

Linux独立中断栈学习笔记及验证实验(ARM、x86)

2016-01-21 09:27 645 查看
前几天在学习内核源码的时候,无意中看到了Linux内核中有中断栈的概念。这个是我以前没有注意到的,为了深入理解这个知识,我查找了一些资料:

Linux内核中的中断栈与内核栈的补充说明
《深入Linux内核构架》第14章 内核活动 14.1 中断 P695

这些资料都是针对X86构架的。对于中断栈,我自己总结如下:

从《深入Linux内核构架》中可以知道:内核在IA-32平台上,早期(2.6.36及之前)内核如果配置了4K内核栈(CONFIG_4KSTACKS)(默认是8K),对于常规的内核工作以及IRQ处理例程共用这个栈来说似乎有点不够用,所有引入了两个栈:硬件IRQ栈和软件IRQ栈。在这种情况下,当内核进入中断之后,检测自己所在的栈是内核栈还是中断栈。如果是中断栈(中断嵌套情况)就去执行中断例程;如果是内核栈就切换到中断栈,同时复制当前内核栈中的部分thread_info数据到中断栈。
但是2.6.36之后的内核就不再有4K内核栈的配置,对于IA-32统一使用8K内核栈,并总是使用两个独立的8K中断栈。这样的改变应该是由于计算机性能的提高、内存的扩大(4G内存已经很平常,16G、32G内存也已不新鲜)以及软件的复杂度提高(对栈的需求增加)。

这个变化的Git提交信息如下:

点击(此处)折叠或打开

commit 91151240ed8e97cc4457dae4094153c2744f1eb8
Merge: 211baf4 fe8e0c2
Author: Linus Torvalds <torvalds@linux-foundation.org>
Date: Fri Oct 22 08:54:21 2010 -0700

Merge branch 'x86-irq-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip

* 'x86-irq-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip:
x86, 32-bit: Align percpu area and irq stacks to THREAD_SIZE
x86, 32-bit: 对其percpu域和irq栈到THREAD_SIZE
x86: Move alloc_desk_mask variables inside ifdef
x86:将alloc_desk_mask变量移入ifdef
x86-32: Align IRQ stacks properly
x86-32:适当对齐IRQ栈
x86: Remove CONFIG_4KSTACKS
x86:移除CONFIG_4KSTACKS
x86: Always use irq stacks
x86:总是使用irq栈

Fixed up trivial conflicts in include/linux/{irq.h, percpu-defs.h}
修正include/linux/{irq.h, percpu-defs.h}中细小的冲突

这个提交并入主线内核的时间:2.6.36~2.6.37-rc1

点击(此处)折叠或打开

$ git tag --contains 91151240ed8
v2.6.37
v2.6.37-rc1
v2.6.37-rc2
v2.6.37-rc3
v2.6.37-rc4
v2.6.37-rc5
v2.6.37-rc6
v2.6.37-rc7
v2.6.37-rc8
v2.6.38
v2.6.38-rc1
v2.6.38-rc2
v2.6.38-rc3
v2.6.38-rc4
v2.6.38-rc5
v2.6.38-rc6
v2.6.38-rc7
v2.6.38-rc8
......

对于内核栈到转换,大家可以参考《深入Linux内核构架》。里面有很详细的介绍。但是这个是针对x86平台的。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
我是做嵌入式Linux开发的,so 我所想的是ARM平台是否有同样的设计呢?我跟踪了ARM构架的内核代码发现其中(arch/arm/kernel/irq.c)并没有和x86类似的栈转换设计。也就是说:ARM并没有独立的中断栈,中断共用当前进程的内核栈。但是单从代码上分析我觉得还是不够的,有可能是我能力有限,没有看出来。所以,再通过实验来证明才是比较可靠的。所有我设计了一个对比实验来证明,使用原理如下:

编写一个内核模块,在模块的初始化函数中注册一个中断。在中断例程中定义一个自动变量(test_point),这个变量必然会被放在运行中断例程时所使用的栈中。在这个中断例程中打印出当前进程的内核栈范围和这个自动变量的地址,从中看出运行中断例程所使用的栈是否为进程的内核栈。如果自动变量的地址不在当前进程的内核栈范围中,则可以推断出这个内核使用了独立的中断栈。






我在ARM平台和IA-32平台都做了实验。

ARM平台是一个开发板,有一个独立GPIO作为中断的引脚,可以手工产生中断,是一个非共享中断,所以代码比较简单,只需要模块初始化和清理函数即可。
ARM平台实验代码:

irq_stack_test_module_v2
_pub.zip
在编译前,请自行修改Makefile和头文件(中断号)。

开发板(基于TI8168,内核2.6.37)上的实验数据如下:

点击(此处)折叠或打开

swapper-PID:0-kernel_stack@0xc04c0000-to-c04c2000
test_point@0xc04c1ea4

swapper-PID:0-kernel_stack@0xc04c0000-to-c04c2000
test_point@0xc04c1ea4

swapper-PID:0-kernel_stack@0xc04c0000-to-c04c2000
test_point@0xc04c1ea4

swapper-PID:0-kernel_stack@0xc04c0000-to-c04c2000
test_point@0xc04c1ea4

swapper-PID:0-kernel_stack@0xc04c0000-to-c04c2000
test_point@0xc04c1ea4

swapper-PID:0-kernel_stack@0xc04c0000-to-c04c2000
test_point@0xc04c1ea4

OGLES2Chameleon-PID:1349-kernel_stack@0xdd6ee000-to-dd6f0000
test_point@0xdd6eff0c

OGLES2Chameleon-PID:1349-kernel_stack@0xdd6ee000-to-dd6f0000
test_point@0xdd6eff0c

OGLES2Chameleon-PID:1349-kernel_stack@0xdd6ee000-to-dd6f0000
test_point@0xdd6eff0c

OGLES2Chameleon-PID:1349-kernel_stack@0xdd6ee000-to-dd6f0000
test_point@0xdd6eff0c

从上面可以看出:所有的test_point都在当前进程内核栈中,ARM没有独立的中断栈。

------------------------------------------------------------------------------
在IA-32就没这么方便了,毕竟是PC,不太方便引一个外部可以手动产生的中断,所以就使用了一个共享中断。当然这个共享中断最好是可以有频繁的中断,这样才可以产生我们需要的打印数据。由于使用了共享中断,我们必须有一个设备结构体要传递给中断申请函数,所以代码就稍微麻烦一点点。
IA-32平台实验代码:

irq_stack_test_module_x86_pub.zip

在编译前,请自行修改Makefile和头文件(中断号)。

IA-32 PC上的实验数据如下:

点击(此处)折叠或打开

Linux tekkaman-desktop 2.6.32-40-generic #87-Ubuntu SMP Mon Mar 5 20:26:31 UTC 2012 i686 GNU/Linux

内核配置文件中:# CONFIG_4KSTACKS is not set

[ 3567.989081] swapper-PID:0-kernel_stack@0xc0764000-to-c0766000
[ 3567.989082] test_point@0xc0765e94
[ 3568.005816]
[ 3568.005817] chrome-PID:4194-kernel_stack@0xf121a000-to-f121c000
[ 3568.005818] test_point@0xf121bea0
[ 3568.022556]
[ 3568.022557] swapper-PID:0-kernel_stack@0xc0764000-to-c0766000
[ 3568.022558] test_point@0xc0765e94
[ 3568.039293]

......

[ 3569.696301] swapper-PID:0-kernel_stack@0xc0764000-to-c0766000
[ 3569.696302] test_point@0xc0765e94
[ 3569.713021]
[ 3569.713022] swapper-PID:0-kernel_stack@0xc0764000-to-c0766000
[ 3569.713023] test_point@0xc0765e94
[ 3569.729764]
[ 3569.729766] nautilus-PID:1662-kernel_stack@0xf577c000-to-f577e000
[ 3569.729767] test_point@0xf577df24
[ 3569.746502]
[ 3569.746503] swapper-PID:0-kernel_stack@0xc0764000-to-c0766000
[ 3569.746505] test_point@0xc0765e94
[ 3569.763234]
[ 3569.763235] swapper-PID:0-kernel_stack@0xc0764000-to-c0766000
[ 3569.763236] test_point@0xc0765e94

从实验中可以看出,所有的test_point都在进程内核栈中,没有独立的中断栈。

更换了一台高版本内核的 PC:

点击(此处)折叠或打开

Linux 3.2.1-gentoo-r2 #2 SMP Wed Feb 22 08:57:23 CST 2012 i686 AMD Athlon(tm) 64 X2 Dual Core Processor 4800+ AuthenticAMD GNU/Linux

中断号:10
[ 3809.857920] swapper/1-PID:0-kernel_stack@0xf5446000-to-f5448000
[ 3809.857923] test_point@0xf5459f90
[ 3809.867821]
[ 3809.867823] swapper/1-PID:0-kernel_stack@0xf5446000-to-f5448000
[ 3809.867826] test_point@0xf5459f90
[ 3809.868093]
[ 3809.868095] swapper/1-PID:0-kernel_stack@0xf5446000-to-f5448000
[ 3809.868098] test_point@0xf5459f90
[ 3810.170480]
[ 3810.170483] chrome-PID:16845-kernel_stack@0xcc2a6000-to-cc2a8000
[ 3810.170487] test_point@0xf5459f90
[ 3810.170919]
[ 3810.170920] chrome-PID:16845-kernel_stack@0xcc2a6000-to-cc2a8000
[ 3810.170923] test_point@0xf5459f90
[ 3810.217095]
[ 3810.217099] chrome-PID:16845-kernel_stack@0xcc2a6000-to-cc2a8000
[ 3810.217102] test_point@0xf5459f90
[ 3811.327909]
[ 3811.327913] swapper/1-PID:0-kernel_stack@0xf5446000-to-f5448000
[ 3811.327916] test_point@0xf5459f90

中断号:41
[ 3972.337204] syslog-ng-PID:2274-kernel_stack@0xf3f3a000-to-f3f3c000
[ 3972.337207] test_point@0xf5459f90
[ 3972.337349]
[ 3972.337350] syslog-ng-PID:2274-kernel_stack@0xf3f3a000-to-f3f3c000
[ 3972.337353] test_point@0xf5459f90
[ 3972.337497]
[ 3972.337499] syslog-ng-PID:2274-kernel_stack@0xf3f3a000-to-f3f3c000
[ 3972.337501] test_point@0xf5459f90
[ 3972.337637]
[ 3972.337639] syslog-ng-PID:2274-kernel_stack@0xf3f3a000-to-f3f3c000
[ 3972.337642] test_point@0xf5459f90
[ 3972.385449]
[ 3972.385451] swapper/1-PID:0-kernel_stack@0xf5446000-to-f5448000
[ 3972.385454] test_point@0xf5459f90
[ 3972.385670]
[ 3972.385672] swapper/1-PID:0-kernel_stack@0xf5446000-to-f5448000
[ 3972.385674] test_point@0xf5459f90
[ 3972.385823]
[ 3972.385825] swapper/1-PID:0-kernel_stack@0xf5446000-to-f5448000
[ 3972.385828] test_point@0xf5459f90

从实验中可以看出,所有的test_point都不在进程内核栈中,并都是同一个地址,so 有独立的中断栈。

综合所有实验以及内核源码,我们可以得出结论:

对于IA-32平台,高版本的内核(>2.6.36)内核都实现了独立的中断栈
对于ARM平台,内核暂未实现独立的中断栈

PS:其实对于独立的中断栈,不仅在IA-32上有,PPC等其他平台也有实现,有兴趣的朋友可以参考内核源码。对于上面的实验,也可以使用软件中断(如tasklet)再做一次,有兴趣的朋友可以自行修改源码。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: