Malloc hook in Skynet
Last updated on 7 minutes ago
[Skynet] Understanding malloc hook
Intro
When I first encoutered skynet_malloc.h
in skynet's
source code, I was much confused by this design:
- Why does the header both define marcos(e.g.
skynet_malloc
)and declarations(e.g.void* skynet_malloc(size_t sz)
)? - Why is there no
skynet_malloc.c
file to provide these definitions?
Additionally, while malloc_hook.h
and
malloc_hook.c
form a compilation unit.
malloc_hook.h
does not declare skynet_malloc
,
instead, malloc_hook.c
defines it. If we just simply treat
it as deplication, when we try to remove this function, although we can
pass compilation safely, we cannot run skynet framework.
In this article, I aim to provide a clear and understandable explanation of this design. This article is relatively short, as the concept may seem complex at first but is actually quite simple.
Description
Let's first example the content of skynet_malloc.h
1 |
|
As we know, marcos essentially perform a text replacement. So, when
another source file includes this header, all instances of
skynet_malloc
will be replaced by malloc
. In
other words, the content of skynet_malloc.h
after
preprocessor becomes:
1 |
|
Although there are function names, they are still replaced by
preprocessor. To verify this, we can check whether
skynet_malloc
is replaced by malloc
by
processing a source file with preprocessor.
The skynet_mq.c
is a good example, we can run the
following command:
1 |
|
In this file, skynet_mq_create
calls
skynet_malloc
, but after preprocessoing,
skynet_malloc
is replaced with
malloc
. Now, we can draw a prelimilary conclusion
that, all instances of skynet_malloc
are replaced by
malloc
. Furthermore, any file including
skynet.h
will also have this replacement.
Additionally, malloc_hook.c
defines
skynet_malloc
. This symblo, too, is replaced by
malloc
during preprocessor. Essentially, this
definition prepares for the definition of malloc
rather
than skynet_malloc
.
By combining these conclusions together, we can deduce:
- If a file wants to invoke
skynet_malloc
, it must includeskynet_malloc
. - Since
skynet_malloc.h
defines the marcos and declares forskynet_malloc
, so any instances ofskynet_malloc
are replaced bymalloc
. - Due to we also compile with
malloc_hook.c
together(we can check makefile to prove it), this unit is used to provide the definition ofmalloc
. - When
jemalloc
is enabled, this definition adds extra functionality to track memory usage.- Due to we compile
jemalloc
with prefixje_
(check makefile), this definition essentially callsjemalloc
interface.
- Due to we compile
We can further verify this by executing the following command to
check malloc_hook.c
:
1 |
|
This file does not contain the definition of
skynet_malloc
; instead, the original definition of
skynet_malloc
is replaced by the definition of
malloc
.
Conclusion
The final conclusions are:
- Skynet framework uses its internal memory allocation interface
skynet_malloc
to allocate memory. - At compile time, every instnace of
skynet_malloc
is replaced bymalloc
. - Without
jemalloc
,malloc
in standard library is used. - With
jemalloc
enabled, we can track extra information for memory usage and invokejemalloc
internally.- Additionally, only when we let
jemalloc
enabled,malloc
is re-defined, which can further confirming these conclusions.
- Additionally, only when we let
Example
Below is a simplified example illustrating this design with three files:
1 |
|
1 |
|
1 |
|
If we not compile with jemalloc, the output is:
1 |
|
Instead, if we compile with it, the output is:
1 |
|
Notes: In an earlier version of this example, I directly used
printf
insideinc_malloc
andinc_free
to print debugging information. This approach led to infinite loop becauseprintf
itself callsmalloc
internally.