内核堆栈和用户空间堆栈


110

内核堆栈和用户堆栈有什么区别?为什么使用内核堆栈?如果在ISR中声明了局部变量,它将存储在哪里?每个进程都有自己的内核堆栈吗?那么,进程如何在这两个堆栈之间进行协调?

Answers:


188
  1. 内核堆栈和用户堆栈有什么区别?

简而言之,除了在内存中使用不同的位置(并因此为堆栈指针寄存器使用不同的值)之外,什么也没有,而且通常使用不同的内存访问保护。即在用户模式下执行时,即使映射了内核内存(其中一部分是内核堆栈)也将不可访问。反之亦然,在没有内核代码明确要求的情况下(在Linux中,通过诸如之类的功能copy_from_user()),通常无法直接访问用户内存(包括用户堆栈)。

  1. 为什么使用[单独的]内核堆栈?

特权与安全性分离。一方面,用户空间程序可以使它们的堆栈(指针)成为他们想要的任何东西,并且通常没有体系结构上的要求就可以拥有一个有效的栈。因此,内核无法信任用户空间堆栈指针是有效的还是不可用的,因此需要在其自己的控制下一组。不同的CPU架构以不同的方式实现此目的。当特权模式切换发生时,x86 CPU会自动切换堆栈指针,并且可以通过特权代码(即仅内核)配置用于不同特权级别的值。

  1. 如果在ISR中声明了局部变量,它将存储在哪里?

在内核堆栈上。内核(即Linux内核)不会 ISR直接挂钩到x86架构的中断门,而是将中断分派委托给通用的内核中断进入/退出机制,该机制可以在调用注册的处理程序之前保存中断前寄存器的状态。 。调度中断时,CPU本身可能会执行特权和/或堆栈切换,并且内核会使用/设置该切换,以便公用中断入口代码已经可以依赖存在的内核堆栈。
就是说,在执行内核代码时发生的中断将简单地(继续)在那时使用内核堆栈。如果中断处理程序具有深层嵌套的调用路径,则可能导致堆栈溢出(如果深层内核调用路径被中断并且处理程序导致了另一条深层路径;在Linux中,文件系统/软件RAID代码被iptables处于活动状态的网络代码中断了)已知会在未调整的较早内核中触发此类问题……解决方案是增加此类工作负载的内核堆栈大小)。

  1. 每个进程都有自己的内核堆栈吗?

不仅仅是每个进程-每个线程都有自己的内核堆栈(实际上,也有自己的用户堆栈)。请记住,进程和线程(对于Linux)之间的唯一区别是多个线程可以共享一个地址空间(形成一个进程)。

  1. 流程如何在这两个堆栈之间进行协调?

完全没有-不需要。调度(如何/何时运行不同的线程,如何保存和恢复它们的状态)是操作系统的任务,而进程不必为此担心。创建线程时(每个进程必须至少有一个线程),内核会为它们创建内核堆栈,而用户空间堆栈则由用于创建线程的任何机制显式创建/提供(函数类似于makecontext()pthread_create()允许调用者指定要用于“子”线程堆栈的内存区域)或继承(通过按需访问内存克隆,在创建新进程时通常称为“写时复制” / COW)。
那就是(状态,其中包括线程的堆栈指针)。这有多种方式:UNIX信号setcontext()pthread_yield()/ pthread_cancel(),... -但这disgressing从原来的问题有点。


优秀答案FrankH。谢谢。
kumar

1
@FrankH很好的答案..但是我有一些与之相关的小问题,但是在ARM体系结构中..此内核堆栈如何与不同的处理器模式相关?
Rahul 2014年

2
@Rahul:“ SO评论的边距太小,无法包含这样的答案”。一个好的问题是堆栈/堆栈指针寄存器如何在不同的ARM CPU模式下工作(哪种模式实现了存储的SP),但需要的空间比注释所能提供的还要多。同样的情况也适用于x86任务门或IST-不存在“单个”内核堆栈指针(就像没有“单个”用户堆栈指针)之类的东西,以及它的硬件支持/要求是什么。在不同的操作模式下,单独的堆栈指针非常依赖于硬件。
FrankH。

@FrankH。我为相同的问题创建了一个新的问题... stackoverflow.com/q/22601165/769260我希望现在您可以在不担心空间的情况下帮助我:)
Rahul 2014年

1
@FrankH。您能否提供一个图表,显示内核堆栈在进程的内存布局中所属的位置?
Jithin Pavithran '18

19

我的答案是从其他SO问题中收集的。

What's the difference between kernel stack and user stack?

作为内核程序员,您知道应该限制内核访问错误的用户程序。假设您为内核和用户空间保留了相同的堆栈,那么用户应用程序中的简单segfault会使内核崩溃并需要重新启动。

每个CPU(如ISR堆栈)都有一个“内核堆栈”,每个进程有一个“内核堆栈”。每个进程有一个“用户堆栈”,尽管每个线程都有自己的堆栈,包括用户线程和内核线程。

http://linux.derkeiler.com/Mailing-Lists/Kernel/2004-10/3194.html

Why kernel stack is used?

因此,当我们处于内核模式时,必须使用堆栈类型的机制来处理函数调用,类似于用户空间的局部变量。

http://www.kernel.org/doc/Documentation/x86/kernel-stacks

If a local variable is declared in an ISR, where it will be stored?

它将存储在ISR堆栈中(IRQSTACKSIZE)。仅当硬件支持时,ISR才在单独的中断堆栈上运行。否则,ISR堆栈帧将被推到被中断线程的堆栈上。

用户空间不知道,坦率地说,它并不关心中断是在当前进程的内核堆栈中还是在单独的ISR堆栈中提供。由于每个CPU都会产生中断,因此ISR堆栈必须每个CPU。

 Does each process has its own kernel stack ?

是。每个进程都有自己的内核堆栈。

 Then how the process coordinates between both these stacks?

@FrankH的回答对我来说很好。


4
  1. 内核堆栈和用户堆栈有什么区别

参考Robert Love的Linux Kernel Development的参考,主要区别在于大小:

通过在堆栈上静态分配许多变量(包括巨大的结构和数千个元素的数组),可以摆脱用户空间的束缚。
此行为是合法的,因为用户空间具有可以动态增长的大堆栈。
内核堆栈既不大也不动态。它很小,尺寸固定。
内核堆栈的确切大小因体系结构而异。
在x86上,堆栈大小在编译时是可配置的,可以是4KB或8KB。
从历史上看,内核堆栈为两页,这通常意味着在32位体系结构上为8KB,在64位体系结构上为16KB,该大小是固定的且是绝对的。
每个进程都有自己的堆栈。

另外,内核堆栈还包含一个指向thread_info结构的指针,该结构保存有关线程的信息。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.