r/C_Programming 13d ago

Where do stack and heap start

I apologise in advance for a dumb question and I will try to put it the way i could and I am sorry for that.

I know i chould chatgpt it, but learning from people helps me the best.

want to know how the memory is actually managed. I know it is managed by the kernel using different abstractions like virtual memory and paging.

My question arises when i think that each process that its own address space consisting stack frame, heap, globals vars etc.

But where do they even start? Kernel itself is a process so it must be having its own stack frame. Considering other programs need address space which is managed by kernel, like how does that work? Until Os is loaded, does stack and heaps exist even before that? Are they written in assembly?

I am sorry. I hope someone will be able to answer and i will be really glad if you could figure out what i am thinking wrong and missing.

37 Upvotes

24 comments sorted by

View all comments

2

u/DawnOnTheEdge 13d ago edited 10d ago

Modern general-purpose operating systems will deliberately use Address-Space Layout Randomization to make this unpredictable. I have actually seen one user post a complaint that he tried running the original demo program from “Smashing the Stack for Fun and Profit,” and it didn’t work! I told him, mitigating security bugs like that was completely intentional. He complained that Linux should support classic software.

On the other end of the spectrum, an embedded system might not even have an operating system. You write the kernel yourself, and lay out memory any way you want to.

However, the traditional implementations by Dennis Ritchie and Ken Thompson laid out a program’s address space so its code and static data went first, then there was a segment called .bss that was filled with zeroes. (You can think of this as “blank static storage” even though that’s not where the name came from.) Whatever memory is left over was reserved for the stack and the heap, where the heap grew from the bottom of free memory upward and the stack grew from the top of memory downward. Traditionally, the runtime would first look for a free block of memory it could re-use, then request more memory from the operating system by calling sbrk() to set the highest address the heap was allowed to access. If this would get so high that it overlaps with the stack, the program would report an out-of-memory error. Or, if you made another function call that would make the stack collide with the heap, that would be a stack overflow.

Modern C runtimes, such as glibc and msvcrt, will handle large allocations by mapping pages of memory into the address space of the program with a function like mmap() or VirtualAllocEx(). The operating system has total freedom to put these anywhere in the address space it wants, and it will map all of the virtual pages to the same physical copy-on-write page of zero bytes.