{"id":179,"date":"2024-01-09T11:18:00","date_gmt":"2024-01-09T14:18:00","guid":{"rendered":"https:\/\/suspensao.blog.br\/disbelief\/?p=179"},"modified":"2024-01-16T16:08:39","modified_gmt":"2024-01-16T19:08:39","slug":"implementing-malloc-and-free-1","status":"publish","type":"post","link":"https:\/\/suspensao.blog.br\/disbelief\/implementing-malloc-and-free-1\/","title":{"rendered":"Implementing malloc() and free() \u2014 first steps"},"content":{"rendered":"\n<p>Following the <a href=\"https:\/\/suspensao.blog.br\/descrenca\/nao-me-interprete-mal-improvisando-testes-para-um-interpretador\/\">wonderful journey<\/a> that is reading <a href=\"http:\/\/craftinginterpreters.com\/\"><em>Crafting Interpreters,<\/em><\/a> I reached the point where we implemented an interpreter in C! As always, <a href=\"http:\/\/stuffwithstuff.com\/\">Bob Nystrom<\/a> mercilessly proposes very interesting challenges that keep us busy for long periods. For instance, <a href=\"http:\/\/craftinginterpreters.com\/chunks-of-bytecode.html#challenges\">in this chapter<\/a>, he suggests implementing our own memory allocator, without any real need! Inevitably, I was nerdsniped. <\/p>\n\n\n\n<p>The challenge allows us to allocate a large memory region with an existing <code>malloc()<\/code> function and manage it, but I decided to implement the <code>malloc()<\/code> from scratch. Since I use <a href=\"https:\/\/www.ubuntu.com\/\">Ubuntu<\/a>, it was necessary to first understand the memory layout of a process on Linux better.<\/p>\n\n\n\n<p>Consider the diagram below, which represents the memory layout of a process.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"840\" height=\"482\" data-attachment-id=\"181\" data-permalink=\"https:\/\/suspensao.blog.br\/disbelief\/implementing-malloc-and-free-1\/memoria-processo-en-1\/\" data-orig-file=\"https:\/\/i0.wp.com\/suspensao.blog.br\/disbelief\/wp-content\/uploads\/2024\/01\/memoria-processo-en-1.png?fit=877%2C503&amp;ssl=1\" data-orig-size=\"877,503\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"memoria-processo-en-1\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/i0.wp.com\/suspensao.blog.br\/disbelief\/wp-content\/uploads\/2024\/01\/memoria-processo-en-1.png?fit=840%2C482&amp;ssl=1\" src=\"https:\/\/i0.wp.com\/suspensao.blog.br\/disbelief\/wp-content\/uploads\/2024\/01\/memoria-processo-en-1.png?resize=840%2C482&#038;ssl=1\" alt=\"\" class=\"wp-image-181\" srcset=\"https:\/\/i0.wp.com\/suspensao.blog.br\/disbelief\/wp-content\/uploads\/2024\/01\/memoria-processo-en-1.png?w=877&amp;ssl=1 877w, https:\/\/i0.wp.com\/suspensao.blog.br\/disbelief\/wp-content\/uploads\/2024\/01\/memoria-processo-en-1.png?resize=300%2C172&amp;ssl=1 300w, https:\/\/i0.wp.com\/suspensao.blog.br\/disbelief\/wp-content\/uploads\/2024\/01\/memoria-processo-en-1.png?resize=768%2C440&amp;ssl=1 768w\" sizes=\"auto, (max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px\" \/><\/figure>\n<\/div>\n\n\n<p>In the memory allocated for the process, there are various sections. When the program starts its execution, the shaded part is not yet in use. Throughout its execution, the program declares local variables, causing the stack to grow backward.<\/p>\n\n\n\n<p>On the other hand, dynamically allocated memory is obtained from the heap, which grows in the opposite direction. The popular way to expand the heap is by increasing the size of the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Data_segment\">data segment<\/a> (i.e., the section that contains global and static variables) with the <code>sbrk()<\/code> system call.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"544\" height=\"393\" data-attachment-id=\"197\" data-permalink=\"https:\/\/suspensao.blog.br\/disbelief\/implementing-malloc-and-free-1\/sbrk-en-1\/\" data-orig-file=\"https:\/\/i0.wp.com\/suspensao.blog.br\/disbelief\/wp-content\/uploads\/2024\/01\/sbrk-en-1.png?fit=544%2C393&amp;ssl=1\" data-orig-size=\"544,393\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"sbrk-en-1\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/i0.wp.com\/suspensao.blog.br\/disbelief\/wp-content\/uploads\/2024\/01\/sbrk-en-1.png?fit=544%2C393&amp;ssl=1\" src=\"https:\/\/i0.wp.com\/suspensao.blog.br\/disbelief\/wp-content\/uploads\/2024\/01\/sbrk-en-1.png?resize=544%2C393&#038;ssl=1\" alt=\"Diagram representing how srbk() works, by increasing the data segment pointer but returning the old value.\" class=\"wp-image-197\" srcset=\"https:\/\/i0.wp.com\/suspensao.blog.br\/disbelief\/wp-content\/uploads\/2024\/01\/sbrk-en-1.png?w=544&amp;ssl=1 544w, https:\/\/i0.wp.com\/suspensao.blog.br\/disbelief\/wp-content\/uploads\/2024\/01\/sbrk-en-1.png?resize=300%2C217&amp;ssl=1 300w\" sizes=\"auto, (max-width: 544px) 85vw, 544px\" \/><\/figure>\n<\/div>\n\n\n<p>The above diagram illustrates how this functional system call works. <code>sbrk()<\/code> takes an integer parameter that will be added to the pointer indicating the end of the data segment. After that, <code>sbrk()<\/code> returns the value of the pointer before the increment.<\/p>\n\n\n\n<p>In a way, the behavior of <code>sbrk()<\/code> is already sufficient for memory allocation. Our <code>malloc()<\/code> function can simply invoke <code>sbrk()<\/code> and return to the user the pointer to the beginning of the allocated memory block:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>void *abmalloc(size_t size) {\n   return sbrk(size);\n}<\/code><\/pre>\n\n\n\n<p>In principle, <code>free()<\/code> doesn&#8217;t need to do anything: since in this implementation, we always use memory from the top of the heap, there is nothing we can do to reuse older memory blocks. In that sense, <code>free()<\/code> can perfectly be a <a href=\"https:\/\/en.wiktionary.org\/wiki\/no-op\">no-op<\/a>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>void abfree(void *ptr) {\n}<\/code><\/pre>\n\n\n\n<p>A useful operation can be done, however, if the block to be freed is the last one allocated. This means it is at the top of the stack, so we just need to move the stack pointer back with the <code>brk()<\/code> system call. This syscall takes a pointer as a parameter and, if this pointer is a &#8220;reasonable&#8221; value (not null, does not point into the stack, does not point before the heap), it uses the pointer&#8217;s value as the new top of the heap. The result would be something like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>void abfree(void *ptr) {\n  if (ptr == last_block) {\n      brk(last_block);\n  }\n}<\/code><\/pre>\n\n\n\n<p>This deallocation, however, is practically useless. Consider the example below:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>void *ptr1 = abmalloc(8);\nvoid *ptr2 = abmalloc(8);\nabfree(ptr2);\nabfree(ptr1);<\/code><\/pre>\n\n\n\n<p>With the current version of <code>abfree()<\/code>, we can free the memory pointed to by <code>ptr1<\/code>, but not the one pointed to by <code>ptr2<\/code>. To be able to free <code>ptr2<\/code>, it would be necessary to know that, once <code>ptr1<\/code> has been deallocated, the next last block is <code>ptr2<\/code>. Could we create a <code>second_last_block<\/code> variable? It wouldn&#8217;t help: we would have the same problem with the penultimate block, and so on.<\/p>\n\n\n\n<p>We need a more powerful data structure here, and that&#8217;s what we&#8217;ll see in our next post.<\/p>\n\n\n\n<p><em>(This post is a translation of <a href=\"https:\/\/suspensao.blog.br\/descrenca\/malloc-free-1\/\">Implementando malloc() e free() \u2014 primeiros passos<\/a>, originally published in <a href=\"https:\/\/suspensao.blog.br\/descrenca\">Suspens\u00e3o de Descren\u00e7a<\/a>.)<\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Following the wonderful journey that is reading Crafting Interpreters, I reached the point where we implemented an interpreter in C! As always, Bob Nystrom mercilessly proposes very interesting challenges that keep us busy for long periods. For instance, in this chapter, he suggests implementing our own memory allocator, without any real need! Inevitably, I was &hellip; <a href=\"https:\/\/suspensao.blog.br\/disbelief\/implementing-malloc-and-free-1\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Implementing malloc() and free() \u2014 first steps&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"jetpack_post_was_ever_published":false},"categories":[75,76,22,9],"tags":[77,81,80,79,78,47],"class_list":["post-179","post","type-post","status-publish","format-standard","hentry","category-c","category-linux","category-programming-languages","category-unix","tag-c","tag-free","tag-malloc","tag-memory-allocation","tag-memory-management","tag-software-development"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p9Ru6q-2T","jetpack-related-posts":[],"_links":{"self":[{"href":"https:\/\/suspensao.blog.br\/disbelief\/wp-json\/wp\/v2\/posts\/179","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/suspensao.blog.br\/disbelief\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/suspensao.blog.br\/disbelief\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/suspensao.blog.br\/disbelief\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/suspensao.blog.br\/disbelief\/wp-json\/wp\/v2\/comments?post=179"}],"version-history":[{"count":6,"href":"https:\/\/suspensao.blog.br\/disbelief\/wp-json\/wp\/v2\/posts\/179\/revisions"}],"predecessor-version":[{"id":199,"href":"https:\/\/suspensao.blog.br\/disbelief\/wp-json\/wp\/v2\/posts\/179\/revisions\/199"}],"wp:attachment":[{"href":"https:\/\/suspensao.blog.br\/disbelief\/wp-json\/wp\/v2\/media?parent=179"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/suspensao.blog.br\/disbelief\/wp-json\/wp\/v2\/categories?post=179"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/suspensao.blog.br\/disbelief\/wp-json\/wp\/v2\/tags?post=179"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}