在执行程序之前,esp
寄存器何时设置为指向有效地址?在调用 exec
期间?还是在用户空间本身?我已经浏览了内核代码,但似乎在任何地方都找不到。
最佳答案
背景
x86 CPU 有两个(实际上是四个)堆栈(每个任务):一个用于用户模式,一个用于内核模式。
当在用户模式下发生中断时,CPU 会将 esp
设置为内核堆栈的地址(有关更多信息,请参阅“TSS”)并将 esp
的原始值(用户模式堆栈的位置)推送到(内核)堆栈。 eip
、 cs
和 eflags
总是在中断发生时压入堆栈。
从中断返回时,iret
指令将从(内核)堆栈中弹出“旧”寄存器值,堆栈指针将再次指向用户堆栈。
抢占式多任务操作系统通常按以下方式工作:
某些任务正在运行,这意味着该任务在非常短的时间内占用了 100% 的 CPU 负载。当定时器中断发生时,当前运行任务的寄存器值存储在堆栈中(由 CPU)。操作系统将对所有其他寄存器的值进行 push
并将 esp
值更改为另一个任务的内核堆栈(在另一个定时器中断发生时保存)。然后它对寄存器进行 pops
并执行 iret
,因此所有寄存器都包含另一个任务的值,并且另一个任务正在运行。
在 Linux (4.12.2)、x86-32 中,这是由汇编源代码“entry_32.S”中的函数 __switch_to_asm
完成的。
直接回答您的问题
创建新任务时,会为该任务分配两个堆栈(用户堆栈和内核堆栈),并将中断中 poped
的初始寄存器值写入内核堆栈。这包括用户模式的 esp
初始值。
一些定时器中断稍后任务第一次启动(与重新激活已经运行的任务的方式相同)。
在(旧版本)Linux 中,有两个命令用于创建新任务:
fork()
将简单地复制内核堆栈。 fork()
将复制现有任务,因此所有寄存器值(包括 esp
)必须与现有任务 execve()
不会分配新的内核堆栈(现在新任务已创建,但另一个可执行文件正在当前任务中运行)。 Execve 将分配一个新的用户堆栈并覆盖内核堆栈上的 esp
值。 (Mark Plotnick 的评论向您展示了完成此操作的位置。) 关于linux - 什么时候在linux中设置esp寄存器?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/45130190/