关于linux:笨叔点滴2-为啥子ARM32体系结构中每个处理模式都有一个单独的栈

“ 各位小伙伴,我是小笨叔。笨叔尽量每天给大家分享一点点小东西,可能是笨笨的、傻傻的、甜甜的、酸酸的小点滴,记录这每一刻每一天的小感悟,就像小雨点一样,它会缓缓会合到大江大海!”

上次提到LinuxCon大会,这种大会正如笨叔说的

“听君一席话,胜读十年书”

笨叔有幸在北京访问了Linux社区外面一位老前辈,凝听他当年在80~90年代做CPU处理器体系结构和Linux操作系统的鲜为人知的历史和对人生和技术的感悟,让笨叔大受启发。

在闲聊中,他提到为什么ARM32的处理器须要7种处理器模式,而且每一种处理器模式都须要一个独自的栈空间?比方irq模式,为什么ARM32的设计里须要一个独自的irq模式,而且这个irq模式只有12个字节?为什么不和SVC模式专用一个栈呢?比方x86等传统经典的处理器架构里,是没有为irq独自开拓一个栈的。

这些是十分好的问题,引发了笨叔回到上海持续思考。本文抛砖引玉,心愿有趣味的小伙伴能够在文章前面留言发表您的观点。

01 ARM32上的设计

咱们来看一下ARM32上,如果产生了一个irq中断,ARM32处理器是怎么解决的?咱们晓得ARM32外面有一个奇葩的7个工作模式:

如上图所示,在ARMV6之前,ARM的处理器模式有7个。失常的内核是跑在SVC模式的,用户态app是跑在User模式,而其余几个就是咱们常说的IRQ中断,FIQ中断和几个异样模式。如果比照x86的话,x86只有ring0 ~ ring3这4个特权级别,内核跑着ring 0里,而用户态跑在ring 3里,而没有辨别SVC模式,IRQ模式等。

咱们在看看ARMv7上改良。

抛开secure模式不谈,在non-secure即normal world来说,ARMv7在持续沿用7个模式的根底上又辨别了PL0模式和PL1和PL2模式, PL是privilege level的意思。简略来说:

PL0模式:用户模式,等同于以前的USER模式

PL1操作系统模式:蕴含以前的SVC模式,IRQ模式,FIQ模式

PL2虚拟化模式:新增的模式,有点相似x86上虚拟化扩大的root模式

如果说ARM32的设计的正当的话,咱们看看ARM v8里的设计。

ARMv8外面曾经齐全摈弃ARMv7之前的做法了,连名字都该了,当初叫做EL,也就是exception level,而且每一个EL级别的异样也变了。其中

名字改了之后,每个EL级别管辖的范畴和权力就不一样了,是不是很像x86外面的ring0~ring3呢,谁叫你x86在PC和服务器上这么火呢?所以,x86上的长处,ARM当然要好好学习啦。

除了这个之外,还齐全摈弃了SVC, IRQ, FIQ等等那7个模式了,这和ARMv7又有很多不同了。当初ARMv8上,异样分成两种:

同步异样:比方MMU的一些拜访权限问题

异步异样:这个就大家常见的IRQ, FIQ, ERR

而且还有一点,ARMv8外面,不再为每个异样类型设置一个专门的栈,比方IRQ曾经没有独自的栈了,当初的栈,只有每个EL才有栈。

02 ARM32中断栈

ARM32产生中断之后,IRQ栈和SVC栈的状况如下:

上述这个图就是ARM32在Linux内核中,产生IRQ中,栈的变动状况,大家能够看《奔跑吧Linux内核》第621~626页。总的来说,IRQ模式上面的栈值保留了产生中断那个现场的SPSR, LR, SP_IRQ三个寄存器的内容。而后切换模式到SVC模式,进一步保留通用寄存器到SVC的栈空间里。所以在ARM32里,中断产生之后,其实是应用了两个模式的栈,一个是IRQ的栈,另外一个SVC模式的栈,这个SVC的栈,在Linux内核里其实就是内核栈。

那在X86里,是怎么样的呢?x86处理器里有一个TSS(Task State Segment),当中断产生时,用户过程或者处于用户态(特权级3)或者处于内核态(特权级0),如果是在用户态,那么会产生栈的切换问题,也就是会切换到内核态的栈,如果是在内核态,那么就没有栈切换的问题。然而x86处理器在特权级0上只有一个ESP,这意味着中断产生后,只能应用一个栈,这个栈就是内核栈(kernel stack)。处理器的硬件逻辑会将被中断过程的下条指令(CS,EIP)以及EFLAG压入栈,当然如果产生用户态栈向内核态栈的切换,处理器还会把用户态的(SS, ESP)也压入栈,此时应用的就是内核栈。这个行为属于处理器的硬件逻辑领域,不是系统软件的行为。

另外一方面,当初Linux内核在x86或者其余体系结构里,曾经反对内核栈和中断栈拆散的办法了。当然,这里说的中断栈不是本文说的那个CPU外部的中断栈,而是和内核栈相似的一个栈,也就是在中断处理程序中,和SVC模式应用的内核栈拆散。这是内核设计的问题了,总之,中断栈可与内核栈共享,也可重新分配一个独立的中断栈。然而负面因素是中断栈如果产生嵌套,可能毁坏内核栈的一些数据,因为毕竟共享,所以栈空间有时候难免会顾此失彼。所以在x86的Linux中,总是应用拆散的内核栈设计。零碎中每个过程都会领有属于本人的内核栈,而零碎中每个CPU都将为中断解决筹备了两个独立的中断栈,别离是hardirq栈和softirq栈。留神,这是OS的设计问题,不是明天咱们想探讨的CPU硬件设计上的中断栈拆散。

上面是Linux内核外面x86架构应用拆散的中断栈的补丁。

https://git.kernel.org/pub/sc…

那为啥子ARM32要为IRQ模式设置独自一个12字节的栈呢,而不是复用SVC模式的栈呢?

笨叔翻阅了ARM相干的技术文档也没找到答案,我猜想可能是因为一个重要的起因是ARM32这个架构设计的时候是90年代,那个年代的ARM处理器是相当慢的,独自一个IRQ栈能够进步中断的响应速度,特地是晚期的ARM处理器次要利用场景是嵌入式零碎,反对中断嵌套的形式。从下面的x86的拆散的中断栈的patch来看,拆散的中断栈确实有不少劣势,至多能够保障不会导致SVC模式的栈产生overflow。那到底是ARM32这种设计先进呢还是ARMv8和x86的设计理念先进呢,是提高还是倒退呢?我有点confuse了!

笨叔抛砖引玉,心愿感兴趣的同学能够在前面留言,大家一起探讨。

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理