返回顶部

BUAA OS lab的一些宏定义

mmu.h:
/*
 o     4G ----------->  +----------------------------+------------0x100000000
 o                      |       ...                  |  kseg3
 o                      +----------------------------+------------0xe000 0000
 o                      |       ...                  |  kseg2
 o                      +----------------------------+------------0xc000 0000
 o                      |   Interrupts & Exception   |  kseg1
 o                      +----------------------------+------------0xa000 0000
 o                      |      Invalid memory        |   /|\
 o                      +----------------------------+----|-------Physics Memory Max
 o                      |       ...                  |  kseg0
 o  VPT,KSTACKTOP-----> +----------------------------+----|-------0x8040 0000-------end
 o                      |       Kernel Stack         |    | KSTKSIZE            /|\
 o                      +----------------------------+----|------                |
 o                      |       Kernel Text          |    |                    PDMAP
 o      KERNBASE -----> +----------------------------+----|-------0x8001 0000    | 
 o                      |   Interrupts & Exception   |   \|/                    \|/
 o      ULIM     -----> +----------------------------+------------0x8000 0000-------    
 o                      |         User VPT           |     PDMAP                /|\ 
 o      UVPT     -----> +----------------------------+------------0x7fc0 0000    |
 o                      |         PAGES              |     PDMAP                 |
 o      UPAGES   -----> +----------------------------+------------0x7f80 0000    |
 o                      |         ENVS               |     PDMAP                 |
 o  UTOP,UENVS   -----> +----------------------------+------------0x7f40 0000    |
 o  UXSTACKTOP -/       |     user exception stack   |     BY2PG                 |
 o                      +----------------------------+------------0x7f3f f000    |
 o                      |       Invalid memory       |     BY2PG                 |
 o      USTACKTOP ----> +----------------------------+------------0x7f3f e000    |
 o                      |     normal user stack      |     BY2PG                 |
 o                      +----------------------------+------------0x7f3f d000    |
 a                      |                            |                           |
 a                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~                           |
 a                      .                            .                           |
 a                      .                            .                         kuseg
 a                      .                            .                           |
 a                      |~~~~~~~~~~~~~~~~~~~~~~~~~~~~|                           |
 a                      |                            |                           |
 o       UTEXT   -----> +----------------------------+                           |
 o                      |                            |     2 * PDMAP            \|/
 a     0 ------------>  +----------------------------+ -----------------------------
 o
*/

宏定义:
	BY2PG 一页大小
	PDMAP 4M 由页目录条目映射的字节
	PGSHIFT、PDSHIFT 12偏移量
	PDX(va)	页目录偏移量31-22 位
	PTX(va) 页表偏移量21-12
	PTE_ADDR(pte) 将低12位清零,虚实地址转换时用
	PPN(va) 右移12位,可以获得第多少页
	VPN(va) 和PPN(va)相同
	PTE_G 0x0100全局权限
	PTE_V 0x0200有效位权限,如果&PTE_V为0,则说明无效
	PTE_R 0x0400写权限
	PADDR(kva) 虚到实地址
	KADDR(pa) 实到虚地址
错误:
	E_NO_MEM  4
地址空间:
	VPT,KSTACKTOP	0x80400000
	ULIM 0x80000000	//从0x80000000开始逐渐往下减
	UVPT (ULIM - PDMAP)	0x7fc0 0000
	UPAGES (UVPT - PDMAP)	0x7f80 0000
	UTOP UENVS UXSTACKTOP (UPAGES - PDMAP) 0x7f40 0000
	USTACKTOP (UTOP - 2*BY2PG)	0x7f3f e000是用户栈,内核栈在0x8040 0000)
数据类型:
	Pde u_long类型,页目录地址
	Pte u_long类型,页表地址
	npage u_long类型,页个数
	
types.h:
	ROUND(a,n) 常用ROUND(a, BY2PG)使其页对其
	ROUNDDOWN(a,n) (((u_long)(a)~((n)-1))
pmap.h:
数据类型:
	pages struct Page*类型
函数:
	page2ppn(struct Page *pp) 返回pp - pages(u_long),表示页偏移
	page2pa(struct Page *pp) 返回u_long类型,该页的物理地址
	pa2page(u_long pa) 返回struct Page *,该物理地址的页
	page2kva(struct Page *pp) 返回u_long类型,该页的虚拟地址
	va2pa(Pde *pgdir, u_long va) 返回u_long类型,虚拟地址va通过页表对应的物理地址
queue.h
宏函数
    LIST_EMPTY(head) 	判断链表是否为空
    LIST_FIRST(head)	获取链表的表头
    LIST_FOREACH(var, head, field)	for循环遍历链表
    LIST_INIT(head)	初始化链表表头为NULL
    LIST_INSERT_AFTER(listelm, elm, field)	在listelm后面插入elm
    LIST_INSERT_BEFORE(listelm, elm, field)	在listelm前面插入elm
    LIST_INSERT_HEAD(head, elm, field)	头插法插入elm
    LIST_INSERT_TAIL(head, elm, field)	尾插法插入elm
    LIST_NEXT(elm, field)	elm 的next指针
    LIST_REMOVE(elm, field)	删除链表中的elm
init.c:
	void bcopy(const void *src, void *dst, size_t len)//src内容复制到dst上
	void bzero(void *b, size_t len)
pmap.c:
    函数:
    mips_detect_memory:初始化maxpa、basemem、npage

    void *alloc(u_int n, u_int align, int clear):分配 n 字节的空间,同时保证 align 可以 整除初始虚拟地址,若 clear 为真则将对应空间的值清零,否则不清零。返回值为分配空间的初始虚拟地址,freemem为未使用的虚拟地址
    mips_vm_init:分配一级页表空间,链式指针空间,进程空间

    page_init:页空间分配,即可用页用链表连起来
    int page_alloc(struct Page **pp):从链表头部分配空闲空间
    void page_free(struct Page *pp):判断 pp 指向内存控制块对应的物理页面引用次数是否为 0,是则把该页回链表
    page_decref(struct Page *pp),它的作用是令 pp 对应内存控制块的引用次数减少 1,如果引用次数为 0 则会调用下面的 page_free 函数将对应物理页面重新设置为空闲页面。

    Pte *boot_pgdir_walk(Pde *pgdir, u_long va, int create):返回一级页表基地址 pgdir 对应的两级页表结构中,va 这个虚拟地址所在的二级页表项,如果 create 不为 0 且对应的二级页表不存在则会使用 alloc 函数分配一页物理内存用于存放。这里之所以使用 alloc 而不用 page_alloc 是因为这个函数是在内核启动过程中使用的,此时还没有建立好物理内存管理机制。

    void boot_map_segment(Pde *pgdir, u_long va, u_long size, u_long pa, int perm)将一级页表基地址 pgdir 对应的两级页表结构做区间地址映射,将虚拟地址区间 [va, va + size − 1] 映射到物理地址区间 [pa, pa + size − 1]

    int pgdir_walk(Pde *pgdir, u_long va, int create, Pte **ppte)如果空间不够允许失败,因此将返回值变为 int,若为 0 代表执行成功否则为一个失败码,同时将原本的返回值 Pte * 放到 ppte 所指的空间上。此外,因为该函数的调用在启动之后,create 不为 0 且对应的二级页表不存在时,它使用 page_alloc 而不使用 alloc 进行物理页面的分配。

    int page_insert(Pde *pgdir, struct Page *pp, u_long va, u_int perm),这个函数的作用是将一级页表基地址 pgdir 对应的两级页表结构中 va 这一虚拟地址映射到内存控制块 pp 对应的物理页面,并将页表项权限为设置为 perm。

    struct Page * page_lookup(Pde *pgdir, u_long va, Pte **ppte),它的作用是返回一级页表基地址 pgdir 对应的两级页表结构中 va 这一虚拟地址映射对应的物理页面对应的内存控制块,同时将 ppte 指向的空间设为对应的二级页表项地址。 

    void page_remove(Pde *pgdir, u_long va),它的作用是删除一级页表基地址 pgdir 对应的两级页表结构中 va 这一虚拟地址对物理地址的映射,如果存在这样的映射,那么对应物理页面的引用次数会减少。

    void pageout(int va, int context),一级页表基地址 context 对应的两级页表结构中 va 新增这一虚拟地址的映射,对应的物理页面通过函数 page_alloc 获取而不特殊指定,这对应其「被动」的性质。该函数的具体调用流程将在下一部分进行介绍。

image-20220427161005745

e_phoff:此字段指明程序头表(program header table)开始处在文件中的偏移量。如果没
有程序头表,该值应设为 0。
e_phnum:此字段表明程序头表中总共有多少个表项。如果一个目标文件中没有程序头
表,该值应设为 0
e_phentsize:此字段表明在程序头表中每一个表项的大小,以字节为单位

image-20220427161400835

p_offset:此数据成员给出本段内容在文件中的位置,即段内容的开始位置相对于文件开头的偏移量。
p_vaddr:此数据成员给出本段内容的开始位置在进程空间中的虚拟地址。
p_filesz:此数据成员给出本段内容在文件中的大小,单位是字节,可以是 0。
p_memsz:此数据成员给出本段内容在内容镜像中的大小,单位是字节,可以是 0。
初始化:mips_vm_init()/env_init
创建进程:|-env_alloc()-env_set_vm()-page_alloc()
		|-load_icode()|-page_alloc()
					  |-load_elf()-load_icode_mapper()

trap.h:
	struct Trapframe {
	unsigned long regs[32];		// 32 个通用寄存器
	unsigned long cp0_status;	// CP0 状态寄存器
	unsigned long hi;		// 乘(除)法高位(模)寄存器
	unsigned long lo;		// 乘(除)法低位(商)寄存器
	unsigned long cp0_badvaddr;		// 异常发生地址
	unsigned long cp0_cause;	// CP0 cause 寄存器
	unsigned long cp0_epc;		// 异常返回地址
	unsigned long pc;		// PC计数器,程序运行的地址
};

kernel_elfloader.c
	int is_elf_format(u_char *binary)
	int load_elf(u_char *binary, int size, u_long *entry_point, void *user_data,int (*map)(u_long va, u_int32_t sgsize,u_char *bin, u_int32_t bin_size, void *user_data))
	ptr_ph_table = binary + ehdr->e_phoff;
    ph_entry_count = ehdr->e_phnum;
    ph_entry_size = ehdr->e_phentsize;
	while (ph_entry_count--) {//程序头数量
		phdr = (Elf32_Phdr *)ptr_ph_table;//程序头首地址
		if (phdr->p_type == PT_LOAD) {
		r = map(phdr->p_vaddr, phdr->p_memsz,binary + phdr->p_offset, phdr->p_filesz, user_data);//映射:程序虚拟地址、内存中大小、文件首地址、文件内容大小
        if(r < 0) return r;
		}
        ptr_ph_table += ph_entry_size;//程序头首地址+每个程序头大小=下一个程序头首地址
}

env.c	
	envs 进程结构体指针
	env_free_list 头部,是一个结构体,表示空闲进程块
	env_sched_list[2] 头部,是一个结构体,表示可运行进程块
	
	int load_icode_mapper(u_long va, u_int32_t sgsize,u_char *bin, u_int32_t bin_size, void *user_data)//将各个segment加载到内存中
	va(该段需要被加载到的虚地址)、sgsize(该段在内存中的大小)、bin(该段在ELF文件中的内容,即是我们需要加载的段的起始地址)、bin_size(该段在文件中的大小)。user_data就是当前进程。
	int page_insert(Pde *pgdir, struct Page *pp, u_long va, u_int perm),这个函数的作用是将一级页表基地址 pgdir 对应的两级页表结构中 va 这一虚拟地址映射到内存控制块 pp 对应的物理页面,并将页表项权限为设置为 perm。
	struct Env *env = (struct Env *)user_data;
	bcopy((void *)bin, (void *)(page2kva(p) + offset), MIN(bin_size, size));
	bcopy((void * )(bin + i), (void *)page2kva(p), MIN(bin_size - i, BY2PG)); //如果是bin_size的最后一页,只要复制到bin截止的内容
	
	void load_icode(struct Env *e, u_char *binary, u_int size)
	申请的页面来初始化一个进程的栈page_insert(e->env_pgdir,p,USTACKTOP-BY2PG, perm);(注意栈的增长方向是向下的)
	调用load_elf函数把二进制文件加载到内存
	设置pc寄存器
	我们要运行的进程的代码段预先被载入到了entry_ point为起点的内存中(也就是进程入口地址,运行进程时,CPU将自动从pc所指的位置开始执行二进制码, 因此将pc设为entry_point。e->env_tf.pc = entry_point;
	
	u_int mkenvid(struct Env *e) //生成一个新的进程ID
		u_int asid_alloc()//生成一个新的ASID
	新的进程ID组成:envid:31-11位asid,10位1,0-9位进程控制块序号(第几个)
		其中:asid实际为6位,进程控制块序号为e-envs
		
	int envid2env(u_int envid, struct Env **penv, int checkperm);
		//通过envid获取该进程控制块,如果checkperm,则它必须是当前进程或者当前进程的子进程
		//当envid是0时,该函数返回当前进程
	
	void env_init(void);//将链表更新,所有进程控制块放到env_free_list中(小地址在头部)
	int env_setup_vm(struct Env *e)//初始化新进程地址空间。也即是初始化该进程的页目录。
	int env_alloc(struct Env **e, u_int parent_id);
		首先,从空闲PCB链表中取出一个空闲PCB。//LIST_FIRST
		调用env_setup_vm函数初始化进程的页目录(pgdir和env_cr3)。
		为该进程分配一个envid, 设置父进程id, 并且把进程的状态设为ENV_RUNNABLE 。
		设置相应的寄存器的值。//cp0_status = 0x10001004;regs[29]=USTACKTOP;(栈寄存器指向用户栈)
		该PCB从空闲链表中移除。//LIST_REMOVE
		
	void env_free(struct Env *);//释放该进程
	void env_create_priority(u_char *binary, int size, int priority);
	void env_create(u_char *binary, int size);
	void env_destroy(struct Env *e);
	void env_run(struct Env *e);
	保存当前进程的上下文信息,设置当前进程上下文中的 pc 为epc。
	//如果当前有进程,则保存当前进程的env_tf、env_tf.pc
	把当前进程curenv切换为需要运行的进程。//curenv = e;
	调用 lcontext 函数,设置全局变量mCONTEXT为当前进程页目录地址,这个值将在TLB重填时用到。context((u_int)curenv->env_pgdir);
	调用env_pop_tf函数,恢复现场、异常返回。
	
env.h
宏定义:
	LOG2NENV	10
	NENV (1<<LOG2NENV)进程控制块个数
    ENV_FREE	0
    ENV_RUNNABLE	1处于RUNNABLE 状态的进程可以是正在运行的,也可能不在运行中。
    ENV_NOT_RUNNABLE	2
	ENVX(envid)	 ((envid) & (NENV - 1))获取0-9位,即第多少个进程块
	GET_ENV_ASID(envid) (((envid)>> 11)<<6) //获取改进程块的ASID
	#define ENV_CREATE_PRIORITY(x, y) \
{ \
    extern u_char binary_##x##_start[];\
    extern u_int binary_##x##_size; \
    env_create_priority(binary_##x##_start, \
    (u_int)binary_##x##_size, y); \
}
#define ENV_CREATE(x) \
{ \
    extern u_char binary_##x##_start[];\
    extern u_int binary_##x##_size; \
    env_create(binary_##x##_start, \
    (u_int)binary_##x##_size); \
}
全局变量:
	 struct Env {
     struct Trapframe env_tf;       // Saved registers 
     LIST_ENTRY(Env) env_link;      // Free LIST_ENTRY 
     u_int env_id;                  // Unique environment identifier 
     u_int env_parent_id;           // env_id of this env's parent 
     u_int env_status;              // Status of the environment 
     Pde *env_pgdir;            // Kernel virtual address of page dir 
     u_int env_cr3; 
     LIST_ENTRY(Env) env_sched_link; 
     u_int env_pri;
};
	struct Env *curenv = NULL; // 当前进程控制块
	struct Env *envs;	//进程链表头部
	struct Env_list env_sched_list[2];//可运行进程链表
hit:
	envs指针为进程控制块头部,envs[i]第i个进程控制块,若struct env*p;如果是p = &envs[i],则代表p指针指向了该控制块,如果是*p = envs[i],则代表p指针指向的地址的内容为该控制块(指向的地址不知道)
	void ttt(struct a** p,struct a z) {
	*p = &z;}
	struct a * temp;
	ttt(&temp,p[1]);
参数为双指针时,表示该双指针指向了指针的地址。*p表示该指针,且与temp一样,当给*p赋值的时候,就是给temp赋值
posted @ 2022-04-26 14:23  wpy的小黑屋  阅读(71)  评论(0编辑  收藏  举报