Chinaunix首页 | 论坛 | 博客
  • 博客访问: 218507
  • 博文数量: 71
  • 博客积分: 1420
  • 博客等级: 上尉
  • 技术积分: 952
  • 用 户 组: 普通用户
  • 注册时间: 2008-01-11 14:26
文章存档

2009年(16)

2008年(55)

我的朋友

分类: LINUX

2008-04-22 11:09:40

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指向的空间,kfreefree的区别在于是否关闭了中断,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[i].free_link.next = &free_area[i].free_link;

    free_area[i].free_link.prev = &free_area[i].free_link;

    free_area[i].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){ /* 这里这样做是让需要的空间按照四字节对齐,这里这样做只想让ByCore32为机上面跑,当然这里设计也比较垃圾。以后再修改吧 */

tmp_size >>= 0x2;

tmp_size += 0x1;

    tmp_size <<= 0x02;

  }

 

  mac_disable_irq();        /* 需要关闭中断 */

  for(i=0;i

    if(free_area[i].size < tmp_size || (free_area[i].free_link.next == &free_area[i].free_link))

      continue;           /* 这里开始检索空闲块索引表 */

    else{

      plist = free_area[i].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[i].free_link,plist); /* 将该空闲块从空闲队列中删除 */

          break;

        }else plist = plist->next; /* 继续检索此空闲块队列的下一个空闲块 */

      }while(plist != free_area[i].free_link.next);

    }

    if(flag == TRUE) break; /* 找到合适的空闲块,退出检索 */

  }

 

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

    mac_enable_irq();

    return NULL;

  }

 

 

  if(page->size - tmp_size <= MEM_PAGE_LIMIT){  /* 检查该空闲块是否需要分裂,主要通过该空闲块的大小与需分配块大小的差来判断,如果差小于等于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[i].size >= pleft->size){

      add_node_seque_rear(&free_area[i].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;     /* 设置当前块为空闲,updown指针分别指向它的上面和下面的内存块 */

  up = page->mem_up;

  down = page->mem_down;

 

  if(down != NULL && down->status == MEM_FREE){   /* 如果存在下面块,且状态为空闲,则将当前块与它的下面块合并为一个块 */

    for(i=0;i查询空闲块索引表,将下面块从空闲块中删除,以便与当前块合并 */

      if(free_area[i].size >= down->size){ /* 找到下面块的队列头,调用del_node_seque函数将下面块从队列中删除 */

        del_node_seque(&free_area[i].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[i].size >= up->size){

        del_node_seque(&free_area[i].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[i].size >= page->size){

      add_node_seque_rear(&free_area[i].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个字节设置成ckgetmemlist( )函数的功能只是返回内存的起始地址,也就是前面提到的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!

阅读(1057) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~