ucore lab2

关于ucore究竟是什么的问题,在ucore1 当中已经介绍过了.这次就直接介入正题吧.

想了想还是想说一点题外话,写完ucore1之后,我把编辑器从sublime3换成了atom.只能说github新出的这个编辑器实在是卡爆了,而且在linux下各种奇怪的情况下报错不断.不过一它是免费的,而且还在测试阶段可以想象会越来越好,二是它安装插件的方式比sublime不知道友好了多少.而且该有的插件都有,咬咬牙还是继续用了.


前段时间室友给我声情并茂念了一句"高端程序员展示自己的代码,低端程序员展示自己的工具".作为天天在各种工具上纠缠不休的我只好默默蹲墙角了.


好吧.现在真的切入正题.


lab2开始前需要的一点准备工作是,把lab1的代码给merge进来.我倒是知道git不同的分支该怎么merge,但怎么从一个文件夹merge到另外一个文件夹,那是毫无概念.搜索一番,找到一个linux叫做meld的工具.gui的,相当好用,点点点就把代码挪过来了.(当然,总共也没多少要改的,不过总归比复制粘贴要舒服,也不容易弄错)


1.first-fit连续内存分配

要做的事情就是将链表当中的page分配出去.说简单也简单.一开始看着代码茫然无措,因为照着注释的要求,它似乎把什么都写好了,提示可能修改的那几个函数已经有模有样放在那里了,完全看不出修改的必要.

make qemu发现,在某个check函数当中发生了assertion failed.查看之,发现它是按照一定规则来检查分配的内存页是否合法合理.然后一开始投机取巧比较严重,我发现在default_free_pages函数中,把list_add改成list_add_before就能完美通过check.

一直到快到ddl了我都以为这样写是没什么问题的,直到有人提醒我,题目的要求是把页排序放入链表.我说它check的内容怎么这么奇怪.只好对整个程序都进行大改.

init_memmap和alloc_pages这两个函数还好,在其基础上一点小改就可以.free_pages这个函数,如果在它的基础上改,简直就是给自己制造麻烦加大工作量.由于一开始的一些失误,加上参考了那个极没有参考价值的答案,我几乎把原来的代码都删光了,后来全都是手动补上的.里面可能有一些问题,然而我暂时也不想去管它们了.


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= 0;
       p->property = 0;
       ClearPageProperty(p);
       set_page_ref(p, 0);
    }
    SetPageProperty(base);
    base->property = n;
    list_add_before(&free_list,&(base->page_link));
    nr_free += n;
}

<span style="font-size:18px;">static struct Page *
default_alloc_pages(size_t n) {

    assert(n > 0);
    if (n > nr_free) {
        return 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){
            if(p->property>n){
                struct Page *np = p+n;
                np->property = p->property - n;
                SetPageProperty(np);
                list_add(&(p->page_link),&(np->page_link));
            }
            SetPageReserved(p);
            ClearPageProperty(p);
            set_page_ref(p,0);
            list_del(&(p->page_link));
            nr_free -= n;
            return p;
       }
    }
    return NULL;
}</span>
<span style="font-size:18px;">static void
default_free_pages(struct Page *base, size_t n) {
	assert(n > 0);
	base->flags = 0;
	SetPageProperty(base);
	set_page_ref(base,0);
	base->property = n;
	list_entry_t *le = &free_list;
	struct Page * p = NULL;
	while((le=list_next(le)) != &free_list) {p = le2page(le, page_link);
		if(p>base){
		break;
		}
	}
	le = (p>base)? (&(p->page_link)) : (&free_list);
	list_add_before(le,&(base->page_link));
	if (le != &free_list){
		if( base+n == p ){
			base->property += p->property;
			p->property = 0;
			list_del(&(p->page_link));
		}
	}
		le = list_prev(&(base->page_link));
		if (le!= &free_list){
			p = le2page(le,page_link);
			if (p+p->property == base){
				p->property += base->property;
				base->property = 0;
				list_del(&(base->page_link));
			}
		}
	nr_free += n;
	return ;
}</span>

2.查找虚拟地址对应页表项

私以为,比起上一题,这题就简单多了,只要跟着注释走就可以了.前提是你要搞清楚如下几个概念:

虚拟地址,物理地址,线性地址,指向struct Page的指针,Page的物理地址和虚拟地址,PDE,PTE...好吧我觉得可能不止这么些,希望上面这些没有把你看乱,只要能在它们之间灵活转换来转换去,那这道题是毫无压力的.

你不需要知道手动怎么解决,你只要找到那几个定义好的宏就可以了.

pte_t *
get_pte(pde_t *pgdir, uintptr_t la, bool create) {
    pde_t *pdep = &pgdir[PDX(la)];
    if (!( (*pdep) & PTE_P )){
        if (!create) return NULL;
        struct Page * page = alloc_page();
        if (!page) return NULL;
        set_page_ref(page, 1);
        uintptr_t pa = page2pa(page);
        memset(KADDR(pa),0,PGSIZE);

        *pdep = pa | PTE_U | PTE_W | PTE_P;
    }
    return &((pte_t *)KADDR(PDE_ADDR(*pdep)))[PTX(la)];
}


4.释放虚拟地址所在页,并取消对应二级页表映射

这道题比上一道还要简单,关键还是搞清楚上面几个概念的区别,完全是手到擒来.

static inline void
page_remove_pte(pde_t *pgdir, uintptr_t la, pte_t *ptep) {
    if (*ptep & PTE_P){
        struct Page* page = pte2page(*ptep);

        if (page_ref_dec(page)==0){
            free_page(page);
        }
        *ptep = 0;
        tlb_invalidate(pgdir, la);
    }
}


5.challenge Slub算法实现

由于并非要实现一个完整版的slub,因此我的实现只完成了低限度的任意内存分配功能.以下描述其结构:


slub部分:

包含kern/mm/slub.*头文件和实现文件

主要函数为三个:

slub_init负责整个slub系统的初始化

slub_alloc负责分配指定大小的小块内存

slub_free负责内存的回收


主要数据结构为:

struct kmem_cache

每一个对象管理一种固定大小的object的内存分配

init中初始化了从8byte2000byte不等的11种不同大小的kmem_cache管理器.

每一个对象都包含下两个数据结构,用于辅助管理.



struct kmem_cache_cpu

管理当前正在使用的分配页,如果当前分配页还有空间,则从直接从中分配,否则将当前页与kmem_cache_node管理的

未填满的页交换.


struct kmem_cache_node

管理已经填满的页和未填满的页.



通过指定kmem_cache分配和释放内存的流程大致如下:

分配内存:

1)刚刚初始化,kmem_cache_cpukmem_cache_node当中都是空的,则初始化一个新page,作为kmem_cache_cpu

前的分配页.


2)kmem_cache_cpu有分配页且未满,则直接从里面分配空间

3)kmem_cache_cpu的分配页已满,则从kmem_cache_node中调用一个未满的分配页到kmem_cache_cpu当中,将原

分配页加入到kmem_cache_node的已满分配页链表当中.如果kmem_cache_node当中没有未满的分配页,则申请并初

始化一个新页.


释放内存:

1)如果被释放的内存在kmem_cache_cpu的当前页中,则直接释放并加入空闲链表

2)如果在kmem_cache_node管理的未满页中,则同上处理,并且如果该页全空,就释放该页

3)如果在一张已满页中,则释放并加入链表,再将该页从已满页链表中删除,加入未满页链表.


上述过程中,页内部的空闲空间由单向链表维护,未满页和已满页的管理由两个双向链表维护

具体细节详见代码.



kmalloc部分

包含kern/mm/kmalloc.*头文件和实现文件


主要函数有:

kmalloc 根据传入的空间大小参数,如果较大则按页为单位分配,如果较小则在slub.c中初始化的11个不同大小的管理器

中选择足够大的最小的管理器,调用slab_alloc分配内存.


Kfree  同上,根据大小来采取不同方案释放内存.


kmalloc_init   调用slub_init初始化slub系统,并对是否使用slub分配内存的不同情况进行测试.



实现感想:整个过程中其实一直觉得很蛋疼,因为有些需要用的空间和结构,按照平时我会直接malloc一个(或是new一

个),然而放在这里就得绞尽脑汁去想把它们塞在哪里,好在最后还是搞定了.毕竟就是让我们自己实现一个malloc.非常

好的体验.

具体代码比较长(其实也就两百多行),就不贴上来了.



 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值