本文主要是介绍malloc free,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
- CosOS内核中使用一个内核堆来管理内存,内核通过kmalloc和kfree从内核堆中申请和释放内存。CosOS为用户态编写的库函数中也实现了用户态堆,应用程序通过malloc和free从堆中申请释放内存。
- 内核堆和用户态堆的算法类似,都通过调用alloc和free_int来操作堆。下面以代码和注释的形式详细介绍CosOS中 alloc和free_int这两个函数的实现。
- 在堆中,可以用的空闲内存块成为hole,堆使用了一个链表来保存堆中所有可用的hole。Alloc函数就是在这个链表中查找到一个大小合适的hole然后根据请求分配的大小把hole切分成两部分,一部分返回给调用者使用,剩下的重新加入到链表中。
- /* alloc两个参数,size代表请求分配内存的大小,page_align代表请求内存空间的首地址是否必须页对齐,即地址是能被0x1000整除的。实现这个特性是因为内核中有些空间必须页对齐,例如页表、也目录。*/
- void *alloc(u32int size, u8int page_align)
- {
- register struct hole *hp, *prev_ptr;
- u32int old_base;
- prev_ptr = NIL_HOLE;
- hp = hole_head;
- //通过变量hole链表,查找一个合适的hole
- while(hp != NIL_HOLE)
- {
- if(page_align)
- {
- //由于页数限制,页对齐实现部分代码省略。
- }else if (hp->h_len >= size)
- {
- //找到了一个足够大的hole,使用这个hole。
- //将其地址保存在临时变量old_base中,作为alloc的返回值,
- old_base =hp->h_base;
- //由于hole比请求的空间大,剩余部分需放入堆中。只需重新调整hole的首地址和长度即可
- hp->h_base +=size;
- hp->h_len -=size;
- //记住使用使用过的内存当中,最高的地址,保存在high_watermark当中
- if(hp->h_base > high_watermark)
- high_watermark= hp->h_base;
- //经过上面代码调整hole的大小之后,如果hole的大小为0,即hole的大小与请求大小相同,则将其从hole链表中移除。
- if (hp->h_len == 0)
- del_slot(prev_ptr,hp);
- //返回分配内存块得首地址。
- return (void*)(old_base);
- }
- prev_ptr = hp;
- hp = hp->h_next;
- }
- //若运行到这里,说明堆中没有合适的内存块,返回一个空指针。
- return0;
- }
- free_int用于将首地址为base,长度为clicks的内存块释放,重新加入到堆的hole列表中。
- void free_int(u32int base, u32int clicks)
- {
- struct hole*hp, *new_ptr, *prev_ptr;
- //如果要释放的内存块大小为0,则直接返回
- if (clicks ==0)
- return;
- if ( (new_ptr= free_slots) == NIL_HOLE)
- ASSERT(0);
- //将内存块设置成一个hole
- new_ptr->h_base = base;
- new_ptr->h_len = clicks;
- free_slots = new_ptr->h_next;
- hp = hole_head;
- //如果内存块得地址是当前hole链表中最小的或者hole链表为空,则把它插入到链表的第一个位置。
- if (hp ==NIL_HOLE || base <= hp->h_base) {
- //插入到第一个位置
- new_ptr->h_next = hp;
- hole_head = new_ptr;
- //检测是否能与相邻的hole合并成一个更大的hole,这样可以减少内存碎片。
- merge(new_ptr);
- return;
- }
- //hole没有被加入到首位置,则执行下面代码
- prev_ptr = NIL_HOLE;
- //hole链表中的hole是按照内存块首地址由低到高排序的,插入新的hole时必须保证插入之后链表还是有序的。因此需要找到一个合适的位置插入。
- while (hp !=NIL_HOLE && base > hp->h_base) {
- prev_ptr = hp;
- hp = hp->h_next;
- }
- //找到了合适的位置,插入其中
- new_ptr->h_next = prev_ptr->h_next;
- prev_ptr->h_next = new_ptr;
- //检测是否能与相邻的hole合并成一个更大的hole,这样可以减少内存碎片。
- merge(prev_ptr);
- }
- Hole合并的实现较为简单,只需检测hole的base加上size是否与下一hole的base相同即可。这里不详细介绍
- alloc和free_int是最核心最原始的两个函数。free_int使用时第二个参数需要确定释放内存的大小,而用户态的malloc、free中,free是不需要知道内存大小,只需传入需要释放的地址即可。
- 为了在使用free时无需知道内存块得大小也可以正确释放内存块,CosOS中采用的方法是,将内存块得大小保存在内存块首地址前得一个整型空间中,具体实现如下:
- //size为申请内存的大小
- void * malloc(u32int size)
- {
- //使用alloc从堆中申请内存时,多申请4个字节,用来保存内存块大小。
- void *p= alloc(size + 4);
- //在返回内存块的前4个字节中(即一个整型中)保存内存块大小。
- *((u32int*)p) = size;
- //返回这4个字节之后的地址,防止使用内存块时,大小被破坏。
- return(void*)((u32int)p + 4);
- }
- void free(void *p)
- {
- //从内存块首地址的前4个字节中获取内存块的大小,保存在size中。
- u32int size = *((u32int*)p - 1);
- //使用free_int释放这块内存。
- free_int((u32int)p - 4, size + 4);
- }
CosOS内核中使用一个内核堆来管理内存,内核通过kmalloc和kfree从内核堆中申请和释放内存。CosOS为用户态编写的库函数中也实现了用户态堆,应用程序通过malloc和free从堆中申请释放内存。内核堆和用户态堆的算法类似,都通过调用alloc和free_int来操作堆。下面以代码和注释的形式详细介绍CosOS中 alloc和free_int这两个函数的实现。在堆中,可以用的空闲内存块成为hole,堆使用了一个链表来保存堆中所有可用的hole。Alloc函数就是在这个链表中查找到一个大小合适的hole然后根据请求分配的大小把hole切分成两部分,一部分返回给调用者使用,剩下的重新加入到链表中。/* alloc两个参数,size代表请求分配内存的大小,page_align代表请求内存空间的首地址是否必须页对齐,即地址是能被0x1000整除的。实现这个特性是因为内核中有些空间必须页对齐,例如页表、也目录。*/void *alloc(u32int size, u8int page_align){register struct hole *hp, *prev_ptr;u32int old_base;prev_ptr = NIL_HOLE;hp = hole_head;//通过变量hole链表,查找一个合适的holewhile(hp != NIL_HOLE) {if(page_align){//由于页数限制,页对齐实现部分代码省略。}else if (hp->h_len >= size) {//找到了一个足够大的hole,使用这个hole。//将其地址保存在临时变量old_base中,作为alloc的返回值,old_base =hp->h_base; //由于hole比请求的空间大,剩余部分需放入堆中。只需重新调整hole的首地址和长度即可hp->h_base +=size; hp->h_len -=size;//记住使用使用过的内存当中,最高的地址,保存在high_watermark当中if(hp->h_base > high_watermark)high_watermark= hp->h_base;//经过上面代码调整hole的大小之后,如果hole的大小为0,即hole的大小与请求大小相同,则将其从hole链表中移除。if (hp->h_len == 0) del_slot(prev_ptr,hp);//返回分配内存块得首地址。return (void*)(old_base);}prev_ptr = hp;hp = hp->h_next;}//若运行到这里,说明堆中没有合适的内存块,返回一个空指针。return0;}free_int用于将首地址为base,长度为clicks的内存块释放,重新加入到堆的hole列表中。void free_int(u32int base, u32int clicks){struct hole*hp, *new_ptr, *prev_ptr;//如果要释放的内存块大小为0,则直接返回if (clicks ==0) return;if ( (new_ptr= free_slots) == NIL_HOLE) ASSERT(0);//将内存块设置成一个holenew_ptr->h_base = base;new_ptr->h_len = clicks;free_slots = new_ptr->h_next;hp = hole_head;//如果内存块得地址是当前hole链表中最小的或者hole链表为空,则把它插入到链表的第一个位置。if (hp ==NIL_HOLE || base <= hp->h_base) {//插入到第一个位置new_ptr->h_next = hp;hole_head = new_ptr;//检测是否能与相邻的hole合并成一个更大的hole,这样可以减少内存碎片。merge(new_ptr);return;}//hole没有被加入到首位置,则执行下面代码prev_ptr = NIL_HOLE;//hole链表中的hole是按照内存块首地址由低到高排序的,插入新的hole时必须保证插入之后链表还是有序的。因此需要找到一个合适的位置插入。while (hp !=NIL_HOLE && base > hp->h_base) {prev_ptr = hp;hp = hp->h_next;}//找到了合适的位置,插入其中new_ptr->h_next = prev_ptr->h_next;prev_ptr->h_next = new_ptr;//检测是否能与相邻的hole合并成一个更大的hole,这样可以减少内存碎片。merge(prev_ptr); }Hole合并的实现较为简单,只需检测hole的base加上size是否与下一hole的base相同即可。这里不详细介绍alloc和free_int是最核心最原始的两个函数。free_int使用时第二个参数需要确定释放内存的大小,而用户态的malloc、free中,free是不需要知道内存大小,只需传入需要释放的地址即可。为了在使用free时无需知道内存块得大小也可以正确释放内存块,CosOS中采用的方法是,将内存块得大小保存在内存块首地址前得一个整型空间中,具体实现如下://size为申请内存的大小void * malloc(u32int size){//使用alloc从堆中申请内存时,多申请4个字节,用来保存内存块大小。void *p= alloc(size + 4);//在返回内存块的前4个字节中(即一个整型中)保存内存块大小。*((u32int*)p) = size;//返回这4个字节之后的地址,防止使用内存块时,大小被破坏。return(void*)((u32int)p + 4);}void free(void *p){//从内存块首地址的前4个字节中获取内存块的大小,保存在size中。u32int size = *((u32int*)p - 1);//使用free_int释放这块内存。free_int((u32int)p - 4, size + 4);}
这篇关于malloc free的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!