Does malloc needs OS support?

Viewed 2727

Memory management is a service provided by underlying operating system. When we call malloc()/free() and there's no operating systems running(for example a bare metal embedded system), how is the memory allocation and tracking handled?

There should be an entity that tracks which addresses are free and which are not. That's OS memory management unit. malloc()/free() will then have to call OS system calls. So no OS means no malloc()/free(). Am I wrong in assuming this?

Update:

All answers pointed out that malloc/free can use either static pool allocation(when no OS is available) or use sbrk/brk which are kernel system calls. Question is how does the malloc/free knows if there's a kernel beneath or not?

Answer(see comment by "Kuba Ober" under his answer below):

malloc doesn't need to know anything, because the C library that you link your project with is specific to the target: if you develop for Linux, you use a C library for Linux, different than when you develop for OS X, or Windows, or bare bones ARM Cortex M0. Or, heck, barebones x86. It's people who write the C library that know how to implement it so that it works on the desired target. For example, a barebones C library for x86 would use EFI and ACPI to query the list of available blocks of RAM unused by hardware nor BIOS, and then use those in fulfilling allocation requests.

3 Answers

malloc() and free() do not require OS support. They can be (and often are!) implemented on bare-metal systems. For example, the Arduino library uses malloc() and free() to manage strings.

In hosted implementations (that is, an application running on an operating system), malloc() and free() will typically use operating system services to allocate new "hunks" of memory -- often as much as a few megabytes at a time -- and to return those hunks to the operating system when they are unused. Smaller allocations are handled by cutting those blocks of memory into the sizes needed by an application. This allows small allocations to be managed without the overhead of a system call.

In an unhosted implementation (like a bare-metal system), the application already has access to all memory in existence on the system, and can parcel out chunks of that memory however it likes.

At a lower level: both hosted and unhosted implementations of malloc() often work by treating each allocated or unallocated block of memory as an entry in a linked list. This is typically accomplished by storing a structure immediately before the start of each allocation, e.g.

struct malloc_block {
    struct malloc_block *prev, *next;
    size_t size;
    ...
    char allocation[];
};

and returning a pointer to allocation as the return value of malloc(). Functions like realloc() and free() can retrieve the structure by subtracting the size of the structure from a pointer to the allocation.

The Malloc/free function manage a pool of memory. These functions are generally not operating system services.

How then is that pool created?

On most systems, malloc calls operating system services to map pages into the the process address space to create and expand the memory pool. If you call malloc, and no memory is available, most implementations will call a system service to map more memory and expand the pool.

The malloc implementation needs to maintain data structures that keep track of what memory in the pool is free and what has been allocated. This is done in many different ways. It is not unusual for programmers to select a malloc/free combination that works best for them and link it into their application.

So, yes there is operating system involvement—generally.

But you asked about whether they can be implemented without an operating system.

Suppose you did:

   static char pool [POOLSIZE] ;

in your malloc implementation. Then you would not require a system service to create the pool during execution time. On the other hand, your pool has a fixed size.

Generally speaking: no, or at least not at runtime, if we define runtime as the moments between main() entering and returning.

Suppose you implement malloc that operates on a fixed-size pool:

static char pool[MALLOC_POOL_SIZE];

void *malloc(size_t size) {
  …
}

void free(void *block) {
  …
}

Then, on both hosted- and unhosted implementations you have working dynamic memory allocation. On hosted implementations, the binary loader will ensure that there’s memory mapped behind the pool. On unhosted implementations, the linker will pre-locate the pool in available RAM, and linking will fail if the pool is too large.

So no, in general no OS involvement is needed once your code is running, but OS involvement is needed to get your code to run in the first place (if there is an OS).

And of course, “not needed” means not necessary, but doesn’t exclude OS support for dynamic memory allocation in a particular C runtime library. In most hosted runtimes, malloc uses a pool that’s dynamically expanded (and perhaps contracted) by invoking relevant OS APIs. On classic Unix, the expansion would be done via the brk syscall.

Related