• 自制操作系统日志——第九天


    自制操作系统日志——第九天

    今天主要的工作内容就是进行内存管理,至于前一天留下的叠加问题,我们放到明天去处理!



    一、整理源文件、内存容量检查

    在正式开始前,我们首先进行整理一下昨天遗留的源文件。使得整体的代码容量看起来更加的简洁:
    我们将之前的鼠标与键盘的代码分别创建对应的c文件:keyboard.c mouse.c 这两个文件。进行分割完以后,再在makefile的OBJS_BOOTPACK 的后面加上keyboard.obj mouse.obj 这两个文件后便可以编译,链接与运行了!!!

    接下来,我来解释一下为什么要做内存容量的管理:
    ⇒ 首先,这是因为如果我们不能很好的规划内存的使用状况的话,可能会使得系统变得一团糟。运行于系统之上的程序将不知道哪里的内存可用,从而导致程序使用的内存出现混乱,也可能会出现多个应用程序使用同一部分的内存,导致程序可能出错。

    因此,我们需要进行有效的内存管理。那么再次之前,我们先查看一下模拟器给我们使用的内存容量大概有多大。

    对了,因为由于486中含有高速缓冲寄存器的存在。因此,简要介绍一下。高速缓存: 主要为了解决cpu与内存中间存在的速度的差异性的问题,cache会存储着访问内存的地址和内容(一般都是即将或者最近要使用的)。cpu每次要访问内存时,都会先将所访问的地址和内容放入cache中,再有cache放入内存。而读数据时,也是先从cache里先读的。

    因此,若我们想通过往内存里写入值来检测内存的可使用大小的话,我们需要先把cache关掉。不然,cache存储着内存的值,会让我们检测是否写入成功的操作变得没有用。(因为写入时值就在cache中,如果没有替换掉,那么继续读内存时候依旧会读取到的)

    检测的整体思路以及bug修改:

    整体的思路就是:

    • 确认cpu的型号;判断是否有高速缓冲寄存器;
    • 关掉高速缓冲寄存器;
    • 开始循环进行检查操作(为加快速度,每次循环4kb,检测最后的两字节)
    • 保存写入的内存地址的旧数据
      -向内存写入数据
    • 写入数据后,为防止机型原因,对内存的数据进行反转后对比,确认写入成功
    • 恢复旧数据

    在这个整体思路的过程中,代码的逻辑上是没什么问题的,但是实际检测过程中却出现了意外(由于我们的模拟器默认可使用的是32mb)但是我们向3g的内存写入时,最终出现结果依旧是3g的。

    在这里插入图片描述
    经过排查,发现出错原因竟然是编译器“过于优秀导致的” 。其原因就是因为觉得我们写入内存的数据进行反转后进行比较(因为如果能写入的话肯定是一样的),然后又反转回去,并吧旧数据还原的操作,根本就没什么意义,但是这种没什么意义的操作却又重复循环好多次!!!很浪费cpu的处理时间,因此,在编译后的汇编里直接把这部分的操作给删去了:
    在这里插入图片描述
    所以,最终这个检查的函数我们还是使用汇编来编写吧:

    bootpack.c:

    void HariMain(void)
    {
    	....
    	s = memtest(0x00400000, 0xbfffffff); //算出内存地址,因为0x00400000之前的地址我们依已经用来做系统启动了肯定是能用的
    	sprintf(s, "memory %dMB  ", s / (1024 * 1024));
    	putfonts8_asc(binfo->vram, binfo->scrnx, 0, 32, COL8_FFFFFF, s);
    	...
    	}
    
    #define EFLAGS_AC_BIT		0x00040000
    #define CR0_CACHE_DISABLE	0x60000000
    
    /*如果是486cpu 则elags寄存器的最高位是AC标志;如果是386则无这个标志,那么将一直是0;
      对cr0寄存器的第31、30位设置为1,因此就用0x600000000进行与,此时可以禁止缓存
      */
    
    unsigned int memtest(unsigned int start, unsigned int end)
    {
    	char flg486 = 0 ;
    	unsigned int eflg, cr0, i;
    	//确认cpu是386还是486,486 ~ 100mhz
    	eflg = io_load_eflags();
    	eflg |= EFLAGS_AC_BIT; /* AC-bit = 1 */
    	io_store_eflags(eflg);
    	eflg = io_load_eflags();
    	if((eflg & EFLAGS_AC_BIT) != 0 )//386 的话即使设定AC等于1,其值还是会自动回到0
    	{
    		flg486 = 1;
    	} 
    	eflg &= ~EFLAGS_AC_BIT; /* AC-bit = 0,~EFLAGS_AC_BIT = 0xfffbffff */
    	io_store_eflags(eflg);
    
    	if( flg486 != 0)
    	{
    		cr0 = load_cr0();
    		cr0 |= CR0_CACHE_DISABLE; //禁止缓存
    		store_cr0(cr0);
    	}
    
    	i = memtest_sub(start, end);
    
    	if( flg486 != 0)
    	{
    		cr0 = load_cr0();
    		cr0 &= ~CR0_CACHE_DISABLE; //允许缓存
    		store_cr0(cr0);
    	}
    
    	return i;
    }
    
    • 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
    • 46
    • 47
    • 48
    • 49
    • 50

    naskfun.nas:

    _load_cr0: ;int load_cro(void)
            mov eax,cr0 
            RET
     _store_cr0: ;void load_cro(int cr0)
            mov EAX,[esp+4]
            mov cr0,EAX
            RET  
    
    _memtest_sub:	; unsigned int memtest_sub(unsigned int start, unsigned int end)
    	PUSH	EDI						; (EBX, ESI, EDI も使いたいので)
    	PUSH	ESI
    	PUSH	EBX
    	MOV	ESI,0xaa55aa55			; pat0 = 0xaa55aa55;
    	MOV	EDI,0x55aa55aa			; pat1 = 0x55aa55aa;
    	MOV	EAX,[ESP+12+4]			; i = start;
    mts_loop:
    	MOV	EBX,EAX
    	ADD	EBX,0xffc				; p = i + 0xffc;
    	MOV	EDX,[EBX]				; old = *p;
    	MOV	[EBX],ESI				; *p = pat0;
    	XOR	DWORD [EBX],0xffffffff	; *p ^= 0xffffffff;
    	CMP	EDI,[EBX]				; if (*p != pat1) goto fin;
    	JNE	mts_fin
    	XOR	DWORD [EBX],0xffffffff	; *p ^= 0xffffffff;
    	CMP	ESI,[EBX]				; if (*p != pat0) goto fin;
    	JNE	mts_fin
    	MOV	[EBX],EDX				; *p = old;
    	ADD	EAX,0x1000				; i += 0x1000;
    	CMP	EAX,[ESP+12+8]			; if (i <= end) goto mts_loop;
    	JBE	mts_loop
    	POP	EBX
    	POP	ESI
    	POP	EDI
    	RET
    mts_fin:
    	MOV	[EBX],EDX				; *p = old;
    	POP	EBX
    	POP	ESI
    	POP	EDI
    	RET
    
    • 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

    然后就可以编译运行成功了:
    在这里插入图片描述

    二、内存管理

    关于内存管理有以下两种方法:这里假设有0x08000000也就是128m
    方法一:

    假设我们以0x1000 4kb为单位进行管理,则128m 需要32768个字节。然后通过往里面填入0,1:
    如果我们需要100kb的空间,我们就从a中找出连续25个进行标记:

    j = 0
    retry:
    for(i = 0; i < 25 ;i++)
    {
    if(a[j+i] != 0 )
    {
      j++;
      if(j<32768 -25) goto retry;
    //  "无可用内存"
    }
    }
    //找到了从j到j+24个连续为0的空闲地址
    //然后把这些地方标记为1,利用j值计算对应的地址放大0x1000倍即可
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    如果释放地址的话:比如从0x00123000开始的100kb不需要了:

    j = 0x00123000 / 0x1000;
    for(i = 0; i<25; i++)
    {
    a[j + i ] = 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    上述这个方法方法虽然看起来操作简单,但是其管理表所占用的内存地址空间会很大,比如管理3gb内存时,需要786432,即768kb的大小。虽然管理表可以利用位来代替char类型进行管理,使得整体大小降低1/8, 经需96kb即可。(Windows 管理软盘就是用这个方法的)但是使用位的话,可能操作就要复杂很多了!!!

    方法二:
    利用列表的管理方法:
    即把从xxx开始的yyy字节地址空间都是空着的这种信息存于列表当中进行管理。
    利用结构体:

    struct FREEINFO {	//可用信息
    	unsigned int addr, size;
    };
    
    struct MEMMAN {		//进行内存管理
    	int frees, maxfrees, lostsize, losts;
    	struct FREEINFO free[MEMMAN_FREES[1000];
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    例如:memman.free[0].addr = 0x00400000 ; memman.free[0].size = 0x07c00000 代表着从0x00400000开始有124mb空间可用。
    使用后,直接将这一段信息从可用空间中删去即可。释放时,需查看前后是否可以合并,能合并就合并,不能就单独添加进入即可。

    单该方法可能会产生碎片空间,这一段空间可能无法利用到,而被割舍。

    本次就使用列表法进行管理,如果有问题后续在优化:
    bootpack.c:

    #define MEMMAN_FREES 4090 //整个的管理表大约是32kb <== 有4090组空余空间
    #define MEMMAN_ADDR	 0x003c0000
    
    struct FREEINFO {	//可用信息
    	unsigned int addr, size;
    };
    
    struct MEMMAN {		//进行内存管理
    	int frees, maxfrees, lostsize, losts;
    	struct FREEINFO free[MEMMAN_FREES];
    };
    
    void memman_init(struct MEMMAN *man)
    {
    	man->frees = 0;//可用信息数目
    	man->maxfrees =0;//用于观察可用状况:frees的最大值
    	man->lostsize = 0;//释放失败的内存的大小总和
    	man->losts = 0; //释放失败的次数
    	return;
    }
    
    unsigned int memman_total(struct MEMMAN *man)
    {//报告空余内存大小的合计
    	unsigned int i, t = 0;
    	for(i = 0; i < man->frees; i++)
    	{
    		 t += man->free[i].size;
    	}
    	return t;
    }
    
    unsigned int memman_alloc(struct MEMMAN *man, unsigned int size)
    {//分配
    	unsigned int i, a;
    	for(i = 0; i < man->frees; i++)
    	{
    		if(man->free[i].size >= size)//如果可以找到足够大的内存
    		{
    			a = man->free[i].addr;
    			man->free[i].addr += size;
    			man->free[i].size -= size;
    			if(man->free[i].size == 0)
    			{//如果free[i] 变成了0 ,就减掉一个可用信息
    				man->frees--;
    				for(; i < man->frees; i++)
    				{
    					man->free[i] = man->free[i+1]; //带入结构体
    				}
    			}
    			return a;
    		}
    	}
    	return 0; //没有可用空间
    }
    
    int memman_free(struct MEMMAN *man, unsigned int addr, unsigned int size)
    {//释放
      int i, j; 
      /*为了便于归纳内存,将free[]按照addr的顺序排列,递增
        所以,下面先决定应该放在哪里
      */ 	
      for(i = 0; i < man->frees; i++)
      { 
      	if(man->free[i].addr > addr)
      	{
      		break;
      	}
      }
      //free[i-1].addr < addr < free[i].addr
      if(i>0)
      {
      	//前面有可用内存
      	if(man->free[i-1].addr + man->free[i-1].size == addr)
      	{//与前面可用内存放在一起
      		man->free[i-1].size += size;
      		if(i < man->frees)
      		{//后面也有
      			if(addr + size == man->free[i].addr)
      			{//与后面可用内存放在一起
      				man->free[i-1].size += man->free[i].size;
      				//删去man->free[i]
      				//free[i]变成0后归纳到前面
      				man->frees--;
      				for(; i < man->frees; i++)
    				  {
    					  man->free[i] = man->free[i+1]; //带入结构体
    				  }
      			}
      		}
      		return 0;//成功完成
      	}
      }
      //不能与前面的可用空间合在一起
      if(i < man->frees)
      {//后面还有
      	if(addr + size == man->free[i].addr)
      	{//可以与后面内容归纳在一起
      		man->free[i].addr = addr;
      		man->free[i].size += size;
      		return 0;//完成
      	}
      }
      //既不能与前面归纳,也不能与后面归纳
      if(man->frees < MEMMAN_FREES)
      {//free[i]之后的,向后移动,腾出空间
      	for(j = man->frees; j > i; j--) //如果还有可用数量的话,则往后移动
      	{
      		man->free[j] = man->free[j-1];
      	}
      	man->frees++;
      	if(man->maxfrees < man->frees)
      	{
      		man->maxfrees = man->frees; //更新最大值
      	}
      	man->free[i].addr = addr;
      	man->free[i].size = size;
      	return 0; //成功完成
      }
      //不能后移
      man->losts++;
      man->lostsize += size;
      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
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124

    然后让我们在主程序中进行添加对应的函数进行计算:

    unsigned int memtotal;
    	struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;//设定了管理表位于内存的0x003c0000地址,预估今后不会用到
    	....
      memtotal = memtest(0x00400000, 0xbfffffff); //算出内存地址
    	memman_init(memman);
    	memman_free(memman, 0x00001000, 0x0009e000); /* 0x00001000 - 0x0009efff */
    	memman_free(memman, 0x00400000, memtotal - 0x00400000);
    	....
    		sprintf(s, "memory %dMB   free : %dKB",
    			memtotal / (1024 * 1024), memman_total(memman) / 1024);
    	putfonts8_asc(binfo->vram, binfo->scrnx, 0, 32, COL8_FFFFFF, s);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在这里插入图片描述


    总结

    以上,我们完成了内存的管理任务,明天开始进行叠加处理,以便解决之前鼠标的bug,即拖入到下面的框框时,会把框框覆盖掉!!

  • 相关阅读:
    2015年亚太杯APMCM数学建模大赛B题城市公共交通服务水平动态评价模型求解全过程文档及程序
    如何在 SAP ABAP 系统中使用 Adobe Form
    五、域对象共享数据
    Selenium基础:自动化你的网页交互
    【Vue面试题十八】、你知道vue中key的原理吗?说说你对它的理解
    猿创征文|前路有光,初心莫忘,从编程小白,到如今小有所成,我这一路是如何走来的?
    番茄插件番茄助手下载
    2024.08.07校招 实习 内推 面经
    信息收集小技巧
    Continuity” of stochastic integral wrt Brownian motion
  • 原文地址:https://blog.csdn.net/qq_43696276/article/details/125993817