• xv6源码解析(三)——内存管理


    01 内存管理

    内存管理:通过编写物理页分配器,以链表管理所有空闲页帧, 实现了对物理页帧的回收与分配;在xv6系统sbrk内存管理方式的基础上,添加了进程用户空间非连续分区的分配。

    image-20221106094833920

    image-20221106094745101

    内存管理参考链接

    mmap

    02 sbrk机制

    描述:
    brk()和sbrk()改变程序间断点的位置。程序间断点就是程序数据段的结尾。(程序间断点是为初始化数据段的起始位置).通过增加程序间断点进程可以更有效的申请内存 。当addr参数合理、系统有足够的内存并且不超过最大值时brk()函数将数据段结尾设置为addr,即间断点设置为addr。sbrk()将程序数据空间增加increment字节。当increment为0时则返回程序间断点的当前位置。

    返回值:
    brk()成功返回0,失败返回-1并且设置errno值为ENOMEM(注:在mmap中会提到)。
    sbrk()成功返回之前的程序间断点地址。如果间断点值增加,那么这个指针(指的是返回的之前的间断点地址)是指向分配的新的内存的首地址。如果出错失败,就返回一个指针并设置errno全局变量的值为ENOMEM。

    总结:
    这两个函数都用来改变 “program break” (程序间断点)的位置,改变数据段长度(Change data segment size),实现虚拟内存到物理内存的映射。
    brk()函数直接修改有效访问范围的末尾地址实现分配与回收。sbrk()参数函数中:当increment为正值时,间断点位置向后移动increment字节。同时返回移动之前的位置,相当于分配内存。当increment为负值时,位置向前移动increment字节,相当与于释放内存,其返回值没有实际意义。当increment为0时,不移动位置只返回当前位置。参数increment的符号决定了是分配还是回收内存。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nUPoIDFQ-1668171558979)(https://image-1312312327.cos.ap-shanghai.myqcloud.com/20170116174212008)]

    03 myalloc/myfree机制

    xv6内存管理方法:sbrk机制

    在学习完xv6的进程分配和释放内存的方式后,发现xv6只允许扩展和收缩进程空间,也就是采用了linux中提供的sbrk机制。

    • sys_sbrk()将进一步调用growproc(n)进行内存空间的调整 , proc.c中的growproc(n)底层实现原理就是根据传入参数n的正负进行扩展和收缩内存
    • kalloc()会分配一个物理页帧,kfree()会释放一个物理页帧,mappages()用于建立虚存地址和物理页帧之间的关系
    • 如何实现Linux的alloc()和free()

    myalloc实现

    进行myalloc分配内存的时候

    (1)需要查找合适的地址范围,并创建vma进行描述

    在sbrk调用的基础上,找到size,然后myalloc传入的参数n就是要分配内存空间的大小,继续往上的vm空间查找合适的地址范围

    (2)要用kalloc分配足够的物理页帧,并用mappages()将这些页帧映射到指定的虚存地址上

    找到对应的地址范围后,分配物理页帧,同时进行页表映射,把物理页帧映射到虚存地址上去

    int 
    mygrowproc(int n){                 // 实现首次最佳适应算法
    	struct vma *vm = proc->vm;     // 遍历寻找合适的空间
    	int start = proc->sz;          // 寻找合适的分配起点
    	int index;
    	int prev = 0;
    	int i;
    
    	for(index = vm[0].next; index != 0; index = vm[index].next){
    		if(start + n < vm[index].address)
    			break;
    		start = vm[index].address + vm[index].length;
    		prev = index;
    	}
    	
    	for(i = 1; i < 10; i++) {            // 寻找一块没有用的 vma 记录新的内存块
    		if(vm[i].next == -1){
    			vm[i].next = index;			
    			vm[i].address = start;
    			vm[i].length = n;
    
    			vm[prev].next = i;
    			
    			myallocuvm(proc->pgdir, start, start + n);
    			switchuvm(proc);
    			return start;   // 返回分配的地址
    		}
    	}
    	switchuvm(proc);
    	return 0;
    }
    
    int 
    myallocuvm(pde_t *pgdir, uint start, uint end) {
    	char* mem;
    	uint a;
    
    	a = PGROUNDUP(start);
    	for(; a < end; a += PGSIZE) {
    		mem = kalloc();
    		memset(mem, 0, PGSIZE);
        mappages(pgdir, (char*)a, PGSIZE, V2P(mem), PTE_W|PTE_U);
    	}
    	return (end-start);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    myfree实现

    进行myfree释放内存的时候

    (1)需要释放所涉及的页帧,逐个解除页表映射

    (2)删除vma

    int
    myreduceproc(int address){  // 释放 address 开头的内存块
    	int prev = 0;
    	int index;
    	for(index = proc->vm[0].next; index != 0; index = proc->vm[index].next) {
    		if(proc->vm[index].address == address && proc->vm[index].length > 0) {
    			mydeallocuvm(proc->pgdir, proc->vm[index].address, proc->vm[index].address + proc->vm[index].length);			
    			proc->vm[prev].next = proc->vm[index].next;
    			proc->vm[index].next = -1;
    			proc->vm[index].length = 0;
    			break;
    		}
    		prev = index;
    	}
    	switchuvm(proc);
    	return 0;
    }
    
    int
    mydeallocuvm(pde_t *pgdir, uint start, uint end) {
    	pte_t *pte;
    	uint a, pa;
    
    	a = PGROUNDUP(start);
    	for(; a < end; a += PGSIZE) {
    		pte = walkpgdir(pgdir, (char*)a, 0);
    		if(!pte)
    			a += (NPTENTRIES - 1) * PGSIZE;
    		else if((*pte & PTE_P) != 0){
    			pa = PTE_ADDR(*pte);
    			if(pa == 0)
    				panic("kfree");
    			char *v = P2V(pa);
    			kfree(v);
    			*pte = 0;
    		}
    	}
    	return 1;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    myalloc测试代码

    myalloc => myalloc、myfree => mygrowproc、myreduceproc => myallocuvm、mydeallocuvm

    #include "types.h"
    #include "stat.h"
    #include "user.h"
    
    
    int
    main(int argc, char *argv[]) {
      // int pid = getpid();   
      // map(pid);
      
      char* m1 = (char*)myalloc(2 * 4096);
      char* m2 = (char*)myalloc(3 * 4096);
      char* m3 = (char*)myalloc(1 * 4096);
      char* m4 = (char*)myalloc(7 * 4096);
      char* m5 = (char*)myalloc(9 * 4096);
    
      m1[0] = 'h';
      m1[1] = '\0';
    
    
      printf(1,"m1:%s\n",m1);
      myfree(m2);
    
      //m2[1] = 'p';
    
      myfree(m4);
      
      // map(pid);
      sleep(5000);
      myfree(m1);
      myfree(m3);
      myfree(m5);
      // char *p=(char *)0x0000;
      // for(int i=0x0000;i<0x08;i++)
      //     *(p+i)='*';
    
      // printf(1,"This string shouldn't be modified!\n");
      // exit();
    
    
      exit();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
  • 相关阅读:
    向毕业妥协系列之机器学习笔记:构建ML系统(一)
    SpringMVC工作原理
    入门安全测试?从渗透测试开始做起....
    Linux:文件搜索
    ZKP3.2 Programming ZKPs (Arkworks & Zokrates)
    【Go语言】切片的扩容
    Apache commons email邮件工具类简介及使用说明
    【电源专题】什么是AC/DC转换器
    Redisson源码解读-分布式锁
    排序算法-冒泡排序
  • 原文地址:https://blog.csdn.net/qq_41945053/article/details/127813100