内核堆栈和用户空间堆栈 [英] kernel stack and user space stack

查看:415
本文介绍了内核堆栈和用户空间堆栈的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

What's the difference between kernel stack and user stack? Why kernel stack is used? If a local variable is declared in an ISR, where it will be stored? Does each process has its own kernel stack? Then how the process coordinates between both these stacks?

推荐答案

  1. 内核堆栈和用户堆栈有什么区别?
  1. What's the difference between kernel stack and user stack ?

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

In short, nothing - apart from using a different location in memory (and hence a different value for the stackpointer register), and usually different memory access protections. I.e. when executing in user mode, kernel memory (part of which is the kernel stack) will not be accessible even if mapped. Vice versa, without explicitly being requested by the kernel code (in Linux, through functions like copy_from_user()), user memory (including the user stack) is not usually directly accessible.

  1. 为什么要使用[单独的]内核堆栈?
  1. Why is [ a separate ] kernel stack used ?

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

Separation of privileges and security. For one, userspace programs can make their stack(pointer) anything they want, and there is usually no architectural requirement to even have a valid one. The kernel therefore cannot trust the userspace stackpointer to be valid nor usable, and therefore will require one set under its own control. Different CPU architectures implement this in different ways; x86 CPUs automatically switch stackpointers when privilege mode switches occur, and the values to be used for different privilege levels are configurable - by privileged code (i.e. only the kernel).

  1. 如果在ISR中声明了局部变量,它将存储在哪里?
  1. If a local variable is declared in an ISR, where will it be stored ?

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

On the kernel stack. The kernel (Linux kernel, that is) does not hook ISRs directly to the x86 architecture's interrupt gates but instead delegates the interrupt dispatch to a common kernel interrupt entry/exit mechanism which saves pre-interrupt register state before calling the registered handler(s). The CPU itself when dispatching an interrupt might execute a privilege and/or stack switch, and this is used/set up by the kernel so that the common interrupt entry code can already rely on a kernel stack being present.
That said, interrupts that occur while executing kernel code will simply (continue to) use the kernel stack in place at that point. This can, if interrupt handlers have deeply nested call paths, lead to stack overflows (if a deep kernel call path is interrupted and the handler causes another deep path; in Linux, filesystem / software RAID code being interrupted by network code with iptables active is known to trigger such in untuned older kernels ... solution is to increase kernel stack sizes for such workloads).

  1. 每个进程都有自己的内核堆栈吗?
  1. Does each process have its own kernel stack ?

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

Not just each process - each thread has its own kernel stack (and, in fact, its own user stack as well). Remember the only difference between processes and threads (to Linux) is the fact that multiple threads can share an address space (forming a process).

  1. 进程如何在这两个堆栈之间进行协调?
  1. How does the process coordinate between both these stacks ?

一点也不-不需要.调度(如何/何时运行不同的线程,如何保存和恢复它们的状态)是操作系统的任务,而进程不必为此担心.创建线程时(每个进程必须至少有一个线程),内核会为它们创建内核堆栈,而用户空间堆栈则由用于创建线程的任何机制(如makecontext()pthread_create()允许调用者指定要用于子"线程堆栈的内存区域)或继承(通过按访问内存克隆,在创建新进程时通常称为写时复制"/COW). br> 也就是说,进程 可以影响其线程的调度和/或影响 context (状态,其中是线程的堆栈指针).可以使用多种方法:UNIX信号setcontext()pthread_yield()/pthread_cancel(),...-但这与原始问题有些偏离.

Not at all - it doesn't need to. Scheduling (how / when different threads are being run, how their state is saved and restored) is the operating system's task and processes don't need to concern themselves with this. As threads are created (and each process must have at least one thread), the kernel creates kernel stacks for them, while userspace stacks are either explicitly created/provided by whichever mechanism is used to create a thread (functions like makecontext() or pthread_create() allow the caller to specify a memory region to be used for the "child" thread's stack), or inherited (by on-access memory cloning, usually called "copy on write" / COW, when creating a new process).
That said, the process can influence scheduling of its threads and/or influence the context (state, amongst that is the thread's stackpointer). There are multiple ways for this: UNIX signals, setcontext(), pthread_yield() / pthread_cancel(), ... - but this is disgressing a bit from the original question.

这篇关于内核堆栈和用户空间堆栈的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆