Implementation of avr-libc malloc/free,

Source: Internet
Author: User

Implementation of avr-libc malloc/free,

Avr-libc is the C Language Runtime Library of the AVR Microcontroller. It provides the AVR version of the GNU Toolset (Binutils, GCC, GDB, etc .), it is a project under nongnu.org and released with Modified BSD License. You can download the source code on your website:

Home Page: http://www.nongnu.org/avr-libc/ Detail Page: http://savannah.nongnu.org/projects/avr-libc/

Of course, you can also use:

Svn checkout svn: // svn.sv.gnu.org/avr-libc/trunk avr-libc

Check the source code of the latest version.

I checked twice and reported the svn: E155009 error, so I changed to a stable release version check:

Svn checkout svn: // svn.sv.gnu.org/avr-libc/tags/avr-libc-rj8_0-release avr-libc-1_8_0

This time is normal and no error is reported. You can use the tree Command to list the structure of the entire project,

The malloc we want to see is located under the avr-libc/stdlib. Here there are four internal files related to the malloc process:

Sectionname. h

Stdlib_private.h

Malloc. c

Realloc. c

The comments in these codes have been described in detail. Here I will demonstrate the steps in an illustration. For ease of reading, some comments, test code, and copyright notices have been deleted. For copyright notice, refer to the source code.


There are only a few lines of code in stdlib_private.hsectioinname.h. Its function is to allow the compiler to correctly place the corresponding storage section of the Code. It has little to do with the topic here and can be skipped directly. Let's look at the code in stdlib:
# If! Defined (_ DOXYGEN _) struct _ freelist {size_t sz; // sizestruct _ freelist * nx; // next }; // idle linked list node # endifextern char * _ brkval;/* first location not yet allocated */extern struct _ freelist * _ empty; /* freelist pointer (head of freelist) */extern size_t _ malloc_margin;/* user-changeable before the first malloc () */extern char * _ malloc_heap_start; extern char * _ malloc_heap_end; extern char _ heap_start; extern char _ heap_end;/* Needed for definition of AVR_STACK_POINTER_REG. */# include <avr/io. h> # define STACK_POINTER () (char *) AVR_STACK_POINTER_REG)
Stdlib_private.h defines the node Structure of freelist and several global variables to be accessed in malloc. c and realloc. c. The freelist node is like this:
Mallocmalloc. c implements malloc and free. Compared with the previous Keil implementation version, the two methods are similar but different. First read the code (comments are more detailed ):
Void * malloc (size_t len) {struct _ freelist * fp1, * fp2, * sfp1, * sfp2; char * cp; size_t s, avail; /** Our minimum chunk size is the size of a pointer (plus the * size of the "sz" field, but we don't need to account for * this ), otherwise we cocould not possibly fit a freelist entry * into the chunk later. * // the block to be handed over by malloc. It must be at least the size of a pointer (the reason will be displayed later) if (len <sizeof (struct _ freelist)-sizeof (size_t )) len = si Zeof (struct _ freelist)-sizeof (size_t);/** First, walk the free list and try finding a chunk that * wocould match exactly. if we found one, we are done. while * walking, note down the smallest chunk we found that wocould * still fit the request -- we need it for step 2. */for (s = 0, fp1 = _ blank, fp2 = 0; fp1; // go to the header and jump out of for fp2 = fp1, fp1 = fp1-> nx) {// fp1 is in front of fp2 (fp2-> next = fp1) if (fp1-> sz <le N) // not enough? Continue searching... continue; if (fp1-> sz = len) {// case 1. right block/** Found it. disconnect the chunk from the * freelist, and return it. */if (fp2) fp2-> nx = fp1-> nx; else _ records = fp1-> nx; // fp2 = 0, fp1 points to freelist head // Note: Here, return the nx domain address return & (fp1-> nx);} else {// large enough! If (s = 0 | fp1-> sz <s) {/* this is the smallest chunk found so far */s = fp1-> sz; // s is the smallest sfp1 = fp1; sfp2 = fp2 ;}}/ ** Step 2 found in the "adequate" chunk: if we found a chunk on the freelist that wocould fit * (but was too large), look it up again and use it, since it * is our closest match now. since the freelist entry needs * to be split into two entries then, watch out that the * difference between t He requested size and the size of the * chunk found is large enough for another freelist entry; if * not, just enlarge the request size to what we have found, * and use the entire chunk. */if (s) {// There is enough chunkif (s-len <sizeof (struct _ freelist) on freelist {// case 2. (Current block) the remaining space is less than one node/* Disconnect it from freelist and return it. */if (sfp2) sfp2-> nx = sfp1-> nx; else _ records = sfp1-> nx; return & (Sfp1-> nx);}/** Split them up. note that we leave the first part * as the new (smaller) freelist entry, and return the * upper portion to the caller. this saves us the * work to fix up the freelist chain; we just need to * fixup the size of the current entry, and note down * the size of the new chunk before returning it to * the caller. * // case 3. (Current block) if the remaining space is enough for one node, the partition is split into cp = (char *) sfp1; s -= Len; cp + = s; sfp2 = (struct _ freelist *) cp; sfp2-> sz = len; sfp1-> sz = s-sizeof (size_t ); return & (sfp2-> nx);} // the chunk is not big enough on freelist/** Step 3: If the request cocould not be satisfied from a * freelist entry, just prepare a new chunk. this means we * need to obtain more memory first. the largest address just * not allocated so far is remembered in the brkval variable. * Under Unix, the "break v Alue "was the end of the data * segment as dynamically requested from the operating system. * Since we don't have an operating system, just make sure * that we don't collide with the stack. */if (_ brkval = 0) _ brkval = _ malloc_heap_start; cp = _ malloc_heap_end; // _ malloc_heap_start, _ malloc_heap_end should be set by the user before calling malloc. If (cp = 0) cp = STACK_POINTER ()-_ malloc_margin; // reserved _ malloc_margin bytes for the stack space. Prevents (heap, stack) collisions! If (cp <= _ brkval)/** Memory exhausted. */return 0; avail = cp-_ brkval; // calculate the remaining available space/** Both tests below are needed to catch the case len> = 0xfffe. */if (avail> = len & avail> = len + sizeof (size_t) {fp1 = (struct _ freelist *) _ brkval; __brkval + = len + sizeof (size_t); // heap "Growth" fp1-> sz = len; return & (fp1-> nx);}/** Step 4: there's no help, just fail. :-/*/return 0 ;}

The first for loop traversal table,
If the current chunk is not large enough, continue to look for it later;
If the chunk size is correct, the chunk is removed from the freelist and returned. Delete -- whether the current chunk is in the freelist header; If yes, move the freelist header pointer back. Note that -- the returned result is & (fp1-> nx ), together with the preceding condition if (fp1-> sz = len), it indicates that the chunk space represented by sz on a node of freelist includes the size of the nx domain (which is different from that of Keil ). A chunk is as follows:


If it is large, it will continue later and record the minimum value so far (the minimum value will be found at the end ).

By Step 2, an available chunk has been found, which is the smallest of all chunks larger than len (which we need;
Next, let's see if it's only a little bigger than what we need? If yes, that is, there is not enough space to put a node, then we can't mount it as a chunk to freelist and return it directly;
Otherwise, it indicates that a chunk can be created in the excess memory (freelist can be mounted freely) and the chunk must be split into two halves;
The following code is used to complete the job:

cp = (char *)sfp1;s -= len;cp += s;sfp2 = (struct __freelist *)cp;sfp2->sz = len;sfp1->sz = s - sizeof(size_t);return &(sfp2->nx);
The following figure shows the execution process of these lines of code. It should be clear here, how much s is before the code is executed, where sfp1 and sfp2 point? Sfp1 points to the "most appropriate" (all the smaller ones than len) sfp2 follows, and s carries the memory size for this chunk:


These three lines

Cp = (char *) sfp1;
S-= len;
Cp + = s;

"S, cp is as follows (Other unchanged) after execution, the cp will be cut and you will be able to see it immediately,

Next,

Sfp2 = (struct _ freelist *) cp;
Sfp2-> sz = len;

Just as a new freelist node has been created at the cp, the sz is recorded here to know the size of the chunk to which p belongs at free (p.


Next,"

Sfp1-> sz = s-sizeof (size_t );

"Updated the sz of the original chunk,


Success! Yes,


In the figure, ret indicates the address actually returned by malloc, and the memory that malloc (len) wants to obtain is ready!


The last few lines of malloc are the last case, that is, no chunk on the entire linked list can meet the requirements (this is also the case for the first call, because of the global variable _ success, __the initial values of brkval are all 0 );
Note step 3: Prepare a new chunk, that is, to obtain more memory.
This code is related to the _ brkval variable and STACK_POINTER (SP) variable. The memory layout problem is involved here, where __brkval, SP ,__ heap_start, __the heap_end relationship can be roughly understood from:

(Picture from http://www.nongnu.org/avr-libc/user-manual/malloc.html)
From the figure, we can see that the stack and heap have a public space, and the growth direction is right. This is the possibility of collide, _ malloc_margin is used to prevent collision.
Through the code"

_ Brkval + = len + sizeof (size_t );

We can know that _ brkval is actually the upper bound of heap. Every time freelist cannot meet the malloc request, heap will increase and update _ brkval when the "Stack gap" space is sufficient.

Free has some analysis of malloc, and the free code is easy to understand,

Voidfree (void * p) {struct _ freelist * fp1, * fp2, * fpnew; char * cp1, * cp2, * cpnew;/* iso c says free (NULL) must be a no-op */if (p = 0) return; cpnew = p; cpnew-= sizeof (size_t); fpnew = (struct _ freelist *) cpnew; fpnew-> nx = 0;/** Trivial case first: if there's no freelist yet, our entry * will be the only one on it. if this is the last entry, we * can reduce _ brkval instead. */if (_ rows = 0) {// f Reelist is empty if (char *) p + fpnew-> sz ==_ _ brkval) // fpnew-> sz ==_ _ brkval-(char *) p, to be free, the last chunk _ brkval = cpnew; // heap is "scaled down" else _ records = fpnew; return;}/** Now, find the position where our new entry belongs onto the * freelist. try to aggregate the chunk with adjacent chunks * if possible. */for (fp1 = _ blank, fp2 = 0; fp1; fp2 = fp1, fp1 = fp1-> nx) {// fp1 is in front of fp2 (fp2-> next = fp1) if (fp1 <Fpnew) continue; // fp1> fpnew, fpnew> fp1cp1 = (char *) fp1; fpnew-> nx = fp1; if (char *) & (fpnew-> nx) + fpnew-> sz = cp1) {/* upper chunk adjacent, assimilate it * // combine fpnew with the chunk behind it-> sz + = fp1-> sz + sizeof (size_t); fpnew-> nx = fp1-> nx ;} if (fp2 = 0) {/* new head of freelist */_ blank = fpnew; return;} break ;} /** Note that we get here either if we hit the "break" above, * or if we fell off the en D of the loop. the latter means * we 've got a new topmost chunk. either way, try aggregating * with the lower chunk if possible. */fp2-> nx = fpnew; cp2 = (char *) & (fp2-> nx); if (cp2 + fp2-> sz = cpnew) {// It can be merged with the previous node/* lower junk adjacent, merge * // merge fp2 with the previous chunk-> sz + = fpnew-> sz + sizeof (size_t); fp2-> nx = fpnew-> nx ;} /** If there's a new topmost chunk, lower _ brkval instead. */for (fp1 = _ records, Fp2 = 0; fp1-> nx! = 0; fp2 = fp1, fp1 = fp1-> nx)/* advance to entry just before end of list */; cp2 = (char *) & (fp1-> nx); if (cp2 + fp1-> sz ==__ brkval) {if (fp2 = NULL)/* Freelist is empty now. */_ partition = NULL; elsefp2-> nx = NULL ;__ brkval = cp2-sizeof (size_t );}}


Starting with"

Cpnew = p;
Cpnew-= sizeof (size_t );
Fpnew = (struct _ freelist *) cpnew;

"After execution, fpnew gets the actual chunk address (malloc cut). The relationship between p and cpnew and fpnew is as follows,

Next, if freelist is empty, add the current chunk to freelist. If (char *) p + fpnew-> sz = _ brkval ), from the previous analysis, we know that _ brkval is the upper bound of heap. Here _ brkval = cpnew; is the upper bound of heap reduction.

Next is a for loop, In the for loop, starting:
If (fp1 <fpnew)
Continue;

There is a break at the end of the for loop. It is obvious that the code from continue to break will only be executed once, and the function will be the same if it is written to the end of the for loop. That is:

For (fp1 = _ blank, fp2 = 0; fp1; fp2 = fp1, fp1 = fp1-> nx) {// fp1 is in front of fp2 (fp2-> next = fp1) if (fp1> = fpnew) break;} // fp1> fpnew> fp2, the node cp1 = (char *) fp1 before and after fpnew is found; fpnew-> nx = fp1; if (char *) & (fpnew-> nx) + fpnew-> sz = cp1) {// adjacent to the following node/* upper chunk adjacent, assimilate it */fpnew-> sz + = fp1-> sz + sizeof (size_t); fpnew-> nx = fp1-> nx;} if (fp2 = 0) {/* new head of freelist */_ blank = fpnew; return ;}

The function of the for Loop is obvious. It is to find the position where the current chunk should be inserted to freelist;

There are several possible situations after the for loop ends. The processing is similar. There is only one of them below, which is displayed in a graphical way.


Case 1 the current chunk (fpnew) can be merged with the following chunk (fp1)

This situation corresponds to (char *) & (fpnew-> nx) + fpnew-> sz = cp1.

When the for loop ends, fp1 and fp2 may be as follows:


The free job is to "Mount" the gray chunk in the middle to freelist, and check whether it can be merged (adjacent to the chunk indicated by fp1 or fp2). If it can be merged, then the chunk is merged with its adjacent chunk.


Next:

Cp1 = (char *) fp1;
Fpnew-> nx = fp1;

Connect the nx domain of the current chunk with the chunk below:





Subsequent code;

If (char *) & (fpnew-> nx) + fpnew-> sz = cp1) corresponding status:



Next:

If (char *) & (fpnew-> nx) + fpnew-> sz = cp1 ){

/* Upperchunk adjacent, assimilate it */

Fpnew->Sz+ = Fp1->Sz+ Sizeof (Size_t);

Fpnew-> nx = fp1-> nx;

}


Update fpnew-> sz, corresponding status:




Next:

If (char *) & (fpnew-> nx) + fpnew-> sz = cp1 ){

/* Upperchunk adjacent, assimilate it */

Fpnew-> sz + = fp1-> sz + sizeof (size_t );

Fpnew->Nx= Fp1->Nx;

}


Update fpnew-> nx, corresponding status:





The following will inevitably be executed:

Fp2->Nx=Fpnew; // Make a new link

Corresponding:



At this point, the free work corresponding to the subsequent chunk can be merged.

(Ps: drawing is too tired. In other cases, it will not be drawn .)


Additional reading

This article was written last year after I read Keil malloc. Another article about Keil memory management is provided by me:

Pooled Allocation instance -- Keil Memory Management

In addition, the source code of the avr-libc project can be viewed online:

Arv-libc/malloc. c

In addition, sdcc (Small Device C Compiler) is an open-source microcontroller Compiler, which also implements malloc and free. Project homepage:

Http://sdcc.sourceforge.net/interested students can download their own source code.




Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.