Implementando malloc() e free() — adicionando metadados aos blocos de memória

You are viewing an old revision of this post, from January 24, 2024 @ 17:20:29. See below for differences between this version and the current revision.

Este post faz parte de uma série sobre como implementar as funções malloc() e free(). Anteriormente, fizemos uma implementação são simplória que praticamente não libera memória nenhuma: um ponteiro aponta para o último bloco alocado, o que permite que abfree() o desaloque, mas somente ele.

Uma opção melhor é fazer o último bloco apontar para o penúltimo, o penúltimo para o antepenúltimo e assim por diante, formando uma lista ligada. Para isso, criamos uma struct que servirá como cabeçalho dos blocos, contendo um ponteiro para o bloco anterior:

typedef struct Header {
  struct Header *previous;
} Header;

Além disso, o ponteiro para o último bloco, que era do tipo void*, agora é do tipo Header*:

Header *last = NULL;

Para usar esses cabeçalhos, abmalloc() reserva memória suficiente para armazenar tanto o cabeçalho quanto o tamanho solicitado:

void *abmalloc(size_t size) {
Header *header = sbrk(sizeof(Header) + size);

Deste modo, utilizamos o início do bloco para armazenar informações necessárias, como um ponteiro para o último bloco alocado antes do novo:

  header->previous = last;

Em seguida, atualizamos last para apontar para o novo bloco:

   last = header;

Por fim, retornamos um ponteiro para a memória que o usuário pode utilizar. Como header aponta para os metadados, não podemos simplesmente retorná-lo. Caso contrário, toda informação do cabeçalho seria sobrescrita quando o usuário usasse o ponteiro! Ao invés disso, retornamos um ponteiro para logo depois do cabeçalho. Este ponteiro é fácil de calcular: é o endereço de memória do cabeçalho mais o tamanho do cabeçalho:

  return header + 1;
}

Note como incrementamos o ponteiro header em 1. Como o tipo do ponteiro é Header*, o incremento será, na verdade, o número de bytes da struct Header, não um byte apenas. Por isso, o tipo do ponteiro incrementado é muito relevante na aritmética de ponteiros.

Agora que nossos blocos de memória possuem metadados no início, é preciso levar isso em conta na hora de desalocar. free() recebe um ponteiro não para o começo do bloco, mas para a memória disponibilizada ao usuário. Logo, precisamos encontrar o começo do bloco a partir do ponteiro que o usuário passou. Nada que uma pequena aritmética de ponteiros não resolva:

void abfree(void *ptr) {
  Header *header = (Header*) ptr - 1;

Se header aponta para o último bloco alocado, o bloco anterior passará a ser o último. Neste caso, podemos retornar memória do heap para o sistema operacional através de brk():

  if (header == last) {
last = header->previous;
brk(header);
}

Eis a nova versão de nossas funções malloc() e free():

typedef struct Header {
   struct Header *previous;
 } Header;

 Header *last = NULL;

 void *abmalloc(size_t size) {
   Header *header = sbrk(sizeof(Header) + size);
   header->previous = last;
   last = header;
   return header + 1;
 }

 void abfree(void *ptr) {
   Header *header = (Header*) ptr - 1;
   if (header == last) {
     last = header->previous;
     brk(header);
   }
 }

abmalloc() e abfree() podem ser ligeiramente mais econômicos em memória agora, mas não muito. Raramente a memória alocada dinamicamente se comporta com uma pilha, onde o bloco mais velho é sempre desalocado primeiro. No próximo post, veremos como utilizar a memória de blocos mais antigos que não estão mais em uso.

Post Revisions:

Changes:

January 24, 2024 @ 17:20:29Current Revision
Content
Unchanged: <!-- wp:paragraph -->Unchanged: <!-- wp:paragraph -->
Unchanged: <p>Este post faz parte de uma série sobre como implementar as funções <code>malloc()</code> e <code>free()</code>. Anteriormente, <a href="https:/ /suspensao.blog.br/descrenca/ malloc-free-1/">fizemos uma implementação são simplória</a> que praticamente não libera memória nenhuma: um ponteiro aponta para o último bloco alocado, o que permite que ab<code>free()</code> o desaloque, mas somente ele.</p>Unchanged: <p>Este post faz parte de uma série sobre como implementar as funções <code>malloc()</code> e <code>free()</code>. Anteriormente, <a href="https:/ /suspensao.blog.br/descrenca/ malloc-free-1/">fizemos uma implementação são simplória</a> que praticamente não libera memória nenhuma: um ponteiro aponta para o último bloco alocado, o que permite que ab<code>free()</code> o desaloque, mas somente ele.</p>
Unchanged: <!-- /wp:paragraph -->Unchanged: <!-- /wp:paragraph -->
Unchanged: <!-- wp:paragraph -->Unchanged: <!-- wp:paragraph -->
Unchanged: <p>Uma opção melhor é fazer o último bloco apontar para o penúltimo, o penúltimo para o antepenúltimo e assim por diante, formando uma <a href="https:/ /pt.wikipedia.org/wiki/Lista_ ligada">lista ligada</a>. Para isso, criamos uma struct que servirá como cabeçalho dos blocos, contendo um ponteiro para o bloco anterior:</p>Unchanged: <p>Uma opção melhor é fazer o último bloco apontar para o penúltimo, o penúltimo para o antepenúltimo e assim por diante, formando uma <a href="https:/ /pt.wikipedia.org/wiki/Lista_ ligada">lista ligada</a>. Para isso, criamos uma struct que servirá como cabeçalho dos blocos, contendo um ponteiro para o bloco anterior:</p>
Unchanged: <!-- /wp:paragraph -->Unchanged: <!-- /wp:paragraph -->
Unchanged: <!-- wp:preformatted -->Unchanged: <!-- wp:preformatted -->
Unchanged: <pre class="wp-block- preformatted">typedef struct Header {Unchanged: <pre class="wp-block- preformatted">typedef struct Header {
Unchanged: struct Header *previous;Unchanged: struct Header *previous;
Unchanged: } Header;</pre>Unchanged: } Header;</pre>
Unchanged: <!-- /wp:preformatted -->Unchanged: <!-- /wp:preformatted -->
Unchanged: <!-- wp:paragraph -->Unchanged: <!-- wp:paragraph -->
Unchanged: <p>Além disso, o ponteiro para o último bloco, que era do tipo <code>void*</code>, agora é do tipo <code>Header*</code>:</p>Unchanged: <p>Além disso, o ponteiro para o último bloco, que era do tipo <code>void*</code>, agora é do tipo <code>Header*</code>:</p>
Unchanged: <!-- /wp:paragraph -->Unchanged: <!-- /wp:paragraph -->
Unchanged: <!-- wp:preformatted -->Unchanged: <!-- wp:preformatted -->
Unchanged: <pre class="wp-block- preformatted">Header *last = NULL;</pre>Unchanged: <pre class="wp-block- preformatted">Header *last = NULL;</pre>
Unchanged: <!-- /wp:preformatted -->Unchanged: <!-- /wp:preformatted -->
Unchanged: <!-- wp:paragraph -->Unchanged: <!-- wp:paragraph -->
Unchanged: <p>Para usar esses cabeçalhos, <code>abmalloc()</code> reserva memória suficiente para armazenar tanto o cabeçalho quanto o tamanho solicitado:</p>Unchanged: <p>Para usar esses cabeçalhos, <code>abmalloc()</code> reserva memória suficiente para armazenar tanto o cabeçalho quanto o tamanho solicitado:</p>
Unchanged: <!-- /wp:paragraph -->Unchanged: <!-- /wp:paragraph -->
Unchanged: <!-- wp:preformatted -->Unchanged: <!-- wp:preformatted -->
Unchanged: <pre class="wp-block- preformatted">void *abmalloc(size_t size) {<br> Header *header = sbrk(sizeof(Header) + size);</pre>Unchanged: <pre class="wp-block- preformatted">void *abmalloc(size_t size) {<br> Header *header = sbrk(sizeof(Header) + size);</pre>
Unchanged: <!-- /wp:preformatted -->Unchanged: <!-- /wp:preformatted -->
Unchanged: <!-- wp:paragraph -->Unchanged: <!-- wp:paragraph -->
Unchanged: <p>Deste modo, utilizamos o início do bloco para armazenar informações necessárias, como um ponteiro para o último bloco alocado antes do novo:</p>Unchanged: <p>Deste modo, utilizamos o início do bloco para armazenar informações necessárias, como um ponteiro para o último bloco alocado antes do novo:</p>
Unchanged: <!-- /wp:paragraph -->Unchanged: <!-- /wp:paragraph -->
Unchanged: <!-- wp:preformatted -->Unchanged: <!-- wp:preformatted -->
Unchanged: <pre class="wp-block- preformatted"> header-&gt;previous = last;</pre>Unchanged: <pre class="wp-block- preformatted"> header-&gt;previous = last;</pre>
Unchanged: <!-- /wp:preformatted -->Unchanged: <!-- /wp:preformatted -->
Unchanged: <!-- wp:paragraph -->Unchanged: <!-- wp:paragraph -->
Unchanged: <p>Em seguida, atualizamos <code>last</code> para apontar para o novo bloco:</p>Unchanged: <p>Em seguida, atualizamos <code>last</code> para apontar para o novo bloco:</p>
Unchanged: <!-- /wp:paragraph -->Unchanged: <!-- /wp:paragraph -->
Unchanged: <!-- wp:preformatted -->Unchanged: <!-- wp:preformatted -->
Unchanged: <pre class="wp-block- preformatted"> last = header;</pre>Unchanged: <pre class="wp-block- preformatted"> last = header;</pre>
Unchanged: <!-- /wp:preformatted -->Unchanged: <!-- /wp:preformatted -->
Unchanged: <!-- wp:paragraph -->Unchanged: <!-- wp:paragraph -->
Unchanged: <p>Por fim, retornamos um ponteiro para a memória que o usuário pode utilizar. Como <code>header</code> aponta para os metadados, não podemos simplesmente retorná-lo. Caso contrário, toda informação do cabeçalho seria sobrescrita quando o usuário usasse o ponteiro! Ao invés disso, retornamos um ponteiro para logo depois do cabeçalho. Este ponteiro é fácil de calcular: é o endereço de memória do cabeçalho <em>mais</em> o tamanho do cabeçalho:</p>Unchanged: <p>Por fim, retornamos um ponteiro para a memória que o usuário pode utilizar. Como <code>header</code> aponta para os metadados, não podemos simplesmente retorná-lo. Caso contrário, toda informação do cabeçalho seria sobrescrita quando o usuário usasse o ponteiro! Ao invés disso, retornamos um ponteiro para logo depois do cabeçalho. Este ponteiro é fácil de calcular: é o endereço de memória do cabeçalho <em>mais</em> o tamanho do cabeçalho:</p>
Unchanged: <!-- /wp:paragraph -->Unchanged: <!-- /wp:paragraph -->
Unchanged: <!-- wp:preformatted -->Unchanged: <!-- wp:preformatted -->
Unchanged: <pre class="wp-block- preformatted"> return header + 1;<br>}</pre>Unchanged: <pre class="wp-block- preformatted"> return header + 1;<br>}</pre>
Unchanged: <!-- /wp:preformatted -->Unchanged: <!-- /wp:preformatted -->
Unchanged: <!-- wp:paragraph -->Unchanged: <!-- wp:paragraph -->
Unchanged: <p>Note como incrementamos o ponteiro header em 1. Como o tipo do ponteiro é <code>Header*</code>, o incremento será, na verdade, o número de bytes da struct <code>Header</code>, não um byte apenas. Por isso, o tipo do ponteiro incrementado é muito relevante na <a href="https:/ /www.ime.usp.br/ ~pf/algoritmos/ aulas/pont.html#array" >aritmética de ponteiros</a>.</p>Unchanged: <p>Note como incrementamos o ponteiro header em 1. Como o tipo do ponteiro é <code>Header*</code>, o incremento será, na verdade, o número de bytes da struct <code>Header</code>, não um byte apenas. Por isso, o tipo do ponteiro incrementado é muito relevante na <a href="https:/ /www.ime.usp.br/ ~pf/algoritmos/ aulas/pont.html#array" >aritmética de ponteiros</a>.</p>
Unchanged: <!-- /wp:paragraph -->Unchanged: <!-- /wp:paragraph -->
Unchanged: <!-- wp:paragraph -->Unchanged: <!-- wp:paragraph -->
Unchanged: <p>Agora que nossos blocos de memória possuem metadados no início, é preciso levar isso em conta na hora de desalocar. <code>free()</code> recebe um ponteiro não para o começo do bloco, mas para a memória disponibilizada ao usuário. Logo, precisamos encontrar o começo do bloco a partir do ponteiro que o usuário passou. Nada que uma pequena aritmética de ponteiros não resolva:</p>Unchanged: <p>Agora que nossos blocos de memória possuem metadados no início, é preciso levar isso em conta na hora de desalocar. <code>free()</code> recebe um ponteiro não para o começo do bloco, mas para a memória disponibilizada ao usuário. Logo, precisamos encontrar o começo do bloco a partir do ponteiro que o usuário passou. Nada que uma pequena aritmética de ponteiros não resolva:</p>
Unchanged: <!-- /wp:paragraph -->Unchanged: <!-- /wp:paragraph -->
Unchanged: <!-- wp:preformatted -->Unchanged: <!-- wp:preformatted -->
Unchanged: <pre class="wp-block- preformatted">void abfree(void *ptr) {Unchanged: <pre class="wp-block- preformatted">void abfree(void *ptr) {
Unchanged: Header *header = (Header*) ptr - 1;</pre>Unchanged: Header *header = (Header*) ptr - 1;</pre>
Unchanged: <!-- /wp:preformatted -->Unchanged: <!-- /wp:preformatted -->
Unchanged: <!-- wp:paragraph -->Unchanged: <!-- wp:paragraph -->
Unchanged: <p>Se <code>header</code> aponta para o último bloco alocado, o bloco anterior passará a ser o último. Neste caso, podemos retornar memória do heap para o sistema operacional através de <code>brk()</code>:</p>Unchanged: <p>Se <code>header</code> aponta para o último bloco alocado, o bloco anterior passará a ser o último. Neste caso, podemos retornar memória do heap para o sistema operacional através de <code>brk()</code>:</p>
Unchanged: <!-- /wp:paragraph -->Unchanged: <!-- /wp:paragraph -->
Unchanged: <!-- wp:preformatted -->Unchanged: <!-- wp:preformatted -->
Unchanged: <pre class="wp-block- preformatted"> if (header == last) {<br> last = header-&gt;previous;<br> brk(header);<br> }</pre>Unchanged: <pre class="wp-block- preformatted"> if (header == last) {<br> last = header-&gt;previous;<br> brk(header);<br> }</pre>
Unchanged: <!-- /wp:preformatted -->Unchanged: <!-- /wp:preformatted -->
Unchanged: <!-- wp:paragraph -->Unchanged: <!-- wp:paragraph -->
Unchanged: <p>Eis a nova versão de nossas funções <code>malloc()</code> e <code>free()</code>:</p>Unchanged: <p>Eis a nova versão de nossas funções <code>malloc()</code> e <code>free()</code>:</p>
Unchanged: <!-- /wp:paragraph -->Unchanged: <!-- /wp:paragraph -->
Unchanged: <!-- wp:preformatted -->Unchanged: <!-- wp:preformatted -->
Unchanged: <pre class="wp-block- preformatted">typedef struct Header {Unchanged: <pre class="wp-block- preformatted">typedef struct Header {
Unchanged: struct Header *previous;Unchanged: struct Header *previous;
Unchanged: } Header;Unchanged: } Header;
Unchanged: Header *last = NULL;Unchanged: Header *last = NULL;
Unchanged: void *abmalloc(size_t size) {Unchanged: void *abmalloc(size_t size) {
Unchanged: Header *header = sbrk(sizeof(Header) + size);Unchanged: Header *header = sbrk(sizeof(Header) + size);
Unchanged: header-&gt;previous = last;Unchanged: header-&gt;previous = last;
Unchanged: last = header;Unchanged: last = header;
Unchanged: return header + 1;Unchanged: return header + 1;
Unchanged: }Unchanged: }
Unchanged: void abfree(void *ptr) {Unchanged: void abfree(void *ptr) {
Unchanged: Header *header = (Header*) ptr - 1;Unchanged: Header *header = (Header*) ptr - 1;
Unchanged: if (header == last) {Unchanged: if (header == last) {
Unchanged: last = header-&gt;previous;Unchanged: last = header-&gt;previous;
Unchanged: brk(header);Unchanged: brk(header);
Unchanged: }Unchanged: }
Unchanged: }</pre>Unchanged: }</pre>
Unchanged: <!-- /wp:preformatted -->Unchanged: <!-- /wp:preformatted -->
Unchanged: <!-- wp:paragraph -->Unchanged: <!-- wp:paragraph -->
Deleted: <p><code>abmalloc()</code> e a<code>bfree()</code> podem ser ligeiramente mais econômicos em memória agora, mas não muito. Raramente a memória alocada dinamicamente se comporta com uma pilha, onde o bloco mais velho é sempre desalocado primeiro. No próximo post, veremos como utilizar a memória de blocos mais antigos que não estão mais em uso.</p> Added: <p><code>abmalloc()</code> e a<code>bfree()</code> podem ser ligeiramente mais econômicos em memória agora, mas não muito. Raramente a memória alocada dinamicamente se comporta com uma pilha, onde o bloco mais velho é sempre desalocado primeiro. No <a href="https:/ /suspensao.blog.br/descrenca/ implementando- malloc-e-free- reutilizando- blocos-de-memoria/">próximo post</a>, veremos como utilizar a memória de blocos mais antigos que não estão mais em uso.</p>
Unchanged: <!-- /wp:paragraph -->Unchanged: <!-- /wp:paragraph -->

Note: Spaces may be added to comparison text to allow better line wrapping.

Post a Comment

Your email is never shared. Required fields are marked *

*
*