在程序执行之前,esp
寄存器何时设置为指向有效地址?在致电exec
期间?还是在用户空间本身?我已经完成了内核代码,似乎无法在任何地方找到它。
答案 0 :(得分:4)
<强>背景强>
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的评论向您展示了这样做的位置。)