【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】
之前我自己也写过基于链表的内存分配算法,但是看了rawos的内存分配算法,还是感觉rawos写的要更好些。大家都知道,链表内存分配就是从链表中快速找到最合适的内存块分配给用户线程。因为这种分配是随机的,所以一般来说内存碎片是不可避免的。但是,我们在编写代码的时候还是应该注意内存合并的问题。这里我们为什么要介绍rawos的内存分配呢,还包括有这么几个原因,
(1)整个内存链表采用循环链表的方法,使用起来简单可靠;
(2)内存分配的时候所有节点都是连在一起的,通过标志数据判断当前数据是否已经分配;
(3)如果当前没有合适的内存,可以选择等待;
(4)代码充分考虑了合并和切分的问题;
(5)在遍历内存的时候灵活处理了中断问题,可以及时响应中断,这个比我考虑得要周到很多;
(6)代码注释丰富,只要多读几遍,是可以慢慢理解代码内容的。
整个代码的结构清晰,共四个函数,分别是创建函数、内存查找函数、内存分配函数、内存释放函数。其中在内存分配和释放的时候,都会调用到内存查找函数。
RAW_U16 raw_byte_pool_create(RAW_BYTE_POOL_STRUCT *pool_ptr, RAW_U8 *name_ptr, RAW_VOID *pool_start, RAW_U32 pool_size)
{
RAW_U8 *block_ptr; /* Working block pointer */
RAW_U8 byte_align_mask;
#if (RAW_BYTE_FUNCTION_CHECK > 0)
if (pool_ptr == 0) {
return RAW_NULL_POINTER;
}
if (pool_start == 0) {
return RAW_NULL_POINTER;
}
/* Check for invalid pool size. */
if (pool_size < RAW_BYTE_POOL_MIN) {
return RAW_BYTE_SIZE_ERROR;
}
#endif
byte_align_mask = sizeof(void *) - 1u;
/*pool_start needs 4 bytes aligned*/
if (((RAW_U32)pool_start & byte_align_mask)){
return RAW_INVALID_ALIGN;
}
/*pool_size needs 4 bytes aligned*/
if ((pool_size & byte_align_mask)) {
return RAW_INVALID_ALIGN;
}
/*Init the list*/
list_init(&pool_ptr->common_block_obj.block_list);
/* Setup the basic byte pool fields. */
pool_ptr ->common_block_obj.name = name_ptr;
pool_ptr ->common_block_obj.block_way = 0;
pool_ptr ->raw_byte_pool_search = (RAW_U8 *) pool_start;
pool_ptr ->raw_byte_pool_owner = 0;
/* Initially, the pool will have two blocks. One large block at the
beginning that is available and a small allocated block at the end
of the pool that is there just for the algorithm. Be sure to count
the available block's header in the available bytes count. */
pool_ptr ->raw_byte_pool_available = pool_size - sizeof(void *) - sizeof(RAW_U32);
pool_ptr ->raw_byte_pool_fragments = 2;
/* Calculate the end of the pool's memory area. */
block_ptr = ((RAW_U8 *) pool_start) + (RAW_U32) pool_size;
/* Backup the end of the pool pointer and build the pre-allocated block. */
block_ptr = block_ptr - sizeof(RAW_U32);
*((RAW_U32 *) block_ptr) = RAW_BYTE_BLOCK_ALLOC;
block_ptr = block_ptr - sizeof(RAW_U8 *);
*((RAW_U8 * *) block_ptr) = pool_start;
/* Now setup the large available block in the pool. */
*((RAW_U8 * *) pool_start) = block_ptr;
block_ptr = (RAW_U8 *) pool_start;
block_ptr = block_ptr + sizeof(RAW_U8 *);
*((RAW_U32 *) block_ptr) = RAW_BYTE_BLOCK_FREE;
return RAW_SUCCESS;
}
内存池的创建还是比较简单的,基本的步骤如下所示,
(1)验证参数的合法性,比如是否是NULL指针、是否对齐;
(2)初始化内存池的基本参数,比如说名称、默认阻塞结构、入口地址、剩余大小等等;
(3)构建两个block节点,前面是free节点,后面是alloc节点,两个节点构成循环节点。每个节点有三个部分组成,分别是下一跳地址、标志、buffer。
static void *raw_byte_pool_search(RAW_BYTE_POOL_STRUCT *pool_ptr, RAW_U32 memory_size)
{
RAW_U8 * current_ptr; /* Current block pointer */
RAW_U8 * next_ptr; /* Next block pointer */
RAW_U32 available_bytes; /* Calculate bytes available */
RAW_U32 examine_blocks; /* Blocks to be examined */
RAW_SR_ALLOC();
/* Disable interrupts. */
RAW_CRITICAL_ENTER();
/* First, determine if there are enough bytes in the pool. */
if (memory_size >= pool_ptr ->raw_byte_pool_available) {
/* Restore interrupts. */
RAW_CRITICAL_EXIT();
/* Not enough memory, return a NULL pointer. */
return 0;
}
/* Walk through the memory pool in search for a large enough block. */
current_ptr = pool_ptr ->raw_byte_pool_search;
examine_blocks = pool_ptr ->raw_byte_pool_fragments + 1;
available_bytes = 0;
do {
/* Check to see if this block is free. */
if (*((RAW_U32 *) (current_ptr + sizeof(RAW_U8 *))) == RAW_BYTE_BLOCK_FREE) {
/* Block is free, see if it is large enough. */
/* Pickup the next block's pointer. */
next_ptr = *((RAW_U8 * *) current_ptr);
/* Calculate the number of byte available in this block. */
available_bytes = next_ptr - current_ptr - sizeof(RAW_U8 *) - sizeof(RAW_U32);
/* If this is large enough, we are done because our first-fit algorithm
has been satisfied! */
if (available_bytes >= memory_size) {
/* Find the suitable position */
break;
}
else {
/* Clear the available bytes variable. */
available_bytes = 0;
/* Not enough memory, check to see if the neighbor is
free and can be merged. */
if (*((RAW_U32 *) (next_ptr + sizeof(RAW_U8 *))) == RAW_BYTE_BLOCK_FREE) {
/* Yes, neighbor block can be merged! This is quickly accomplished
by updating the current block with the next blocks pointer. */
*((RAW_U8 * *) current_ptr) = *((RAW_U8 * *) next_ptr);
/* Reduce the fragment number, and does not need to increase available bytes since
they are already there*/
pool_ptr ->raw_byte_pool_fragments--;
/* Update the search pointer, if it is involved */
if (pool_ptr ->raw_byte_pool_search == next_ptr) {
pool_ptr ->raw_byte_pool_search = current_ptr;
}
}
else {
/* Neighbor is not free so get to the next search point*/
current_ptr = *((RAW_U8 * *) next_ptr);
/* Reduse the examined block since we have skiped one search */
if (examine_blocks) {
examine_blocks--;
}
}
}
}
else
{
/* Block is not free, move to next block. */
current_ptr = *((RAW_U8 * *) current_ptr);
}
/* finish one block search*/
if (examine_blocks) {
examine_blocks--;
}
/* Restore interrupts temporarily. */
RAW_CRITICAL_EXIT();
/* Disable interrupts. */
RAW_CRITICAL_ENTER();
/* Determine if anything has changed in terms of pool ownership. */
if (pool_ptr ->raw_byte_pool_owner != raw_task_active)
{
/* Pool changed ownership during interrupts.so we reset the search*/
current_ptr = pool_ptr ->raw_byte_pool_search;
examine_blocks = pool_ptr ->raw_byte_pool_fragments + 1;
/* Setup our ownership again. */
pool_ptr ->raw_byte_pool_owner = raw_task_active;
}
} while (examine_blocks);
/* Determine if a block was found. If so, determine if it needs to be
split. */
if (available_bytes) {
/* Do we need to split this block if this is big enough.*/
if ((available_bytes - memory_size) >= ((RAW_U32) RAW_BYTE_BLOCK_MIN)) {
/* Split the block. */
next_ptr = current_ptr + memory_size + sizeof(RAW_U8 *) + sizeof(RAW_U32);
/* Setup the new free block. */
*((RAW_U8 * *) next_ptr) = *((RAW_U8 * *) current_ptr);
*((RAW_U32 *) (next_ptr + sizeof(RAW_U8 *))) = RAW_BYTE_BLOCK_FREE;
/* Increase the total fragment counter. */
pool_ptr ->raw_byte_pool_fragments++;
/* Update the current pointer to point at the newly created block. */
*((RAW_U8 * *) current_ptr) = next_ptr;
/* Set available equal to memory size for subsequent calculation. */
available_bytes = memory_size;
}
/* In any case, mark the current block as allocated. */
*((RAW_U32 *) (current_ptr + sizeof(RAW_U8 *))) = RAW_BYTE_BLOCK_ALLOC;
/* Reduce the number of available bytes in the pool. */
pool_ptr ->raw_byte_pool_available = pool_ptr ->raw_byte_pool_available - available_bytes
- sizeof(RAW_U8 *) - sizeof(RAW_U32);
/* Adjust the pointer for the application. */
current_ptr = current_ptr + sizeof(RAW_U8 *) + sizeof(RAW_U32);
}
else {
/* Set current pointer to NULL to indicate nothing was found. */
current_ptr = 0;
}
/* Restore interrupts temporarily. */
RAW_CRITICAL_EXIT();
/* Return the searched result*/
return current_ptr;
}
这个内存查找函数是这四个函数中最难的那个函数。看上去内容很多,其实我们可以分成两个部分分析,其中do~while的这个部分就是负责查找合适的内存块,后面的if~else部分是对分配的节点进行后续处理,下面我们可以详细来看看,
(1)验证剩余空间是否满足条件,不满足立即返回;
(2)遍历所有的block节点,查找合适的节点,
a、如果寻找到合适的节点,那么立即跳出循环;
b、如果没有寻找到合适的节点,可以做一些额外的工作,比如说对相连的free节点进行合并;
c、循环的结束条件是examine_blocks全部遍历结束,当然如果发现raw_byte_pool_owner不为当前线程,那么一切重来;
(3)对于获得的block节点,同样有两种方法处理,
a、如果除去分配的空间,剩下的节点空间仍然很多,那么可以把当前节点拆分成两个节点进行处理;
b、如果剩下的空间本身就很有限,那么全部分配给当前线程算了。
(4)函数返回,current_ptr包含了那个分配好的地址。
RAW_U16 raw_byte_allocate(RAW_BYTE_POOL_STRUCT *pool_ptr, RAW_VOID **memory_ptr,
RAW_U32 memory_size, RAW_U32 wait_option)
{
RAW_U16 status; /* Return status */
RAW_U8 *work_ptr; /* Working byte pointer */
RAW_TASK_OBJ *current_work_task;
RAW_SR_ALLOC();
#if (RAW_BYTE_FUNCTION_CHECK > 0)
if (pool_ptr == 0) {
return RAW_NULL_POINTER;
}
if (memory_ptr == 0) {
return RAW_NULL_POINTER;
}
if (raw_int_nesting) {
if (wait_option != RAW_NO_WAIT)
return RAW_NOT_CALLED_BY_ISR;
}
#endif
/* align the memory size to 4 byte*/
memory_size = ((memory_size & (~3u)) +4u);
current_work_task = raw_task_active;
/* Disable interrupts. */
RAW_CRITICAL_ENTER();
/* Loop to handle cases where the owner of the pool changed. */
do {
/* Indicate that this thread is the current owner. */
pool_ptr ->raw_byte_pool_owner = current_work_task;
/* Restore interrupts. */
RAW_CRITICAL_EXIT();
/*Search for free memory*/
work_ptr = raw_byte_pool_search(pool_ptr, memory_size);
/* Disable interrupts. */
RAW_CRITICAL_ENTER();
/*if raw_byte_pool_owner changed and we have not got memory yet, continue tom do search*/
} while ((!work_ptr) && (pool_ptr ->raw_byte_pool_owner != current_work_task));
/* Determine if memory was found. */
if (work_ptr) {
/* Copy the pointer into the return destination. */
*memory_ptr = (RAW_U8 *) work_ptr;
RAW_CRITICAL_EXIT();
/* Set the status to success. */
return RAW_SUCCESS;
}
else {
if (wait_option) {
/*system is locked so task can not be blocked just return immediately*/
if (raw_sched_lock) {
*memory_ptr = 0;
RAW_CRITICAL_EXIT();
return RAW_SCHED_DISABLE;
}
raw_task_active->msg = (RAW_VOID *)memory_size;
raw_pend_object(&pool_ptr->common_block_obj, raw_task_active, wait_option);
}
else {
*memory_ptr = 0;
RAW_CRITICAL_EXIT();
/* Immediate return, return error completion. */
return RAW_NO_MEMORY;
}
}
/* Restore interrupts. */
RAW_CRITICAL_EXIT();
raw_sched();
RAW_CRITICAL_ENTER();
*memory_ptr = 0;
status = block_state_post_process(raw_task_active, memory_ptr);
RAW_CRITICAL_EXIT();
return status;
}
和内存查找函数相比,内存分配函数就简单多了,
(1)判断参数的合法性;
(2)循环查找链表节点,寻找合适的内存块,注意循环的跳出条件,即work_ptr==NULL且没有其他线程的干扰;
(3)如果寻找到了合适的节点,那么没有问题,反之需要根据wait_option判断是否需要把自己挂起在等待队列上;
(4)线程再次得到运行的机会,memory_ptr中包含了内存地址,函数返回。
RAW_U16 raw_byte_release(RAW_BYTE_POOL_STRUCT *pool_ptr, void *memory_ptr)
{
RAW_U8 *work_ptr; /* Working block pointer */
LIST *iter;
LIST *iter_temp;
LIST *byte_head_ptr;
RAW_TASK_OBJ *task_ptr;
RAW_U8 need_sche = 0;
RAW_SR_ALLOC();
#if (RAW_BYTE_FUNCTION_CHECK > 0)
if (pool_ptr == 0) {
return RAW_NULL_POINTER;
}
if (memory_ptr == 0) {
return RAW_NULL_POINTER;
}
#endif
byte_head_ptr = &pool_ptr->common_block_obj.block_list;
/* Back off the memory pointer to pickup its header. */
work_ptr = (RAW_U8 *) memory_ptr - sizeof(RAW_U8 *) - sizeof(RAW_U32);
/* Disable interrupts. */
RAW_CRITICAL_ENTER();
/* Indicate that this thread is the current owner. */
pool_ptr ->raw_byte_pool_owner = raw_task_active;
/* Release the memory.*/
*((RAW_U32 *) (work_ptr + sizeof(RAW_U8 *))) = RAW_BYTE_BLOCK_FREE;
/* Update the number of available bytes in the pool. */
pool_ptr ->raw_byte_pool_available =
pool_ptr ->raw_byte_pool_available + (*((RAW_U8 * *) (work_ptr)) - work_ptr);
/* Set the pool search value appropriately. */
pool_ptr ->raw_byte_pool_search = work_ptr;
iter = byte_head_ptr->next;
while (iter != byte_head_ptr) {
iter_temp = iter->next;
task_ptr = list_entry(iter, RAW_TASK_OBJ, task_list);
RAW_CRITICAL_EXIT();
/* See if the request can be satisfied. */
work_ptr = raw_byte_pool_search(pool_ptr, (RAW_U32)task_ptr->msg);
RAW_CRITICAL_ENTER();
/* If there is not enough memory, break this loop! */
if (!work_ptr) {
break;
}
wake_send_msg(task_ptr, (RAW_VOID *)work_ptr);
need_sche = 1;
iter = iter_temp;
}
RAW_CRITICAL_EXIT();
if (need_sche) {
raw_sched();
}
return RAW_SUCCESS;
}
内存释放后,本来我们只需要把内存节点的标志设置为RAW_BYTE_BLOCK_FREE即可。可是,我们还需要判断当前是否存在等待唤醒的线程,是否可以为等待线程寻找到合适的内存节点,整个函数基本流程如下所示,
(1)判断参数合法性;
(2)将当前block节点设置为可用,调整pool中的参数数据;
(3)遍历等待线程,并为之查找到合适的节点,寻找到节点跳出,遍历完所有等待节点也跳出;
(4)如果有线程被唤醒,那么需要重新调度,否则函数返回。