在执行程序之前,esp 寄存器何时设置为指向有效地址?在调用 exec 期间?还是在用户空间本身?我已经浏览了内核代码,但似乎在任何地方都找不到。

最佳答案

背景

x86 CPU 有两个(实际上是四个)堆栈(每个任务):一个用于用户模式,一个用于内核模式。

当在用户模式下发生中断时,CPU 会将 esp 设置为内核堆栈的地址(有关更多信息,请参阅“TSS”)并将 esp 的原始值(用户模式堆栈的位置)推送到(内核)堆栈。 eipcseflags 总是在中断发生时压入堆栈。

从中断返回时,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/

    10-11 22:13
    查看更多