内核栈空间和用户栈空间

在Linux系统中,每个线程都拥有独立的用户栈和内核栈空间。用户栈用于线程的正常执行,而内核栈在进行特权操作时使用,以确保安全性。内核栈不信任用户栈,因此在切换到内核模式时会使用自己控制的栈。中断服务例程中的局部变量将存储在内核栈上。每个线程的栈空间都是独立的,调度和协调由操作系统负责,而进程可以通过多种方式影响线程的调度和上下文。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

内核栈空间和用户栈空间 kernel stack and user space stack

简而言之,一个程序至少有一个进程,一个进程至少有一个线程. 线程的划分尺度小于进程,使得多线程程序的并发性高。
另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
同一个进程的多个子线程在进程的共享内存中分配独立的栈空间
栈:是个线程独有的,保存其运行状态和局部自动变量的。栈在线程开始的时候初始化,每个线程的栈互相独立,因此,栈是thread safe的。每个C++对象的数据成员也存在在栈中,每个函数都有自己的栈,栈被用来在函数之间传递参数。操作系统在切换线程的时候会自动的切换栈,就是切换SS/ESP寄存器。栈空间不需要在高级语言里面显式的分配和释放。
在Linux系统上每一个线程,实际上都有独立的2个栈空间,用户栈空间和内核栈空间。(注意是每个线程一个)
进程和线程在Linux上的唯一区别就是同一个进程的不同线程共享进程的地址空间,仅此而已。

http://stackoverflow.com/questions/12911841/kernel-stack-and-user-space-stack

What’s the difference between kernel stack and user stack ? 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.

Why is [ a separate ] kernel stack used ? 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).

If a local variable is declared in an ISR, where will it be stored ?
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).

Does each process have its own kernel stack ? 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).

How does the process coordinate between both these stacks ? 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.

### ARM架构中内核用户的区别及工作机制 在ARM架构中,内核用户是操作系统管理内存任务执行的重要组成部分。它们各自服务于不同的目的,并在不同的特权级别下运行。 #### 内核 内核是操作系统内核的一部分,用于存储内核级别的函数调用信息。每当一个进程从用户模式切换到内核模式时(例如,通过系统调用或中断),当前的上下文会被保存到内核中。这样可以确保当处理完内核模式下的操作后,能够正确地恢复到用户模式并继续执行原来的程序。 - 内核通常位于内核空间的高地址区域,以防止与用户空间的数据发生冲突。 - 每个进程都有自己的内核,其大小一般固定,由内核配置决定。 - 内核的管理由操作系统负责,用户程序无法直接访问或修改它。 #### 用户 用户则是每个用户进程私有的栈空间,用于存储用户程序中的函数调用信息。它随着用户程序的执行动态增长缩小。 - 用户位于用户空间的低地址区域,靠近程序代码数据段。 - 用户的大小可以根据需要动态调整,通常受限于系统的资源限制。 - 用户的增长方向通常是向低地址方向进行的,而内核则可能向高地址方向增长。 #### 工作机制 在ARM架构中,的工作机制涉及到处理器模式特权级别的切换。ARM处理器支持多种工作模式,包括用户模式、快速中断模式、外部中断模式、管理模式、数据访问终止模式、未定义指令中止模式以及系统模式等。 - 当用户程序执行系统调用或者遇到异常时,处理器会切换到相应的特权模式,并自动保存当前的状态到对应的内核中。 - 一旦进入内核模式,所有的操作都将使用内核来进行函数调用局部变量的分配。 - 完成内核模式下的处理后,通过恢复之前保存的状态,处理器返回到用户模式,并继续使用用户执行用户程序。 为了保证安全性稳定性,操作系统会对用户内核之间的转换进行严格的控制。例如,在Linux内核中,当进程执行系统调用时,会触发软中断,导致处理器切换到管理模式,并跳转到特定的处理例程,此时处理器会自动保存当前的寄存器状态到内核中[^2]。 ```assembly ; 示例伪代码展示系统调用进入内核的过程 ; 当用户程序执行svc指令时,处理器切换到管理模式 ; 并跳转到向量表中的SVC处理入口 SVC_Handler: ; 保存现场 mrs r0, spsr stmfd sp!, {r0-r12, lr} ; 调用具体的系统调用处理函数 bl do_svc ; 恢复现场 ldmfd sp!, {r0-r12, pc}^ ``` 这段伪代码展示了当用户程序执行`svc`指令时,如何进入内核模式并处理系统调用。可以看到,处理器会自动保存寄存器状态到内核中,然后调用处理函数,最后恢复现场并返回用户模式。 #### 总结 内核用户在ARM架构中扮演着不同的角色,分别服务于内核模式用户模式下的函数调用需求。它们之间的转换机制确保了系统的稳定性安全性,同时也支持了高效的上下文切换异常处理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值