The preceding sections describe two storage pool implementations that both use a linear list to keep track of the free areas. When the singly-linked list is used, the linear list is kept sorted by address; when the doubly-linked list is used, the order of the areas in the list is essentially random.
Each time an area is to be reserved, the free lists are searched in order to find an area that is sufficiently large to satisfy the request. Since there is no direct relationship between the size of an area and its position in the free list, the search has worst-case running time that is O(l), where l is the length of the free list. And in the worst case l is O(n), where n is the number of blocks in the storage pool.
In this section we present a storage pool implementation, called a buddy system , that uses more than one free list. All of the areas in a given free list have the same size and there is a separate free list for each available size of area. As a result a suitable free area can be found quickly.
Given a storage pool of size N bytes, we would require N free lists altogether if we were to place no restriction the allowable size of an area. This is clearly infeasible. Instead we require that N is a power of two, i.e., for some positive integer m. Furthermore, the size of each area in the pool must also be a power of two. As a result, we only need m+1 free lists, since the allowed sizes of an area (in bytes) are
The key feature of a buddy system is that when a request is made for an area of size for some k less than m, we first look in the corresponding free list for an area with the correct size. Notice that if there are no areas of size left, we can obtain one by splitting an area of size in two. And if there are no areas of size left, we can obtain one of those by splitting an area of size in two, and so on.
The two areas obtained when a larger area is split in two are called buddies. Whenever an area is freed, we check to see if its buddy is also free. If an area of size and its buddy are both free, they can be combined into a single area of size .
Of course, the user does not always need an an amount of storage that is exactly a power of two. In those situations where it is not, we shall allocate an amount of memory that is the smallest power of two no less than the amount requested.
Figure shows a memory map of a storage pool managed using the buddy system. The reserved areas in Figure are exactly the same as those shown in Figure . Figure shows an important characteristic of the buddy pool: An area of size bytes is always aligned on a byte boundary. E.g., all 1KB areas are aligned on 1KB boundaries. I.e., they begin at 0KB, 1KB, 2KB, ...
Figure: Memory Map of a Buddy System Storage Pool
Let b be the offset from the start of the pool (in bytes) of an area of size . Then for b to be aligned on a byte boundary means that . In other words, the binary representation of the number b has the form
where is the bit in the representation of b.
If we take the block of size at offset b and split it into two blocks of size , the offsets of the two blocks which result are
I.e., the offsets of the buddies of size differ in only the bit position. This gives us a very simple way to determine the position of the buddy of a given area. I.e., given the offset of a buddy of size is b, the offset of the buddy is given by
Fortunately, it is quite simple to compute Equation since all that we need to do is toggle the bit of the binary representation b. This can be done using the bitwise exclusive-or operation as the following function definition shows:
unsigned int Buddy (unsigned int b, unsigned int k) { return b ^ (1 << (k - 1U)); }
As before, we implement the storage pool as an array of Blocks. The structure of a Block is shown in Figure . A sequence of contiguous blocks in the array constitutes and area. This time the size (in bytes) of every area in the pool is an integer power of two. The first block in each area is used to keep track the entire area.
Figure: BuddyPool::Block Structure Layout
The structure of the block is quite similar to that used in the implementation of the DoublyLinkedPool class. I.e., the header is comprised of two parts: A single bit which indicates whether the area represented by the block is reserved or free and a field called k which specifies the size of the area. I.e., the size of the block is bytes.
The free lists are implemented as doubly-linked lists. Therefore, a free block contains two pointers, prev and next, which point to the previous and next areas (respectively) in the free list.