At&t's malloc implementation-the basis and essence of malloc

Source: Internet
Author: User

As a memory allocation call for Standard C, malloc must have been used by every C language. However, there are n Different implementations under this commonly used unified interface, linux glibc has its own implementation, Windows CRT has its own implementation, these implementations have their own strategies, especially the implementation of glibc makes people dizzy, although the implementation of CRT is simple, it seems silly to have a strategy. The most primitive and essential implementation is the implementation of Bell Labs. It is very simple. There are no more than 60 lines of code before and after, it makes people feel relaxed after reading, and together with the implementation of free, it forms a beautiful picture. Essentially, Bell's malloc uses a Linear Linked List to represent the memory blocks that can be allocated, if you are familiar with the partner system and slab, you should know this is the case, but it is still different from the slab and the partner system. The slab allocates memory blocks of the same size, in the partner system, memory blocks of different sizes are allocated, for example, determined 1, 2, 4, 8... the bell test room's malloc version is a random allocation, which follows the minimum fragmentation and maximizes utilization. In principle allocation, the original intention of slab is actually the concept of a pool, saving the allocation overhead. The partner system proposed to eliminate fragments, and the bell malloc version was used in two birds with one stone, although many people have forgotten this version, or you can say that the implementation of glibc or CRT has exceeded this version, these implementations have more or less added to the snake, it is not as pure as Bell malloc. Now let's take a look at the basic data structure:

Typedef struct mem {

Struct mem * next;

Unsigned Len;

} MEM;

This data structure represents all memory block slots. Each slot represents a piece of memory, and Len represents its length. This structure is actually a concept of a header, the real data is attached to the adjacent place behind this struct. The clever design of this struct is that it can make the implementation of malloc and free easier, formally, because its first field is of the mem * type, where is it? Only read malloc:

Mem * F; // a global mem * linked list. F indicates free. It points to the header of the current idle linked list, which is a linear linked list.

Void * malloc (unsigned size)

{

Register mem * P, * q, * r, * s;

Unsigned register K, M;

Extern char * sbrk (INT );

Char * Top, * top1;

Size = (size + 7 )&~ 7; // byte alignment

R = (MEM *) & F; // get the address of the linked list header. This header is a pointer type, so R is actually a pointer, it is precisely because the first field of mem is of the mem * type that it can be converted to the mem * type. F is the address of F after the address is obtained, and F is stored, f is a men pointer, so the data at the & F address is a mem * type. After converting the data to Mem *, the data is the next field of mem, note that its LEN field is invalid at this time. The significance of this conversion is that R's next is F.

For (P = F, q = 0; P; r = P, P = p-> next) // traverse the idle linked list until the precursor of F is null.

{

If (k = p-> Len)> = size &&(! Q | M> K) // & make sure that the current idle block meets the allocation requirements, and then find the smallest idle block with a size greater

{

M = K; // M records the currently found minimum block that meets the requirements

Q = P; // Q records the one that currently meets the requirements

S = r; // the first part of the record that currently meets the requirements

}

}

If (q) // If you have found a block that meets the requirements, you can either directly allocate the block, split it, and assign it again. Then, you can add the one that is not used after the allocation to the idle linked list.

{

If (Q-> len-size> = minblk) // meet the segmentation requirement, that is, the minimum granularity requirement.

{

P = (MEM *) (char *) (q + 1) + size); // set the Q size. This part will be removed from the idle linked list and returned for use.

P-> next = Q-> next; // set the next of the new idle block after split to the Next of Q before split.

P-> Len = Q-> len-size-sizeof (MEM); // The split P Len obviously reduces the sum of size and mem.

S-> next = P; // point the next of the first part that meets the requirements to the next part of Q.

Q-> Len = size; // allocate Q, that is, remove from the idle linked list.

}

Else // if it cannot be split, the Q will be allocated directly.

S-> next = Q-> next;

}

Else // if no suitable idle block can be allocated, you need to ask the Operating System

{

Top = (char *) (long) sbrk (0) + 7 )&~ 7); // find the top of the current heap

If (F & (char *) (F + 1) + F-> Len = Top) // if there is an idle block and the idle block is the top part of the heap

{

Q = f; // assign the first pointer of the idle block to Q

F = f-> next; // push the idle block backward.

}

Else // otherwise, top the heap to Q

Q = (MEM *) top;

Top1 = (char *) (q + 1) + size; // locate the new heap, but distribute sbgulp in advance.

If (sbrk (INT) (top1-top + sbgulp) = (char *)-1)

Return 0;

R = (MEM *) top1; // R records the next of the new slot to be allocated

R-> Len = sbgulp-sizeof (MEM );

R-> next = f; // assign the original idle linked list to the next of the new slot

F = r; // The next slot of the new slot is assigned to the new idle linked list.

Q-> Len = size;

}

Return (char *) (q + 1); // return Q. Q is the allocated memory.

}

The above is the malloc operation, which is very organized and clear. The key is the usage of the pointer. After reading this implementation, we will find that the original pointer can still be used like this, if the first field of the mem struct is not a pointer of the mem type, the above malloc implementation cannot be as simple as that. Note that the "next element" has two concepts: one is a logical concept, obtained using the next field, the other is a physical concept, and Qn = (char *) F + q-> Len. The next element obtained by using the next pointer is physically not necessarily adjacent. All the elements connected with next are idle elements, the next element obtained by offset is physically adjacent memory blocks, that is, adjacent memory blocks of virtual memory. The memory blocks obtained by this method are not necessarily idle blocks. Allocation is the above malloc function. It is a very simple function. Release is actually very interesting. It is actually a free function:

Void free (char * F)

{

Mem * P, * q, * R;

Char * Pn, * Qn;

If (! F)

Return;

Q = (MEM *) (char *) F-sizeof (MEM); // obtain the location of the mem header of F to be released, although Q has been removed from the idle linked list, it actually exists.

Qn = (char *) F + q-> Len; // obtain the next element of Q in a linear linked list. It is not necessarily an idle element because it travels by memory location.

For (P = F, r = (MEM *) & F; r = P, P = p-> next)

{

If (qN = (char *) P) // if the next element of Q is P, It is swallowed up. Note that P must be an idle element, because it travels by the next pointer.

{

Q-> Len + = p-> Len + sizeof (MEM); // update the LEN field of Q. This is the behavior of P.

P = p-> next; // because P is swallowed up by the Q to be released, P enters the Q and no longer exists, it becomes a part of the memory block to be released.

}

Pn = P? (Char *) (p + 1) + P-> Len: 0; // obtain the next element of P, not necessarily idle.

If (Pn = (char *) q) // If the adjacent element behind P is the Q to be released, then Q and P are merged as a larger idle block.

{

P-> Len + = sizeof (MEM) + q-> Len; // merge p and q

Q-> Len = 0; // discard Q

Q-> next = P; // loop, actually Q has been discarded

R-> next = P; // This is very interesting.

Break;

}

If (PN <(char *) q) // If the element address next to P is less than Q, link Q to the idle linked list.

{

R-> next = Q; // update the link pointer

Q-> next = P;

Break;

}

}

}

So far, all the allocation and release are finished, isn't it easy? The purpose of the above merge operation is the same as that of the partner system, except that the partner system merges memory blocks with fixed sizes, the merge here is possible as long as there is a merge adjacent to the merge, regardless of the size of the memory block. Note that the above Code does not consider concurrency and locks, but this is the most pure and essential thing, isn't it? After this is simple and can be explained, I will write two improved versions of malloc, Microsoft and glibc.

Appendix: A pointer issue

As mentioned above, if the mem structure is not designed so skillfully, at&t's malloc will not be so simple. The most important thing is that it can be converted to the mem * type through & F, in this way, this pointer is the first element of F. If the mem structure is not designed in this way, a prev pointer may be required in addition to the next pointer. NOTE: If F is set to Mem pointer, & F is not a real mem pointer, but because the first field of mem is a mem pointer, & F should be a mem pointer in the memory, but the pointer of this pointer is also a pointer type, and the data it points to is exactly a pointer, the latter is the mem pointer type, which is in line with the layout of the mem struct. The first field of the mem struct is a mem pointer type, therefore, we can understand & F as the first element of F, because the first field of & F is F, which is only understandable. If we do not regard next as the first field, so there is no such thing, and any behavior to change the next field of & F will change F itself, except that & F is the first element of F, they are closely related to each other. This is the greatness of pointers and may bring more confusion.

Link: http://blog.csdn.net/dog250/article/details/5302958

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.