操作系统参与堆栈操作
Posted
技术标签:
【中文标题】操作系统参与堆栈操作【英文标题】:OS involvement in stack operations 【发布时间】:2020-09-26 15:58:04 【问题描述】:就我understand 而言,操作系统经常参与管理堆及其簿记。例如,用户程序的进程需要调用系统调用才能从堆中分配和释放内存。
这对 堆栈 有何作用?据我所知:
CPU 处理器提供推送和弹出指令以从堆栈中添加/删除项目。这给了我一种(可能是错误的)印象,即用户程序可以直接编译为汇编代码以执行不涉及操作系统的堆栈推送/弹出 CPU 指令 操作系统监督堆栈内存,至少限制其大小,所以我理解它必须知道并在用户程序尝试使用堆栈时参与其中(例如,推送和弹出调用堆栈帧等)。我上面的理解正确吗?
更广泛地说,操作系统是否以任何方式参与了涉及堆栈的运行时操作? (例如弹出和推送新的堆栈帧、它们的组织等),如果是这样,怎么做?
还是操作系统只参与提前分配整个任务/线程/进程的栈内存?
【问题讨论】:
为什么用java、c++或c标记?如果它们之间存在差异,您是否感兴趣? 这能回答你的问题吗? “操作系统在创建线程时为每个系统级线程分配堆栈。通常语言运行时调用操作系统为应用程序分配堆。” @TedKleinBergman 谢谢 - 当程序弹出或推送数据到堆栈时,操作系统不会以任何方式参与吗? 如果操作系统必须在每次推送或弹出出现时进行干预,它会降低性能。为什么你会有操作系统负责堆栈的印象? 了解分页和内存管理。 【参考方案1】:这给我一种(可能是错误的)印象,即用户程序可以直接编译为汇编代码以执行不涉及操作系统的堆栈推送/弹出 CPU 指令
确实如此。
我理解它必须了解并在用户程序尝试使用堆栈时参与其中(例如,推送和弹出调用堆栈帧等)。
不是真的。操作系统不需要参与每一个 push/pop 或堆栈上的任何其他类型的读/写操作,事实上它不是。如果您考虑一下,每次访问堆栈时都请求操作系统干预会非常缓慢且适得其反。
操作系统是否以任何方式参与涉及堆栈的运行时操作? (例如,弹出和推送新的堆栈帧、它们的组织等),如果是,如何?
不,不是。管理堆栈是拥有它的进程的职责。
如果堆栈操作是直接在 CPU 级别从已编译的汇编代码中完成的,那么操作系统可能只在程序启动时参与“描绘”堆栈的虚拟地址范围?
是的,没错,你快到了。
当一个新的进程被创建时,内核会为栈保留空间,并且它也会在栈的最底层复制一些数据,比如命令行参数。所有这些都在开始流程开始之前完成。
堆栈操作(例如最简单的 push 和 pop)直接由 CPU 完成。换句话说,像 push 和 pop 这样的指令是直接执行的。
操作系统仅在必要时在某些情况下进行干预。一个(非详尽的)列表:
当指令(例如,推送)导致进程读取或写入超出堆栈末尾时,CPU 会生成异常。操作系统(在启动时注册了一个异常处理程序)捕获此异常并根据需要进行处理。通常这会导致尽可能增加堆栈大小,否则会终止进程。用户进程随即恢复,就好像什么都没发生一样。 进行系统调用时,操作系统会将所有用户寄存器临时保存到堆栈中,然后执行其处理系统调用的工作,然后恢复所有内容。 当一个信号被传递给一个进程,并且该进程注册了一个信号处理程序时,同样的事情会发生:寄存器被保存到堆栈中,然后使用正确的参数调用信号处理程序,当它返回之前的状态时已恢复并继续该过程。【讨论】:
谢谢! “进行系统调用时,操作系统会暂时将所有 用户 寄存器保存到堆栈中,然后执行其处理系统调用的工作,然后恢复所有内容。” 这很有趣,为什么此时需要为 user 注册者这样做?那是因为内核在处理完系统调用后可能不会将 CPU 还给同一个进程吗? @Josh 当我说“用户”寄存器时,我只是指在进入内核之前的那个特定时刻(在用户空间中)寄存器所具有的值。真的没有“用户”注册这样的东西。内核使用与用户进程相同的 CPU 寄存器,因此需要在开始执行任何操作之前保存它们的值,然后再恢复它们。可以将执行权交给另一个进程这一事实与此无关。 关于此说明 - 当您说 “当指令(例如推送)导致进程读取或写入超出堆栈末尾时,CPU 会生成异常”。这真的是 CPU 的 exception 吗?什么类型的异常?它是CPU的硬件中断吗?内核的软件中断?它真的来自CPU本身吗? @Josh 这是一个由 CPU 本身产生的异常,更准确地说是由作为 CPU 的一个组件的内存管理单元 (MMU) 产生的。准确地说,这是一个Invalid Page Fault 异常。它由内核通过do_page_fault()
处理。另请参阅了解 Linux 内核 chapter 4.5 和 9.4。以上是关于操作系统参与堆栈操作的主要内容,如果未能解决你的问题,请参考以下文章