0

As far as I understand, the OS is often involved in managing the heap and its bookkeeping. For example, the process of a user program needs to invoke a system call to be able to allocate and de-allocate memory from the heap.

How does that work for the stack? To my knowledge:

  • CPU processors provide push and pop instructions to add / remove items from the stack. This gives me the (perhaps wrong) impression that a user program may be compiled directly to assembly code to execute stack push / pop CPU instructions that don't involve the OS in any way
  • The OS supervises the stack memory, at least limiting its size, so I understand it must know and get involved the moment a user program is trying to work with the stack (e.g. pushing and popping call stack frames, etc).

Is my understanding above correct?

More broadly speaking, is the OS involved in any way in runtime operations involving the stack? (e.g. popping and pushing new stack frames, their organization, etc.), and if so, how?

Or is the OS only involved in allocating the stack memory of the entire task / thread / process ahead of time?

Josh
  • 11,979
  • 17
  • 60
  • 96
  • Why is it tagged with java, c++ or c? Are you interested if there are differences between them? – Ted Klein Bergman Jun 07 '20 at 18:01
  • Does this answer your question? _"The OS allocates the stack for each system-level thread when the thread is created. Typically the OS is called by the language runtime to allocate the heap for the application."_ – Ted Klein Bergman Jun 07 '20 at 18:04
  • @TedKleinBergman Thanks - Does the OS **not** get involved in any way at all when the program pops or pushes data to the stack? – Josh Jun 07 '20 at 21:00
  • If the OS had to intervene every time a push or pop appear it will degrade performance. Why do you have the impression the OS is responsible for the stack? – Tony Tannous Jun 07 '20 at 21:03
  • Read about paging and memory management. – Tony Tannous Jun 07 '20 at 21:05

1 Answers1

1

This gives me the (perhaps wrong) impression that a user program may be compiled directly to assembly code to execute stack push / pop CPU instructions that don't involve the OS in any way

Indeed that's right.

I understand it must know and get involved the moment a user program is trying to work with the stack (e.g. pushing and popping call stack frames, etc).

Not really. The OS does not need to get involved in every single push/pop or any other kind of read/write operation on the stack, and in fact it isn't. If you think about it, requesting OS intervention at every single stack access would be painfully slow and counterproductive.

is the OS involved in any way in runtime operations involving the stack? (e.g. popping and pushing new stack frames, their organization, etc.), and if so, how?

No, it isn't. Managing the stack is a duty of the process that owns it.

if stack operations are done directly at the CPU level from compiled assembly code, the OS is only perhaps involved at the startup of the program in "delineating" the virtual address range for the stack?

Yes, exactly, you are getting there.

When a new process is created, the kernel will reserve space for the stack, and it will also copy some data like command line arguments at the very bottom of the stack. All of this is done before starting the process.

Stack operations (for example push and pop, which are the simplest ones), are done directly by the CPU. In other words, instructions like push and pop are directly executed.

The OS only intervenes in certain situations when necessary. A (non exhaustive) list:

  • When an instruction (e.g. a push) causes the process to read or write past the end of the stack, the CPU generates an exception. The OS (which registered an exception handler at startup) catches this exception and handles it as needed. Normally this results in incrementing the stack size if possible, otherwise killing the process. The user process is then resumed as if nothing happened.
  • When a syscall is made, the OS temporarily saves all user registers to the stack, then does its job to handle the syscall, and then restores everything.
  • When a signal is delivered to a process, and the process registered a signal handler, the same thing happens: registers are saved to the stack, then the signal handler is called with the right arguments, and when it returns the previous state is restored and the process resumes.
Marco Bonelli
  • 63,369
  • 21
  • 118
  • 128
  • Thanks! _"When a syscall is made, the OS temporarily saves all **user** registers to the stack, then does its job to handle the syscall, and then restores everything."_ That's interesting, why does it need to do that for _**user**_ registers at that moment? Is that because the kernel may not give the CPU back to the same process after taking care of the syscall? – Josh Jun 07 '20 at 23:05
  • 1
    @Josh When I say "user" registers I only mean the values that the registers have at that particular moment (in userspace) before entering the kernel. There is no such thing as "user" registers really. The kernel uses the same CPU registers as the user process does, and therefore needs to save their values before starting to do anything, and restore them afterwards. The fact that the execution could be given to another process is unrelated to this. – Marco Bonelli Jun 07 '20 at 23:26
  • On this note - When you said _"When an instruction (e.g. a push) causes the process to read or write past the end of the stack, the CPU generates an exception"_. Is this really an *exception* from the CPU? What type of exception? Is it a HW interrupt for the CPU? A SW interrupt for the kernel? And does it actually come from the CPU itself? – Josh Jul 06 '20 at 14:57
  • 1
    @Josh it's an exception generated by the CPU itself, more precisely by the memory management unit (MMU), which is a component of the CPU. It's an [Invalid Page Fault](https://en.wikipedia.org/wiki/Page_fault) exception to be precise. It's handled by the kernel through [`do_page_fault()`](https://elixir.bootlin.com/linux/v5.7.2/source/arch/x86/mm/fault.c#L1522). See also Understanding the Linux Kernel [chapter 4.5](https://www.oreilly.com/library/view/understanding-the-linux/0596005652/ch04s05.html) and [9.4](https://www.oreilly.com/library/view/understanding-the-linux/0596005652/ch09s04.html). – Marco Bonelli Jul 06 '20 at 16:51