什么时候esp寄存器设置在Linux?

在程序执行之前, esp寄存器何时被设置为指向有效地址? 在调用exec期间? 或者在用户空间本身? 我已经通过了内核代码,似乎无法find任何地方。

背景

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函数完成。

直接回答你的问题

当一个新的任务被创建时,两个堆栈(用户和内核堆栈)被分配给该任务,并且在中断中被初始化的注册值被写入内核堆栈。 这包括用户模式的esp的初始值。

有些计时器会在第一次启动任务之后中断(与已经运行的任务重新启动的方式相同)。

在(旧版本的)Linux中,有两个命令用于创建新的任务:

  • fork()将简单地复制内核堆栈。 fork()会复制一个已经存在的任务,所以所有的寄存器值(包括esp )必须与已经存在的任务完全相同
  • execve()将不会分配一个新的内核堆栈(现在新的任务被创建,但另一个可执行文件正在当前任务中运行)。 Execve将分配一个新的用户堆栈并覆盖内核堆栈上的esp值。 (Mark Plotnick的评论向你展示了完成这个任务的位置。)