TL;DR: You don't need to worry about
malloc() in your OS dev project, because
malloc() isn't part of the OS in the first place. You won't need it until you start writing applications, and by the time you do, you'll probably have started porting GNU LibC to your OS and you'll just use the one that comes with that for most purposes. There's no reason whatsoever for you to write your own general-purpose
malloc() unless you're re-implementing the whole C library, at which point you have bigger worries than just that.
Also, ignore everything ~ says.
Now on to the overly-long version.
Thpertic wrote:Schol-R-LEA wrote:It is crucially important not to confuse the user-space memory allocation functions with those in the kernel, too. The kernel memory allocator - that is to say, the one which allocates memory for the kernel itself - has very different requirements and priorities from either the user-space allocator, or the userspace memory allocator which the system calls such as sbrk() operate on. The kernel allocator must come first, and must have priority over any other subordinate allocator, though in practice they are mostly disjoint. Broadly speaking, the kernel allocator works on kernel memory with no real overlap to userspace, the system call works on user-space memory at a coarse-grained level, the user functions manage the user-space memory at a finer-grained level. While the top-level kernel allocator still has to decide the virtual-to-physical mapping of all of the pages, and decides which pages can be used for mapping a given piece of user addressing to, its main concern is with its own memory, period. The kmalloc() within the kernel should be separate both from the VM->PM mapping, and the user-space mapping.
All of this is to say that, regarding the user-space version of malloc(), you generally want to have it do as much of the memory management for a user space process in userspace itself.
Ehm ok... the kernel must have the priority and kmalloc() and malloc() have to be different as the first has to work for the kernel but be prepared to map user space (didn’t really got this one) too while the second has just to map the user space. Could you please give an example?
OK, I wasn't very clear, I think. I should have said 'system memory manager' rather than 'kernel allocator', as it is much more than just
kmalloc() (or whichever function or functions which allocate memory for the kernel's own use in your design - even for a POSIX implementation, you aren't obligated to use the Unix design or naming conventions for the internal parts of the kernel). The system memory manager isn't necessarily a single unit so much as a system-wide allocation policy and the collection of functions which implement it, covering all system-wide memory management including the vm->pm mapping and where to page in and page out.
The thing is, the user-space functions
malloc() and
free() aren't part of that at all -
malloc() and related allocation functions such as
calloc() make requests to the kernel from memory (
sbrk() in most conventional POSIX implementations, or at least older ones which are more readily available), but they are purely library functions, not kernel operations. They are not part of the OS at all, and you only need them when implementing the
C standard library's memory management - they are orthogonal to the OS itself, and the memory management which they do is only inside the user process, for that user process itself.
Technically speaking, your system doesn't need
[m|c|r]alloc()/
free() at all, if you only have user applications written in languages other than C (where the libraries are not themselves implemented in C either), or you only allow static memory allocation in user processes. This scenario is pretty absurd for a POSIX system, admittedly, but the point is that they aren't OS functions at all. You don't need to even think about them until you are porting the C library for your userspace applications.
Regarding
sbrk(), note that there is the system call referred to as
SBRK in the formal Unix/POSIX documentation, and the
sbrk() library function, which is a thin wrapper around the system call. Most of the time we are talking about both at once, since the functions (or in some implementations, macros) are basically the same as the system calls regardless of the actual system call mechanism, but it is worth bearing in the back of your mind that what your are seeing with the
sbrk() isn't the system call as such.
Now, the BRK and SBRK system calls only matter if you are strictly implementing POSIX at the OS level -
sbrk() is purely Unix thing and you have no obligation to implement it at all unless you are building a Unix. If you are writing a new OS which isn't a POSIX implementation on the Application Binary Interface (ABI) level, but you intent to provide a POSIX compatibility layer, your own kernel interface can work however you want it to be so long as you provide a set of wrappers (whether as alternative system calls, or more realistically, as library functions) which behave the same as the POSIX Application Programmer Interface (API) - which might include a
sbrk() library function, or not, since (as you noted yourself)
sbrk() has been deprecated on most major current Unices (Linux, FreeBSD, MacOS) for some time now with
mmap() being the modern equivalent since the late 1980s.
Note that strictly speaking, the purpose of
mmap() isn't memory allocation, but demand paging - it maps a disk file into virtual memory - but because allocation is a part of this, IIUC it has become the basis for memory allocation strategies on several POSIX systems.
Why? Because BRK and SBRK are too simplistic, and - among other things - provide no means for returning memory to the OS during the lifetime of the process (technically, BRK sort of does, but it's never used that way AFAICT). With the newer design, the companion function
munmap() allows a process to return memory to the system.
mmap() also allows the process to request certain memory properties or the allocated memory (e.g., read-only, shared with other processes, etc.), and there are also three additional system calls,
mprotect(),
msync(), and
madvise() for more fine-grained manipulation of memory which was already mapped.
There are some aspects of
mmap() which are problematic for a simple implementation, as well, regarding whether successive pages are guaranteed to be contiguous or not, but it sounds as if they can be worked around.
(Comments and corrections welcome; POSIX hasn't exactly been a major concern for me, since my implementation language and OS design plans do not resemble C and Unix in the slightest.)
Thpertic wrote:Schol-R-LEA wrote:The K&R implementation is very bare-bones, even for its time, and is meant only as a minimal example of how to make a userspace allocator; [...]
Yes, I am looking at this version and I found it really interesting mostly because it’s pretty straightforward and easy to understand. I couldn’t really find any other
malloc() implementation that didn’t use
sbrk() and the skeleton of this example so I stuck with it.
I thought that it only needed an allocator that starting from an address (for example at the end of the kernel) it keeps mapping there as an heap should do.
OK, that's an understandable problem, as most tutorials do still use
sbrk() for the sake of simplicity (and also because most of them are out of date, and were based on information which was already out of date to begin with).
While we normally don't suggest this (since production-grade code tends to be a mess, or at least hard to follow as it does a lot of things which are confusing but pragmatic), if you really want to write a modern
malloc(), you'll want to look at more robust implementations such as the one which comes with
glibc, or even at some of the high-performance versions such as
DLmalloc() (a somewhat dated overview of which can be found
here, with additional material for it on
Doug Lea's home page),
JEmalloc() (which is the FreeBSD version), or
TCMalloc(). Don't expect these to be readily understandable, however, especially the last three, as they are Heavy Wizardry and do some things which are hard to follow without a spending a few hours studying them. Also, the high-performance ones are more specialized, and
may not be ideal for all uses.
And of course, any given C program can have a handrolled memory allocator - or even a conservative garbage collector, such as the
Boehm Collector - rather than using the standard functions, and implementations of other languages likewise, but that's going a bit off-topic.
Some additional theory can be found
here, to clarify the whole behavior of memory in a running program.
So why do all of these implementations of
malloc() which you see in tutorials use
sbrk()? Because the writers were lazy, and didn't want to have to clutter up the tutorial with the more complex interface provide by MMAP and MPROTECT. To repeat
what the Wiki says over and over again: There are no true tutorials for OS development, and the ones which are out there claiming to be are critically flawed. They can be a guide to OS development, and help you understand what needs to be done, but in a very real sense, once you have your development and testing platforms set up and can start designing your OS, you are on your own. We can give you advice, and the wiki can provide information, but the OS is yours.