ucore lab2及challenge

练习1:实现 first-fit 连续物理内存分配算法

修改default_pmm.c中的default_initdefault_init_memmap

default_alloc_pagesdefault_free_pages等相关函数。

default_pmm.c有一段很长的注释,首先应该要仔细阅读注释。

First, you should get familiar with the struct list in list.h. Structlist is a simple doubly linked list implementation. You should know how to use

list_init,list_add(list_add_after),list_add_before,list_del,

list_next, list_prev.

list.h

struct list_entry {
   
    struct list_entry *prev, *next;//两个指针,父子节点
};
typedef struct list_entry list_entry_t;//重命名为list_entry_t

list_init初始化一个新的list_entry

list_addlist_add_afterlist_add_after都是添加一个新的条目

list_del是从列表中删除一个条目

list_del_init是从列表中删除一个条目并重定义它

list_empty是判断列表是否为空

list_nextlist_prev分别是获取列表的前一项和后一项

There’s a tricky method that is to transform a general list struct to a special struct (such as struct page), using the following MACROs: le2page(in memlayout.h), (and in future labs: le2vma (in vmm.h), le2proc (in proc.h), etc).

memlayout.h中找到Page的定义:

struct Page {
   
    int ref;                        // 映射此物理页的虚拟页个数(该页被引用的次数)
    uint32_t flags;                 // 物理页的标志
    unsigned int property;          // 用于记录first fit pm manager的空闲块数
  																	//(只在地址最低页有值)
    list_entry_t page_link;         // 双向链接各个page的双向链表
};

le2page的定义:

// 将列表转换成页
#define le2page(le, member)                 \
    to_struct((le), struct Page, member)
First-Fit(首次适应算法)

​ 首次适应算法从空闲分区表的第一个表目起查找该表,把最先能够满足要求的空闲区分配给作业,这种方法目的在于减少查找时间。

该算法倾向于优先利用内存中低址部分的空闲分区,从而保留了高址部分的大空闲区,这为以后到达的大作业分配大的内存空间创造了条件。

缺点是会造成外部碎片

1. default_init()
static void
default_init(void) {
   
    list_init(&free_list);
    nr_free = 0;
}

nr_freefree_area_t的结构体中被定义,用于表示空闲页的数量

/* free_area_t - 维护一个双向链表来记录空闲(未使用的)页*/
typedef struct {
   
    list_entry_t free_list;         // 链表头部
    unsigned int nr_free;           // 表示空闲页的数量
} free_area_t;

这个函数可以直接使用。


2. default_init_memmap()

注释中有这一部分提示:

CALL GRAPH: kern_init --> pmm_init --> page_init --> init_memmap -->pmm_manager --> init_memmap.

This function is used to initialize a free block (with parameter addr_base, page_number). In order to initialize a free block, firstly, you should initialize each page (defined in memlayout.h) in this free block. This procedure includes:

  • Setting the bit PG_property of p->flags, which means this page is valid.

    P.S: In function pmm_init (in pmm.c), the bit PG_reserved of p->flags is already set.

  • If this page is free and is not the first page of a free block, p->property should be set to 0.

  • If this page is free and is the first page of a free block, p->property should be set to be the total number of pages in the block.

  • p->ref should be 0, because now p is free and has no reference.

  • After that, We can use p->page_link to link this page into free_list.

    (e.g. : list_add_before(&free_list, &(p->page_link)); )

  • Finally, we should update the sum of the free memory blocks: nr_free += n.

找到相关的一些函数和定义:

/* Flags describing the status of a page frame */
#define PG_reserved                 0       
// if this bit=1: the Page is reserved for kernel, cannot be used in alloc/free_pages; otherwise, this bit=0 
//如果为1,这一页为内核保留,否则为0
#define PG_property                 1       
// if this bit=1: the Page is the head page of a free memory block(contains some continuous_addrress pages), and can be used in alloc_pages; if this bit=0: if the Page is the the head page of a free memory block, then this Page and the memory block is alloced. Or this Page isn't the head page.

#define PageReserved(page)          test_bit(PG_reserved, &((page)->flags))
//检查是否为保留页
#define SetPageProperty(page)       set_bit(PG_property, &((page)->flags)))
//设置为保留页
static inline void
list_add_before(list_entry_t *listelm, list_entry_t *elm) {
   
    __list_add(elm, listelm->prev, listelm);
}
static inline void
__list_add(list_entry_t *elm, list_entry_t *prev, list_entry_t *next) {
   
    prev->next = next->prev = elm;
    elm->next = next;
    elm->prev = prev;
}
static inline void
set_page_ref(struct Page *page, int val) {
   
    page->ref = val;
}//在pmm.h中

default_init_memmap()由:

static void
default_init_memmap(struct Page *base, size_t n) {
   
    assert(n > 0);
    struct Page *p = base;
    for (; p != base + n; p ++) {
   
        assert(PageReserved(p));
        p->flags = p->property = 0;
        set_page_ref(p, 0);
    }
    base->property = n;
    SetPageProperty(base);
    nr_free += n;
    list_add(&free_list, &(base->page_link));
}

pmm.c中的注释:

set_page_ref(page,1) : means the page be referenced by one time

可以知道set_page_ref(p,0)的作用是清除引用此页的虚拟页的个数。

assert()函数的作用:

assert的作用是先计算表达式 expression ,如果其值为假(即为0),那么它先向stderr打印一条出错信息,然后通过调用 abort 来终止程序运行。

改写为:

static void
default_init_memmap(struct Page *base, size_t n) {
   
    assert(n > 0);   //使用assert宏,当为假时中止程序
    struct Page *p = base;//声明一个base的Page,随后生成起始地址为base的n个连续页
    for (; p != base + n; p ++) {
    //初始化n块物理页
        assert(PageReserved(p)); //确保此页不是保留页,如果是,中止程序
        p->flags = p->property= 0; //标志位置为0
        SetPageProperty(p);       //设置标志位为1
        set_page_ref(p, 0);  
        list_add_before(&free_list, &(p->page_link)); //加入空闲链表
    }
    nr_free += n;  //空闲页总数置为n
    base->property = n; //修改base的连续空页值为n
}

相对于改写前的函数,SetPageProperty()list_add_before()这两个函数被移动到循环里,并对每个页都进行修改,因为生成以base为起始地址的连续空页时,后续的连续空页要被设为保留页然后链接成一个双向链表。

这一个函数是传入base页的地址和生成物理页的个数n,然后把物理页初始化后设为保留页与base页连接。并将修改base页的property为n,修改nr_free为n,记录空闲页的个数。


3. default_alloc_pages()

注释:

default_alloc_pages:
Search for the first free block (block size >= n) in the free list and reszie the block found, returning the address of this block as the address required by malloc.

  1. So you should search the free list like this:
    list_entry_t le = &free_list;
    while((le=list_next(le)) != &free_list) {

    (4.1.1)
    In the while loop, get the struct page and check if p->property
    (recording the num of free pages in this block) >= n.
    struct Page p = le2page(le, page_link);
    if(p->property >= n){ …
    (4.1.2)
    If we find this p, it means we’ve found a free block with its size
    >= n, whose first n pages can be malloced. Some flag bits of this page
    should be set as the following: PG_reserved = 1, PG_property = 0.
    Then, unlink the pages from free_list.
    (4.1.2.1)
    If p->property > n, we should re-calculate number of the rest
    pages of this free block. (e.g.: le2page(le,page_link))->property = p->property - n;)
    (4.1.3)
    Re-caluclate nr_free (number of the the rest of all free block).
    (4.1.4)
    return p
  2. If we can not find a free block with its size >=n, then return NULL.

default_alloc_pages()函数:

static struct Page *
default_alloc_pages(size_t n) {
   
    assert(n > 0);
    if (n > nr_free) {
   
        return NULL;
    }
    struct Page *page = NULL;
    list_entry_t *le = &free_list;
    while ((le = list_next(le)) != &free_list) {
   
        struct Page *p = le2page(le, page_link);
        if (p->property >= n) {
   
            page = p;
            break;
        }
    }
    if (page != NULL) {
   
        list_del(&(page->page_link));
        if (page->property > n) {
   
            struct Page *p = page + n;
            p->property = page->property - n;
            list_add(&free_list, &(p->page_link));
    }
        nr_free -= n;
        ClearPageProperty(page);
    }
    return page;
}

注释和原函数中使用的定义:

// convert list entry to page
#define le2page(le, member)                 \
    to_struct((le), struct Page, member)
  
#define ClearPageReserved(page)     clear_bit(PG_reserved, &((page)->flags))

to_struct()函数:

/* *
 * to_struct - get the struct from a ptr
 * @ptr:    a struct pointer of member
 * @type:   the type of the struct this is embedded in
 * @member: the name of the member within the struct
 * */
#define to_struct(ptr, type, member)                               \
    ((type *)((char *)(ptr) - offsetof(type, member)))

offsetof()函数:

/* Return the offset of 'member' relative to the beginning of a struct type */
#define offsetof(type, member)                                      \
    ((size_t)(&((type *)0)->member))

改写后的default_alloc_pages()

static struct Page *
default_alloc_pages(size_t n) {
   
    assert(n > 0); 
    if (n > nr_free) {
    //如果需要分配的页少于空闲页的总数,返回NULL
        return NULL;
    }
    list_entry_t *le, *len; //声明一个空闲链表的头部和长度
    le = &free_list;  //空闲链表的头部

    while((le=list_next(le)) != &free_list) {
   //遍历整个链表
      struct Page *p = le2page(le, page_link); //转换为页
      if(p->property >= n){
   //找到页(whose first `n` pages can be malloced)
        int i;
        for(i=0;i<n;i++){
   //对前n页进行操作
          len = list_next(le); 
          struct Page *pp = le2page(le, page_link); //转换为页
          SetPageReserved(pp); //PG_reserved = '1'
          ClearPageProperty(pp);//PG_property = '0'
          list_del(le); //将此页从free_list中清除
          le = len;
        }
        if(p->property>n){
    //如果页块大小大于所需大小,分割页块
          (le2page(le,page_link))->property = p->property-n;
        }
        ClearPageProperty(p);
        SetPageReserved(p);
        nr_free -= n; //减去已经分配的页块大小
        return p;
      }
    }
    return NULL;
}

这个函数用于分配空闲页,根据注释的说明,先判断p->property的大小,如果是>=n,则对标志位进行一些修改,如果是>n,则要分割页块后进行操作,如果都不符合则返回NULL退出。(根据注释一步一步写即可,注释讲的很详细。)


4. default_free_pages()

default_free_pages:

​ re-link the pages into the free list, and may merge small free blocks into the big ones.
(5.1)
​ According to the base address of the withdrawed blocks, search the free list for its correct position (with address from low to high), and insert the pages. (May use list_next, le2page, list_add_before)
(5.2)
​ Reset the fields of the pages, such as p->ref and p->flags (PageProperty)
(5.3)
​ Try to merge blocks at lower or higher addresses. Notice: This should
change some pages’ p->property correctly.

default_free_pages()函数:

default_free_pages(struct Page *base, size_t n) {
   
    assert(n > 0);
    struct Page *p = base;
    for (; p != base + n; p ++) {
   
        assert(!PageReserved(p) && !PageProperty(p));
        p->flags = 0;
        set_page_ref(p, 0);
    }
    base->property = n;
    SetPageProperty(base);
    list_entry_t *le = list_next(&free_list);
    while (le != &free_list) {
   
        p = le2page(le, page_link);
        le = list_next(le);
        if (base + base->property == p) {
   
            base->property += p->property;
            ClearPageProperty(p);
            list_del(&(p->page_link));
        }
        else if (p + p->property == base) {
   
            p->property += base->property;
            ClearPageProperty(base);
            base = p;
            list_del(&(p->page_link));
        }
    }
    nr_free += n;
    list_add(&free_list, &(base->page_link));
}

修改后:

static void
default_free_pages(struct Page *base, size_t n) {
   
    assert(n > 0);  
    assert(PageReserved(base));    //检查需要释放的页块是否已经被分配
    list_entry_t *le = &free_list; 
    struct Page * p;
    while((le=list_next(le)) != &free_list) {
   //找到释放的位置
      p = le2page(le, page_link);
      if(p>base){
       
        break;
      }
    }
    for(p=base;p<base+n;p++){
                 
      list_add_before(le, &(p->page_link)); //在这个位置开始,插入释放数量的空页
    }
    base->flags = 0;         //修改标志位
    set_page_ref(base, 0);   //修改引用次数为0 
    ClearPageProperty(base);
    SetPageProperty(base);
    base->property = n;
  • 13
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值