1、 软件英才网 软件行业驰名招聘网站有需要请联系我们嵌入式操作系统内核原理和开发(改进的链表内存分配算法) 之前我自己也写过基于链表的内存分配算法,但是看了 rawos 的内存分配算法,还是感觉rawos 写的要更好些。大家都知道,链表内存分配就是从链表中快速找到最合适的内存块分配给用户线程。因为这种分配是随机的,所以一般来说内存碎片是不可避免的。但是,我们在编写代码的时候还是应该注意内存合并的问题。这里我们为什么要介绍 rawos 的内存分配呢,还包括有这么几个原因,(1)整个内存链表采用循环链表的方法,使用起来简单可靠;(2)内存分配的时候所有节点都是连在一起的,通过标志数据判断当前数据是否
2、已经分配;(3)如果当前没有合适的内存,可以选择等待;(4)代码充分考虑了合并和切分的问题;(5)在遍历内存的时候灵活处理了中断问题,可以及时响应中断,这个比我考虑得要周到很多;(6)代码注释丰富,只要多读几遍,是可以慢慢理解代码内容的。整个代码的结构清晰,共四个函数,分别是创建函数、内存查找函数、内存分配函数、内存释放函数。其中在内存分配和释放的时候,都会调用到内存查找函数。cpp view plaincopy RAW_U16 raw_byte_pool_create(RAW_BYTE_POOL_STRUCT *pool_ptr, RAW_U8 *name_ptr, RAW_VOID *po
3、ol_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 common
4、_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 bloc
5、k 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 blocks header in the available bytes count. */ pool_ptr -raw_byte_pool_available = pool_size - sizeof(void *) - sizeof(RAW_U32); 软件英才网 软件行业驰名招聘
6、网站有需要请联系我们 pool_ptr -raw_byte_pool_fragments = 2; /* Calculate the end of the pools 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) =
7、 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) =
8、 RAW_BYTE_BLOCK_FREE; return RAW_SUCCESS; 内存池的创建还是比较简单的,基本的步骤如下所示,(1)验证参数的合法性,比如是否是 NULL 指针、是否对齐;(2)初始化内存池的基本参数,比如说名称、默认阻塞结构、入口地址、剩余大小等等;(3)构建两个 block 节点,前面是 free 节点,后面是 alloc 节点,两个节点构成循环节点。每个节点有三个部分组成,分别是下一跳地址、标志、buffer。cpp view plaincopy static void *raw_byte_pool_search(RAW_BYTE_POOL_STRUCT *pool
9、_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
10、_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
11、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. */ /*
12、 Pickup the next blocks 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 satis
13、fied! */ 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
14、 *) = 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 a
15、re 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);
16、 /* 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 inte
17、rrupts 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 = poo
18、l_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
19、 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) = *(
20、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 equa
21、l 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
22、_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 interr
23、upts temporarily. */ RAW_CRITICAL_EXIT(); 软件英才网 软件行业驰名招聘网站有需要请联系我们 /* Return the searched result*/ return current_ptr; 这个内存查找函数是这四个函数中最难的那个函数。看上去内容很多,其实我们可以分成两个部分分析,其中 dowhile 的这个部分就是负责查找合适的内存块,后面的ifelse 部分是对分配的节点进行后续处理,下面我们可以详细来看看, (1)验证剩余空间是否满足条件,不满足立即返回;(2)遍历所有的 block 节点,查找合适的节点,a、如果寻找到合适的节点,那么立即
24、跳出循环;b、如果没有寻找到合适的节点,可以做一些额外的工作,比如说对相连的free 节点进行合并;c、循环的结束条件是 examine_blocks 全部遍历结束,当然如果发现raw_byte_pool_owner 不为当前线程,那么一切重来;(3)对于获得的 block 节点,同样有两种方法处理,a、如果除去分配的空间,剩下的节点空间仍然很多,那么可以把当前节点拆分成两个节点进行处理;b、如果剩下的空间本身就很有限,那么全部分配给当前线程算了。(4)函数返回,current_ptr 包含了那个分配好的地址。cpp view plaincopy RAW_U16 raw_byte_alloca
25、te(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) 软件英才网 软件行业驰名招聘网站有需要请联
26、系我们 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 current_work_task = raw_task_active; /* Disable interrupts. */ RAW_CRITI
27、CAL_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,
28、 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) /* Determine if memory was found. */ if (work_ptr) /* Copy the pointer into the return destination. */ *m
29、emory_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
30、-msg = (RAW_VOID *)memory_size; raw_pend_object( 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 = bloc
31、k_state_post_process(raw_task_active, memory_ptr); RAW_CRITICAL_EXIT(); return status; 和内存查找函数相比,内存分配函数就简单多了, (1)判断参数的合法性;(2)循环查找链表节点,寻找合适的内存块,注意循环的跳出条件,即work_ptr=NULL 且没有其他线程的干扰;(3)如果寻找到了合适的节点,那么没有问题,反之需要根据 wait_option 判断是否需要把自己挂起在等待队列上;(4)线程再次得到运行的机会,memory_ptr 中包含了内存地址,函数返回。cpp view plaincopy RAW
32、_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
33、= 0) return RAW_NULL_POINTER; if (memory_ptr = 0) return RAW_NULL_POINTER; #endif byte_head_ptr = /* 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 threa
34、d 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
35、_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(
36、); /* 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)如果有线程被唤醒,那么需要重新调度,否则函数返回。