免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 734 | 回复: 0

嵌入式操作系统内核实现(六) [复制链接]

论坛徽章:
0
发表于 2008-04-22 11:09 |显示全部楼层
2.2.4.2 函数接口
这应该是本章的结尾部分了,上面说了那么多的文字,最终还是要用程序语言来实现,这才是最重要的。ByCore的内存管理部分提供的函数接口不多,全部的函数如下:
1) void initmem(void);
2) void *kmalloc(uword_t Size);
3) void free(void *pfree);
4) void kfree(void *pfree);
5) void kmemset(void *buffer, uword_t c, uword_t count);
6) page_t* kgetmemlist(void);
第一个函数为初始化内存块,该函数在内核初始化阶段被调用,用户不必调用它;第二个函数向内核申请分配一个大小为size的空间;第三和第四为释放有pfree指向的空间,kfree与free的区别在于是否关闭了中断,kfree只是简单调用free;第五个函数将buffer指向的空间设置为c代表的值;最后一个函数简单返回整个内存的起始地址,这个地址被转化成page_t类型,这样可以在应用程序中检索所有的内存分配情况。下面按照编号顺序逐个解释这些函数的实现。
1.       initmem( )函数
此函数的主要功能是初始化空闲块索引表,并将整个内存空间当成一个空闲块插入到空闲块索引表中。它的源代码如下:
void initmem(void){
  uword_t i;
  uword_t index = 1;
  page_t *page;
/* 初始化空闲块索引表free_area[] */
  for(i=0;i
    free_area.free_link.next = &free_area.free_link;
    free_area.free_link.prev = &free_area.free_link;
    free_area.size = index * MEM_PAGE_SIZE;
    index *= 2;
  }

  page = ((page_t *)MEM_START_ADDR); /* 将内存首地址转化成page_t结构,该结构用于保存该块的数据信息 */

  mem = page;  /* mem为全局指针,它的定义为static page_t *mem; */
  page->free_link.prev = &page->free_link;  /* 初始化page_t结构中的各个域 */
  page->free_link.next = &page->free_link;
  page->mem_up = NULL;
  page->mem_down = NULL;
  page->size = MEM_SIZE;
  page->status = MEM_FREE;
  add_node_seque_rear(&free_area[MEM_PAGE_LIST-1].free_link,&page->free_link); /* 将该空闲块插入到适当的空闲队列中,add_node_seque_rear 函数可以参见前面的内容,前面已经提供了源代码 */
}
2.       kmalloc ( )函数
这个函数应该算是比较核心的函数了,应用程序会调用它申请一个大小为size的空间,如果该函数成功将返回一个大小为Size的内存块的首地址。如果理解了前面小节的内容看懂该函数的源代码也很容易,具体源代码如下所示:
void *kmalloc(uword_t Size){
  list_t *plist;
  page_t *page;
  page_t *pleft;
  uword_t i;
  uword_t flag = FALSE;
  uword_t tmp_size = Size + sizeof(page_t);  /* 这里的做法是因为一个内存块的描述结构page_t就在该块的最前面,所以这里应该在Size的基础上多申请一个page_t大小的空间。 */

  if(tmp_size & 0x03){ /* 这里这样做是让需要的空间按照四字节对齐,这里这样做只想让ByCore在32为机上面跑,当然这里设计也比较垃圾。以后再修改吧 */
tmp_size >>= 0x2;
tmp_size += 0x1;
    tmp_size
  }

  mac_disable_irq();        /* 需要关闭中断 */
  for(i=0;i
    if(free_area.size
      continue;           /* 这里开始检索空闲块索引表 */
    else{
      plist = free_area.free_link.next;
      do{
        page = mac_find_entry(plist,page_t,free_link);
        if(page->size >= tmp_size){ /* 找到合适的空闲块 */
          flag = TRUE;
          del_node_seque(&free_area.free_link,plist); /* 将该空闲块从空闲队列中删除 */
          break;
        }else plist = plist->next; /* 继续检索此空闲块队列的下一个空闲块 */
      }while(plist != free_area.free_link.next);
    }
    if(flag == TRUE) break; /* 找到合适的空闲块,退出检索 */
  }

  if(flag != TRUE){ /* 检索完所有的空闲内存块,未找到合适的空闲块,打开中断并返回NULL。*/
    mac_enable_irq();
    return NULL;
  }


  if(page->size - tmp_size   /* 检查该空闲块是否需要分裂,主要通过该空闲块的大小与需分配块大小的差来判断,如果差小于等于MEM_PAGE_LIMIT就全部分配该块,反之,需要分裂此空闲块。 */
      page->status = MEM_ALLOC;  /* 将该块标记为MEM_ALLOC表示为已分配。 */
    mac_enable_irq();
    return (page+1);
  }
/* 下面的代码表示空闲块需要分裂成两块,一块分配出去,另一块插入到合适的空闲块链表中 */
  pleft = (page_t *)((char_t *)page + tmp_size);
  pleft->size = page->size - tmp_size;
  pleft->status = MEM_FREE;
  pleft->mem_up = page;
  pleft->mem_down = page->mem_down;

  if(page->mem_down != NULL)
    page->mem_down->mem_up = pleft;

  page->status = MEM_ALLOC;
  page->mem_down = pleft;
  page->size = tmp_size;
    /* 将剩余的空闲块(pleft指向的),插入到合适的空闲块队列中。 */
  for(i=0;i
    if(free_area.size >= pleft->size){
      add_node_seque_rear(&free_area.free_link,&pleft->free_link);
      break;
    }
  }
  mac_enable_irq();    /* 开中断返回 */
  return (page + 1);
}
3.       free (void *pfree)函数
该函数也是非常重要的,它的功能是将pfree指向的一块空间收回,主要算法为,首先根据pfree找到该块的page_t结构,上面说过page_t结构就从该块的起始处开始,根据kmalloc源码的实现,只要将pfree转换成page_t类型然后减一就能找到该块page_t结构。所以这里就有个要求,该要求就是pfree通过转换成page_t后减一必须能找到该块的page_t的起始地址,这个要求给编程带来的不方便,这也是比较垃圾的地方,有机会一定要修改,不过现在还没有想到如何修改。先看看free的代码是怎样实现的:
void free(void *pfree){
  uword_t i;
  page_t *page;
  page_t *up;
  page_t *down;

  page = (((page_t*)(pfree )) - 1);  /* 得到当前块的page_t结构,取得它的数据信息 */

  if(page->status != MEM_ALLOC)
    return;
  
  page->status = MEM_FREE;     /* 设置当前块为空闲,up和down指针分别指向它的上面和下面的内存块 */
  up = page->mem_up;
  down = page->mem_down;

  if(down != NULL && down->status == MEM_FREE){   /* 如果存在下面块,且状态为空闲,则将当前块与它的下面块合并为一个块 */
    for(i=0;i查询空闲块索引表,将下面块从空闲块中删除,以便与当前块合并 */
      if(free_area.size >= down->size){ /* 找到下面块的队列头,调用del_node_seque函数将下面块从队列中删除 */
        del_node_seque(&free_area.free_link,&down->free_link);
        break;
      }
    }
    page->size += down->size;  /* down指针指向的page_t结构已经没有用处了,修改page指针指向的page_t结构 */
    down->size = 0;
    down->status = 0;
    down->mem_up =  NULL;
    page->mem_down =  down->mem_down;

    if(down->mem_down != NULL){ // 如果下面块还有下面块,则将它的mem_up指向page
      down->mem_down->mem_up = page;
      down->mem_down =  NULL;
    }
  }

  if(up != NULL && up->status == MEM_FREE){ /* 如果存在上面块,且状态为空闲,则将当前块与下面块合并,合并算法与上面类似 */
for(i=0;i
      if(free_area.size >= up->size){
        del_node_seque(&free_area.free_link,&up->free_link);
        break;
      }
    }
    up->size += page->size;
    page->size = 0;
    page->status = 0;
    page->mem_up = NULL;      
    up->mem_down = page->mem_down;
   
    if(page->mem_down != NULL){
      page->mem_down->mem_up = up;
      page->mem_down = NULL;
    }
    page = up;       /* page指向合并好的新块 */
  }

  for(i=0;i将新块插入到合适的空闲块队列中 */
    if(free_area.size >= page->size){
      add_node_seque_rear(&free_area.free_link,&page->free_link);
      break;
    }
  }
}
4.       kfree (void *pfree)函数
kfree( )函数的实现非常简单,它只是在调用free( )之前,关闭中断,free( )返回之后再打开中断。free( )函数一般在内核中被调用,应用程序不需要调用free( )函数,而只需调用kfree( )函数,kfree( )函数的实现如下:
void kfree(void *pfree){
  mac_disable_irq();
  free(pfree);
  mac_enable_irq();
}
5. kmemset( )函数与kgetmemlist( )函数
kmemset( )函数的原型是(void *buffer, uword_t c, uword_t count),它的功能是将从buffer地址开始的count个字节设置成c;kgetmemlist( )函数的功能只是返回内存的起始地址,也就是前面提到的page_t *mem指向的位置。它们的实现如下:
void kmemset(void *buffer, uword_t c, uword_t count){
  char_t *p = buffer;
  while(count--)
    *p++ = c;
}
page_t* kgetmemlist(void){
  return mem;
}
OK,到现在为止,内存管理一节到此结束,后面的很多地方需要调用kmalloc( )和kfree( )函数,如果不明白它们的实现方式和工作原理,也没关系,记住它们的功能就行了。好了,see you next time!


本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u1/59291/showart_572380.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

DTCC2020中国数据库技术大会

【架构革新 高效可控】2020年12月21日-23日第十一届中国数据库技术大会将在北京隆重召开。

大会设置2大主会场,20+技术专场,将邀请超百位行业专家,重点围绕数据架构、AI与大数据、传统企业数据库实践和国产开源数据库等内容展开分享和探讨,为广大数据领域从业人士提供一场年度盛会和交流平台。

http://dtcc.it168.com


大会官网>>
  

北京盛拓优讯信息技术有限公司. 版权所有 16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122
中国互联网协会会员  联系我们:huangweiwei@it168.com
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP