30天自制操作系统学习-第9天

2023-10-31

1 整理源文件

 昨天对鼠标键盘的控制函数都放在了HariMain主函数中,今天我们先将这些功能独立一个对应的C文件中即可。修改后的文件目录结构:

只需在bootpack.h头文件中声明即可:

2 内存管理

高速缓存----维基百科:

Cache一词来源于1967年的一篇电子工程期刊论文。其作者将法语词“cache”赋予“safekeeping storage”的涵义,用于计算机工程领域。PC-AT/XT和80286时代,没有Cache,CPU和内存都很慢,CPU直接访问内存。80386芯片组增加了对可选的Cache的支持,高级主板带有64KB,甚至高端大气上档次的128KB Write-Through Cache。80486 CPU里面加入了8KB的L1 Unified Cache,当时也叫做内部Cache,不分代码和数据,都存在一起;芯片组中的Cache,变成了L2,也被叫做外部Cache,从128KB到256KB不等;增加了Write-back的Cache属性。Pentium CPU的L1 Cache分为Code和data,各自8KB;L2还被放在主板上。Pentium Pro的L2被放入到CPU的Package上。Pentium 4开始,L2 Cache被放入了CPU的Die中。从Intel Core CPU开始,L2 Cache为多核共享。

CPU处理数据时,它会先到Cache中去寻找,如果数据因之前的操作已经读取而被暂存其中,就不需要再从随机存取存储器(Main memory)中读取数据——由于CPU的运行速度一般比主内存的读取速度快,主存储器周期(访问主存储器所需要的时间)为数个时钟周期。因此若要访问主内存的话,就必须等待数个CPU周期从而造成浪费。

提供“缓存”的目的是为了让数据访问的速度适应CPU的处理速度,其基于的原理是内存中“程序执行与数据访问的局域性行为”,即一定程序执行时间和空间内,被访问的代码集中于一部分。为了充分发挥缓存的作用,不仅依靠“暂存刚刚访问过的数据”,还要使用硬件实现的指令预测数据预取技术——尽可能把将要使用的数据预先从内存中取到缓存里。

CPU的缓存曾经是用在超级计算机上的一种高级技术,不过现今计算机上使用的的AMDIntel微处理器都在芯片内部集成了大小不等的数据缓存和指令缓存,通称为L1缓存(L1 Cache即Level 1 On-die Cache,第一级片上高速缓冲存储器);而比L1更大容量的L2缓存曾经被放在CPU外部(主板或者CPU接口卡上),但是现在已经成为CPU内部的标准组件;更昂贵的CPU会配备比L2缓存还要大的L3缓存(level 3 On-die Cache第三级高速缓冲存储器)。

CPU访问内存时寄存器对内存单元的访问速度并不快,相比寄存器与寄存器之间的访问而言,为此CPU的设计者使用缓存这一概念提高访问速度,读取内存单元数据时,CPU会先检查缓存中,如果存在则取出,这样无需通过访问内存单元。当CPU向内存单元写入数据前,会先往缓存里写入。值得一提的是机器语言乃至汇编语言,高级语言,程序运行的大部分时间都花费在循环上,为此。比如for(int i=0;i<100;i++),CPU在缓存内会对i频繁赋值进行处理,在循环结束后才往内存单元中写入i最终的值,而不是每循环一次写入一次。

我们在检查内存时需要注意CPU是否有缓存,如果在忽略CPU缓存的情况下对内存进行检查是否被使用,而CPU缓存已经预约了内存中的某块地址,而仅仅是还没写入内存而已,这样我们的程序将会混乱。

486以上的CPU都有高速缓存这一机制,当我们检查到CPU位486以上时,我们需要先将CPU的高速缓存置为OFF,那么如何检查CPU是386还是486以上,我们检查EFLAGS的第18位即所谓的AC标志,如果CPU是386那么AC标志位则为0,如果是486以上则为1。我们检查AC标志位是否为1即可。

那么管理内存又有什么用呢,假设我们内存任意使用,例如应用程序A需要120kb内存,画面控制需要100kb。在内存被使用时应该标志这一段内存,避免其他应用占用而使系统应用错乱,在应用使用完所需的内存单元后,需要释放那部分不需要的内存单元,继而让其它应用使用。

具体代码上的实现:

/* bootpack主函数*/

#include "bootpack.h"
#include <stdio.h>

#define MEMMAN_FREES 4090 /* 大约是32KB*/

struct FREEINFO { /* 可用信息 */
	unsigned int addr, size;
};
struct MEMMAN { /* 内存管理 */
	int frees, maxfrees, lostsize, losts;
	struct FREEINFO free[MEMMAN_FREES];
};

unsigned int memtest(unsigned int start, unsigned int end);
void memman_init(struct MEMMAN *man);
unsigned int memman_total(struct MEMMAN *man);
unsigned int memman_alloc(struct MEMMAN *man, unsigned int size);
int memman_free(struct MEMMAN *man, unsigned int addr, unsigned int size);

#define MEMMAN_ADDR 0x003c0000

void HariMain(void)
{
	struct BOOTINFO *binfo = (struct BOOTINFO *) ADR_BOOTINFO;
	char s[40], mcursor[256], keybuf[32], mousebuf[128];
	int mx, my, i;
	struct MOUSE_DEC mdec;

	unsigned int memtotal;
	struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;

	init_gdtidt();
	init_pic();
	io_sti(); /* IDT/PIC的初始化已经完成,于是开放CPU的中断 */

	fifo8_init(&keyfifo, 32, keybuf);
	fifo8_init(&mousefifo, 128, mousebuf);
	io_out8(PIC0_IMR, 0xf9); /* 开放PIC1和键盘中断(11111001) */
	io_out8(PIC1_IMR, 0xef); /* 开放鼠标中断(11101111) */

	init_keyboard();
	enable_mouse(&mdec);
	memtotal = memtest(0x00400000, 0xbfffffff);
	memman_init(memman);
	memman_free(memman, 0x00001000, 0x0009e000); /* 0x00001000 - 0x0009efff */
	memman_free(memman, 0x00400000, memtotal - 0x00400000);

	init_palette();
	init_screen8(binfo->vram, binfo->scrnx, binfo->scrny);
	mx = (binfo->scrnx - 16) / 2; /* 计算画面中心坐标 */
	my = (binfo->scrny - 28 - 16) / 2;
	init_mouse_cursor8(mcursor, COL8_008484);
	putblock8_8(binfo->vram, binfo->scrnx, 16, 16, mx, my, mcursor, 16);
	sprintf(s, "(%d, %d)", mx, my);
	putfonts8_asc(binfo->vram, binfo->scrnx, 0, 0, COL8_FFFFFF, s);

	sprintf(s, "memory %dMB free : %dKB", memtotal / (1024 * 1024), memman_total(memman) / 1024);
	putfonts8_asc(binfo->vram, binfo->scrnx, 0, 32, COL8_FFFFFF, s);

	for (;;) {
		io_cli();
		if (fifo8_status(&keyfifo) + fifo8_status(&mousefifo) == 0) {
			io_stihlt();
		} else {
			if (fifo8_status(&keyfifo) != 0) {
				i = fifo8_get(&keyfifo);
				io_sti();
				sprintf(s, "%02X", i);
				boxfill8(binfo->vram, binfo->scrnx, COL8_008484,  0, 16, 15, 31);
				putfonts8_asc(binfo->vram, binfo->scrnx, 0, 16, COL8_FFFFFF, s);
			} else if (fifo8_status(&mousefifo) != 0) {
				i = fifo8_get(&mousefifo);
				io_sti();
				if (mouse_decode(&mdec, i) != 0) {
					/* 3字节都凑齐了,所以把它们显示出来*/
					sprintf(s, "[lcr %4d %4d]", mdec.x, mdec.y);
					if ((mdec.btn & 0x01) != 0) {
						s[1] = 'L';
					}
					if ((mdec.btn & 0x02) != 0) {
						s[3] = 'R';
					}
					if ((mdec.btn & 0x04) != 0) {
						s[2] = 'C';
					}
					boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 32, 16, 32 + 15 * 8 - 1, 31);
					putfonts8_asc(binfo->vram, binfo->scrnx, 32, 16, COL8_FFFFFF, s);
					/* 鼠标指针的移动 */
					boxfill8(binfo->vram, binfo->scrnx, COL8_008484, mx, my, mx + 15, my + 15); /* 隐藏鼠标 */
					mx += mdec.x;
					my += mdec.y;
					if (mx < 0) {
						mx = 0;
					}
					if (my < 0) {
						my = 0;
					}
					if (mx > binfo->scrnx - 16) {
						mx = binfo->scrnx - 16;
					}
					if (my > binfo->scrny - 16) {
						my = binfo->scrny - 16;
					}
					sprintf(s, "(%3d, %3d)", mx, my);
					boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 0, 0, 79, 15); /* 隐藏坐标 */
					putfonts8_asc(binfo->vram, binfo->scrnx, 0, 0, COL8_FFFFFF, s); /* 显示坐标 */
					putblock8_8(binfo->vram, binfo->scrnx, 16, 16, mx, my, mcursor, 16); /* 描画鼠标 */
				}
			}
		}
	}
}

#define EFLAGS_AC_BIT			0x00040000
#define CR0_CACHE_DISABLE	0x60000000

unsigned int memtest(unsigned int start, unsigned int end) 
{
	char flg486 = 0;
	unsigned int eflg, cr0, i;

	/* 确认CPU是386还是486以上的 */
	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,AC的值还会自动回到0 */
		flg486 = 1;
	}

	eflg &= ~EFLAGS_AC_BIT; /* AC-bit = 0 */
	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;
}

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; /* 失败 */
}

运行:

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

30天自制操作系统学习-第9天 的相关文章

  • Wireshark零基础使用教程(超详细)

    Wireshark零基础使用教程 一 Wireshark是什么 二 Wireshark抓包原理 三 Wireshark安装入门 1 选择网卡 2 停止抓包 3 保存数据 四 界面介绍 五 基础操作 1 调整界面大小 2 设置显示列 1 添加
  • 一个仿 github for windows 及 windows 8 的进度条

    https github com wly2014 ProgressBar 转载于 https www cnblogs com eustoma p 4470396 html
  • dubbo_异常Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException

    Exception in thread main org springframework beans factory UnsatisfiedDependencyException Error creating bean with name
  • BES2300x笔记(27) -- 声道设定与声道切换

    哈喽大家好 这是该系列博文的第二十七篇 篇 lt lt 系列博文索引 快速通道 gt gt 一 前言 前几天 有道友私信问到 BES2300如何进行声道设定 想通过硬件进行固定 那么 这一篇我们就讲讲BES平台有关声道的设定 以及如何进行硬
  • 计算机程序设计语言教案,计算机程序设计(C语言)课程教案讲稿概要.doc

    计算机程序设计 C 语言 课程教案 讲稿 教师姓名 纪澍琴 学院 部 中心 信息传播工程学院 教研室 实验室计算机基础教研室 2008年3月 长春工业大学课程教案 讲稿 与课程有关的信息 教师编号 000361 课程名称 计算机程序设计 C
  • android listview 图片闪烁,listView异步加载图片导致图片错位、闪烁、重复的问题的解决...

    androidListView是android中重要的控件 几乎每一个项目都会用到 但是在使用中我们避免不 了会出现一些问题 包括一些滑动事件的处理 例如 ListView中嵌套scrollView 容易出现listView 展现数据不全的
  • adb关闭手机系统自动更新

    下载adb工具 https mclub lenovo com cn forum php mod attachment aid NDg5ODc1Nnw4MWRhZDE4OHwxNjU0NTI0OTY1fDB8NzgzNzg5OQ 3D 3D
  • Android jni ndk crash c++bug定位

    最近遇到了一个底层c 库的问题 然而看不到是在哪里报错的 有一个方法就是用 ndk stack的方法 在cmd里面切换到adb 在电脑上的目录 然后输入adb logcat ndk stack sym F whl MyApp 替换为你的项目
  • 【C++】递归

    1 什么是递归 程序调用自身的编程技巧称为递归 递归做为一种算法在程序设计语言中广泛应用 一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法 它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解 递归策略只需
  • 前端学习之原生JS实现addClass及removeClass方法的封装

    addclass方法封装 HTML代码 div class two div div class two three div JS代码 var o1 document querySelector b1 var o2 document quer
  • python 选择结构

    选择 用于判断 注意符号 单分支 If 条件 条件成立就执行if缩进的代码 双分支 If 条件 条件成立就执行if缩进的代码 Else 条件 条件不成立就执行else缩进的代码
  • 调整计算机硬盘大小,详细教您怎么调整硬盘分区

    新买的电脑很多磁盘只有一两个 很多时候需要对资料分区管理 甚至有些磁盘容量的分配不是很合理的 这时候怎么解决呢 就要我们对硬盘重新分区大小了 下面由小编给你带来硬盘分区的详细操作步骤 新买的笔记本 有时候会发现整个硬盘只有一个分区 如果将所
  • SpringCloud之服务发现

    1 服务提供者接口 以获取一个订单接口为例 order服务对user服务进行远程调用获取user基本信息 user服务提供的接口如下 package com acx controller import com acx pojo vo Act
  • 白盒测试——代码检查法

    代码检查法是静态的 不用生成测试用例并驱动被测程序运行来发现错误 代码检查法分为三种 一 代码审查 代码审查被认为是软件业最佳的实践 人们可以审查任何一种软件工作产品 包括需求和设计文档 源代码 测试文档及项目计划等 1 代码审查角色 主持

随机推荐

  • unity AR 教程

    start Frist 去Vuforia 官网注册新号 获取 LicenseKey链接地址 https developer vuforia com 如果unity是2017 2以上会在unity安装的时候自带AR组件 如果是老版本 就下载下
  • 华硕主板如何设置开机自启_华硕主板每次开机都进bios 华硕主板开机总是自动进入了BIOS设置界面怎么办?...

    华硕主板开机总是自动进入了BIOS设置界面怎么办 华硕笔记本若电脑一开机自动进入bios设置 可按以下操作更改BIOS设置 1 开机的时候长按F2键进入BIOS界面 通过方向键进 Secure 菜单 通过方向键选择 Secure Boot
  • Android疑难解决-Android Gradle plugin requires Java 11 to run. You are currently using Java 1.8.

    编译项目报错 版本错误 Build file xxx build gradle line 2 An exception occurred applying plugin request id com android application
  • flutter 手势控制

    flutter 手势控制 事件监听 1 指针事件Point 2 手势识别Gesture 3 跨组件事件 在大前端的开发中 必然存在着各种各样和用户交互的情况 比如手指点击 手指滑动 双击 长按等 在Flutter中 手势有两个不同的层次 第
  • QT 的 QSS 的基本概念

    https www cnblogs com davesla archive 2011 01 30 1947928 html 借用css 的灵感 Qt也支持Qt自己的css 简称qss 同css 相似 qss的主要功能与最终目的都是能使界面的
  • C - Check The Text(string)

    C Check The Texthttps vjudge csgrandeur cn problem Gym 102263C Roze有一个特殊的键盘 只有29个键 26个字母a z键 打印26个小写拉丁字母 空格 键 打印一个空格 Cap
  • 机器学习比较好的视频资源

    吴恩达 经典入门课程 中英字幕 吴恩达机器学习系列课程 哔哩哔哩 bilibili www bilibili com video BV164411b7dx spm id from 333 999 0 0正在上传 重新上传取消 双语字幕 吴恩
  • JavaScript中window.print()打印

    JavaScript中使用window print 打印方法时 打印的是当前页的所有内容 所以如果直接在当前页使用此打印方法应先保存当前页面再把打印部分替换当前页面执行完之后再替换回来 或者新打开一个页面 把所打印的部分都写到新打开的页面上
  • Windows10中CUDA cundnn pytorch环境搭建记录

    关于在win10中安装cuda cudnn及pytorch全家桶 torch torchvision torchaudio 的详细安装步骤 可以参考这个帖子 说的非常详细 win10下pytorch gpu安装以及CUDA详细安装过程 仅在
  • ArchLinux安装slock锁屏(suckless)

    简介 一款suckless团队开发的锁屏小工具 下载 git clone https git suckless org slock 或者点击该链接下载 https dl suckless org tools slock 1 4 tar gz
  • JS高级(2)函数高级 — 原型与原型链

    原型与原型链 1 原型prototype 每个函数都有一个prototype属性 它默认指向一个Object空对象 即原型对象 里面没有我们的属性 原型中有一个属性constructor 它指向函数对象 构造函数和原型对象相互引用 func
  • 从零开发区块链应用(六)--gin框架使用

    文章目录 一 Gin 框架介绍 二 Gin安装 三 Gin使用 3 1 设置gin模式 3 2 创建新路由 3 3 创建多路由分组 3 4 创建路由 3 5 编写接口执行函数 3 6 启动服务 参考文档 Gin框架介绍及使用 https w
  • bread是可数还是不可数_在英语语法里,为什么bread是不可数名词?

    学英语 我们要分清楚名词的类型 可数名词 不可数名词 可数名词就是能够用 1 2 3 4 5 这样数的人或事物 比如苹果 茶杯 汽车 都可以这样数 但是 当我们打算去 数 water 水的时候 就水本身而言 它是液体 没有固定的形状和结构
  • SingleThreaded是如何进入cull_draw()的?

    正如以前所说 单线程模式是通过cull draw 进行剔除绘制的 如何进入的呢 其实很简单 逆推下 最后 回到梦开始的地方
  • React学习之围棋记谱本制作(二)棋盘、棋子、交替落子

    与儿子一起学围棋 上网上找 发现好用的记谱本软件特别少 打算自己做一个 不知能不能克服惰性 完成这个目标 千里之行 始于足下 今天完成了基础工作 棋盘 棋子组件 并完成了交替落子功能 是React基本功能的很好示范 代码贴一下 下一步就是多
  • 状态机的思想

    http blog sina com cn s blog 3e71aaaa0100834m html 转载于 https www cnblogs com as3lib p 3518613 html
  • 谷歌云活动

    Google x Cloud Ace 线下活动即将开始 本期主题 解锁 AIGC 密码 探寻企业发展新商机 期待您的莅临 时间 5月24日13 30 17 00 地点 深圳南山 本次活动定向邀请 CXO 们请扫码报名获取地址信息
  • 因果推断--Uplift model的原理和python实操(三)

    目录 一 Uplift Model的应用场景 二 Uplift Model原理及建模方法 2 1 建模目标 2 2 建模方法 1 双模型 差分响应模型 2 标签转化 Class Transformation Method 2 3 模型评估
  • Anaconda实验环境的搭建

    Anaconda和Jupyter notebook Anaconda Conda Package 和 Environment Data Science IDE vs Developer IDE 从IPython 到 Jupyter mac上
  • 30天自制操作系统学习-第9天

    1 整理源文件 昨天对鼠标键盘的控制函数都放在了HariMain主函数中 今天我们先将这些功能独立一个对应的C文件中即可 修改后的文件目录结构 只需在bootpack h头文件中声明即可 2 内存管理 高速缓存 维基百科 Cache一词来源