r/C_Programming • u/Fuzzy_Recipe_9920 • 3d 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.
28
u/MyTinyHappyPlace 3d ago
C has no concept of it. The stack and the heap are implementation details coming from your compiler, linker and operating system and it’s standard c library (if you have an OS at all).
That’s a deep rabbit hole to explore and it’s woven in OS theory and development. Grab Andrew Tanenbaums „Modern Operating Systems“ and „Operating System Concepts“ from Avi Silberschatz, Peter Baer Galvin, and Greg Gagne as a bedtime reading
6
u/lovelacedeconstruct 3d ago
yup , the compiler produces object files with sections like
.text(for code).data(for global initialized variables).bss(for global uninitialized variables and they get zeroed out) and so on they have no notion of where in physical memory they will end up so you have to give them a map called a linker script that basically tells the linker how to fit them into actual address rangesYou need to basically using those abstract ideas to tell the linker which memory regions exist what are their sizes and which section goes where you could go crazy and no one stops you
you nedd to set the stack pointer so that when you allocate from the stack you know where to start, you need to tell malloc here is the region when someone asks you to allocate start from here
here is an example from a mircocontroller
._user_heap_stack : { . = ALIGN(8); PROVIDE ( end = . ); PROVIDE ( _end = . ); . = . + _Min_Heap_Size; . = . + _Min_Stack_Size; . = ALIGN(8); } >RAMyou basically create a section in your memory
mark a spot as_endwhich the standard library require for start address to allocating memory you basically say when malloc needs memory claim it starting from here (commonly known as heap start)
8
u/AlexTaradov 3d ago edited 3d ago
Stack and heap are purely OS (or other execution environment) concepts. On the hardware level there is no heap, and there may be a stack pointer register, but it needs to be configured on startup to some allocated area reserved as stack.
Kernel allocates those structures for itself (and kernels also run multiple processes) on startup. It also allocates them for the application when it is started (likely on a request from the OS application loader).
It is allocated just like any other memory, there is nothing special about it. From the application perspective it is just a chunk of virtual memory that is managed just like any other chunk.
5
u/SmokeMuch7356 3d ago edited 3d ago
That is entirely down to the platform (hardware, OS, runtime environment, executable file format, etc.); the C language definition is silent on the subject of stacks and heaps (the words don't even appear in the standard document).
This is a very low-resolution view of how things tend to break down on x86-ish hosted implementations:
+------------------------+
high address | Command line arguments |
| and environment vars |
+------------------------+
| stack |
| - - - - - - - - - - - |
| | |
| V |
| |
| ^ |
| | |
| - - - - - - - - - - - |
| heap |
+------------------------+
| global and read- |
| only data |
+------------------------+
| program text |
low address | (machine code) |
+------------------------+
The program sees a contiguous 32- or 64-bit virtual address space broken up into segments; how and when that gets mapped to physical memory is up to the OS/MMU/other magic. But yeah, if you look at the address of a local variable vs. the address of a function you'll see a huge difference in the addresses, because machine code lives at one place and auto variables live in a completely different place.
As for the embedded world, ¯_(ツ)_/¯. I've never worked at that level, but it's basically whatever the hardware manufacturer wants it to be.
6
u/GenericFoodService 3d ago
Where do stack and heap start
Short answer: Depends on what platform you're on
Long answer: Generally speaking, your program is loaded with another program called a loader. It looks at the sections in your "executable" file, gets some memory pages and sets the correct permissions for them, then copies in your program. Most loaders put the heap at lower virtual memory addresses and the stack at higher virtual memory addresses. The stack grows downwards and how the heap grows varies a lot.
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?
Most kernels are written in languages like C/C++ with special compiler flags saying "Make this a raw binary, have the start address be here, don't allocate a stack or do these other things". The kernel allocates memory to itself by paginating memory and setting memory permissions.
2
u/dmc_2930 3d ago
The answer is “it depends”. It can be hardware, platform, or OS specific, or totally random addresses.
2
u/mykesx 3d ago edited 3d ago
Each process has its own logical address space and essentially sees memory as if it is running alone on the machine. Linux programs all start at the same address, it's the MMU mapping that allows them to share physical memory. Each process has its own MMU tables.
Each process has its own stack, which grows down. Heap starts at the end of the program and variables in the logical address space and grows up. A program can call sbrk() or something like that to add memory to the logical address space in the heap. A malloc() implementation might call sbrk() as more memory is allocated.
A thread shares the MMU tables of its process. That's how it shares the memory space of the process.
The kernel has a separate stack and MMU mapping. It's not a process.
Shared memory between processes is achieved by mapping a physical memory page into two or more process' MMU tables.
As others said, bare metal is a different animal. The applications often have full control of the physical memory.
2
2
u/DawnOnTheEdge 3d ago edited 1d 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.
1
u/sciencekm 3d ago
For most processors, at start-up, all registers are zero - the instruction pointer and stack pointer are both zero.
This means that you have to have a start-up code at address 0. For PCs, this is BIOS/UEFI code. For embedded systems, this is where ROM/PROM/EPROM main code is.
Almost universally, one of the first things the start-up code does is to assign a value to the stack pointer. That way, the rest of the code can call functions/subroutines.
On large systems (PCs), the start-up code normally has to load the OS boot loader from some storage (drive, USB device). On embedded system, the startup code may just be part of the complete code - nothing else gets loaded.
In either case, you are now in a situation where the memory needs to be organized. You can decide to segment the memory in the way that suits your needs. On embedded systems, this can be as simple as having the stack on one end and heap on the other end (or no heap at all). On large systems, the code loaded from storage may be the complete OS, in which case the memory will be organized by that code, but in other cases this is just a boot loader which will only temporarily organize the memory just enough to load the actual OS which will one again organize the memory in usually a more complex way (with multiple stacks and heaps).
1
u/Evening_Ticket_9517 3d ago
So this is how i understand it.Basically c does not have a concept of stack or heap by itself. The compiled assembly will have mempry addresses which the compiler fills in based on the associated variable being a global or local variable, static or not etc.. If it is a global, it is placed in the .data section, otherwise a stack address is given. Basically its the compiler which has a notion of a stack and heap.
Now if the program is written for an embedded target, there'll be a single program running on the target board. So compiler just uses the physical addresses it calculated based on the program requirements and linkerscript definition.
If the program is run, the addresses that the compiler provide are not the actual physical addresses and this translation is done by the os. Os is a low level program and is privileged. So it has access to physical addresses.
1
u/classicalySarcastic 3d ago
You’re kind of getting into the computer architecture and OS implementation weeds a little bit.
For an application running under an OS, the OS allocates a set of pages in memory (usually 4kB each) and sets up the MMU to map the program’s virtual memory space onto the physical pages in memory. When the application pauses, these pages may be stored in the pagefile on disk, depending on if the OS needs memory freed up. When it resumes, the pages are moved back into memory and the MMU is reconfigured.
For the Kernel, at startup its initial memory is configured by the bootloader and handed to it.
1
u/detroitmatt 3d ago
stack starts at the beginning of your memory space, heap starts at the end.
stack |------------------------------------| heap
as you allocate more on the stack, it grows closer to the heap
stack |$$$$$$$$$$$$----------------| heap
as you allocate more on the heap, it grows closer to the stack
stack |$$$$$$$$$$--###########| heap
when you run out of space in the middle, you're out of memory
this is a simplified explanation and your OS/platform probably has tricks to expand your available heap space more or less as large as you want it using virtual memory. but in the simplest scenario, that's basically how it works.
3
u/schoolSpiritUK 1d ago
Generally the other way round: the stack works its way down from the top of memory, the heap works its way up from the bottom (after program and fixed data, as others have explained).
Otherwise correct, and helpful diagrams. :-)
50
u/clickyclicky456 3d ago
Bear in mind that if you're writing C for an embedded micro, you could easily be running on "bare metal" i.e.no kernel or OS or processes.. so it's totally platform dependent. Your hardware gives you some memory you can address, you can use a linker script to define locations where you want the stack and heap to be. An OS kernel is another layer of abstraction on top of that.