手写简易操作系统(五)--获得物理内存容量
前情提要
上一章中我们进入了保护模式,并且跳转到了32位模式下执行。这一章较为简单,我们来获取物理内存的实际容量。
一、获得内存容量的方式
在Linux中有多种方法获取内存容量,如果一种方法失败,就会试用其他方法。其本质上是通过调用BIOS中断0x15实现的。分别是三个子功能,子功能号要放在寄存器EAX或AX中。
EAX=0xE820:遍历主机上全部内存。最大支持2^64Byte
AX=0xE801:分别检测低15MB和16MB~4GB的内存,最大支持2^32Byte。
AH=0x88:最多检测出64MB内存,实际内存超过此容量也按照64MB返回。
若三种方法都失败了只能将机器挂起,停止运行。
二、利用子功能号0xE820
BIOS中断 0x15的子功能0xE820能够获取系统的内存布局,由于系统内存各部分的类型属性不同,BIOS就按照类型属性来划分这片系统内存,所以这种查询呈迭代式,每次BIOS只返回一种类型的内存信息,直到将所有内存类型返回完毕。
内存信息的内容是用地址范围描述符来描述的,用于存储这种描述符的结构称之为地址范围描述符(Address Range Descriptor Structure,ARDS),格式见下表
其中Type为1则表示这段内存可以被操作系统使用,Type为2则表示这段内存不能给操作系统使用(因为这个地址可能是硬件端口,系统ROM,某种设备的内存映射到了这部分什么的),其他的都未定义。
正常情况下,不会出现较大的内存区域不可用的情况,除非安装的物理内存极其小。这意味着,在所有返回的ARDS结构里,此值最大的内存块一定是操作系统可使用的部分,即主板上配置的物理内存容量。
此中断子功能参数见下表
三、利用子功能号0xE801
此方法虽然简单,但功能也不强大,最大只能识别4GB内存,不过这对咱们32位地址总线足够了。稍微有点不便的是此方法检测到的内存是分别存放到两组寄存器中的。低于15MB的内存以1KB为单位大小来记录,单位数量在寄存器AX和CX中记录,其中AX和CX的值是一样的。16MB~4GB是以64KB为单位大小来记录的,单位数量在寄存器BX和DX中记录,其中BX和DX的值是一样的。
为什么区分16MB以上即以下呢?其实这只是为了兼容,80286拥有24位地址线,其寻址空间是16MB。当时有一些ISA设备要用到地址15MB以上的内存作为缓冲区,也就是此缓冲区为1MB大小,所以硬件系统就把这部分内存保留下来,操作系统不可以用此段内存空间。现在这些设备我们几乎不会接触到,但是这个问题还是保留下来了。我们当然在实际操作时要无视这个空间。
四、利用子功能号0x88
该方法使用最简单,但功能也最简单,简单到只能识别最大64MB的内存。即使内存容量大于64MB,也只会显示63MB。此中断只会显示1MB之上的内存,不包括这1MB。
五、检测代码
将程序修改为
; os/src/boot/loader.s
%include "boot.inc"
section loader vstart=LOADER_BASE_ADDR ; 程序开始的地址jmp loader_startLOADER_STACK_TOP equ LOADER_BASE_ADDR ; 栈顶地址;构建gdt及其内部的描述符
GDT_BASE: dd 0x00000000 dd 0x00000000CODE_DESC: dd 0x0000FFFF dd DESC_CODE_HIGH4DATA_STACK_DESC: dd 0x0000FFFFdd DESC_DATA_HIGH4VIDEO_DESC: dd 0x80000007 ; limit=(0xbffff-0xb8000)/4k=0x7dd DESC_VIDEO_HIGH4 ; 此时dpl为0GDT_SIZE equ $ - GDT_BASE
GDT_LIMIT equ GDT_SIZE - 1
times 60 dq 0 ; 此处预留60个描述符的slot
SELECTOR_CODE equ (0x0001<<3) + TI_GDT + RPL0 ; 第一个选择子
SELECTOR_DATA equ (0x0002<<3) + TI_GDT + RPL0 ; 第二个选择子
SELECTOR_VIDEO equ (0x0003<<3) + TI_GDT + RPL0 ; 第三个选择子; 以下是定义gdt的指针,前2字节是gdt界限,后4字节是gdt起始地址
gdt_ptr dw GDT_LIMIT dd GDT_BASEtotal_mem_bytes dd 0 ; 保存内存容量,以字节为单位
ards_buf times 244 db 0 ; 人工对齐:total_mem_bytes4字节+gdt_ptr6字节+ards_buf244字节+ards_nr2,共256字节
ards_nr dw 0 ; 用于记录ards结构体数量loader_start:mov byte [gs:160],'L'mov byte [gs:161],0x0Fmov byte [gs:162],'O'mov byte [gs:163],0x0Fmov byte [gs:164],'A'mov byte [gs:165],0x0F mov byte [gs:166],'D'mov byte [gs:167],0x0Fmov byte [gs:168],'E'mov byte [gs:169],0x0Fmov byte [gs:170],'R'mov byte [gs:171],0x0F; 获取内存容量,int 15, ax = E820h
.get_total_mem_bytes:xor ebx, ebx ;第一次调用时,ebx值要为0mov edx, 0x534d4150 ;edx只赋值一次,循环体中不会改变mov di, ards_buf ;ards结构缓冲区
.e820_mem_get_loop: ;循环获取每个ARDS内存范围描述结构mov eax, 0x0000e820 ;执行int 0x15后,eax值变为0x534d4150,所以每次执行int前都要更新为子功能号。mov ecx, 20 ;ARDS地址范围描述符结构大小是20字节int 0x15jc .failed_so_try_e801 ;若cf位为1则有错误发生,尝试0xe801子功能add di, cx ;使di增加20字节指向缓冲区中新的ARDS结构位置inc word [ards_nr] ;记录ARDS数量cmp ebx, 0 ;若ebx为0且cf不为1,这说明ards全部返回,当前已是最后一个jnz .e820_mem_get_loop;在所有ards结构中,找出(base_add_low + length_low)的最大值,即内存的容量。mov cx, [ards_nr] ;遍历每一个ARDS结构体,循环次数是ARDS的数量mov ebx, ards_buf xor edx, edx ;edx为最大的内存容量,在此先清0
.find_max_mem_area: ;无须判断type是否为1,最大的内存块一定是可被使用mov eax, [ebx] ;base_add_lowadd eax, [ebx+8] ;length_lowadd ebx, 20 ;指向缓冲区中下一个ARDS结构cmp edx, eax ;冒泡排序,找出最大,edx寄存器始终是最大的内存容量jge .next_ardsmov edx, eax ;edx为总内存大小
.next_ards:loop .find_max_mem_areajmp .mem_get_ok; 获取内存容量,int 15, ax = E801h
.failed_so_try_e801:mov ax,0xe801int 0x15jc .failed_so_try88 ;若当前e801方法失败,就尝试0x88方法; 先算出低15M的内存,ax和cx中是以KB为单位的内存数量,将其转换为以byte为单位mov cx,0x400 ;cx和ax值一样,cx用做乘数mul cx shl edx,16and eax,0x0000FFFFor edx,eaxadd edx, 0x100000 ;ax只是15MB,故要加1MBmov esi,edx ;先把低15MB的内存容量存入esi寄存器备份; 再将16MB以上的内存转换为byte为单位,寄存器bx和dx中是以64KB为单位的内存数量xor eax,eaxmov ax,bx mov ecx, 0x10000 ;0x10000十进制为64KBmul ecx ;32位乘法,默认的被乘数是eax,积为64位,高32位存入edx,低32位存入eax.add esi,eax ;由于此方法只能测出4G以内的内存,故32位eax足够了,edx肯定为0,只加eax便可mov edx,esi ;edx为总内存大小jmp .mem_get_ok; 获取内存容量,int 15, ah = 0x88
.failed_so_try88: ;int 15后,ax存入的是以kb为单位的内存容量mov ah, 0x88int 0x15jc .error_hltand eax,0x0000FFFF;16位乘法,被乘数是ax,积为32位.积的高16位在dx中,积的低16位在ax中mov cx, 0x400 ;0x400等于1024,将ax中的内存容量换为以byte为单位mul cxshl edx, 16 ;把dx移到高16位or edx, eax ;把积的低16位组合到edx,为32位的积add edx,0x100000 ;0x88子功能只会返回1MB以上的内存,故实际内存大小要加上1MBjmp .mem_get_ok;将内存换为byte单位后存入total_mem_bytes处。
.mem_get_ok:mov [total_mem_bytes], edx ; 打开A20地址线
.open_A20:in al,0x92or al,0000_0010Bout 0x92,al; 加载gdt描述符
.load_gdt:lgdt [gdt_ptr]; 修改cr0标志寄存器的PE位
.change_cr0_PE:mov eax, cr0or eax, 0x00000001mov cr0, eax.jmp_bit_32jmp SELECTOR_CODE:p_mode_start ; 刷新流水线,避免分支预测的影响; 远跳将导致之前做的预测失效,从而起到了刷新的作用。.error_hlt: ;出错则挂起hlt; 下面就是保护模式下的程序了
[bits 32]
p_mode_start:mov ax, SELECTOR_DATAmov ds, axmov es, axmov ss, axmov esp,LOADER_STACK_TOPmov ax, SELECTOR_VIDEOmov gs, axmov byte [gs:320], 'M'mov byte [gs:321],0x0Fmov byte [gs:322], 'A'mov byte [gs:323],0x0Fmov byte [gs:324], 'I'mov byte [gs:325],0x0Fmov byte [gs:326], 'N'mov byte [gs:327],0x0Fjmp $
可以看到仿真结果
可以看到,检测到的内存就是32MB
结束语
这节我们讲述了如何检测物理内存的大小,下节课我们对内存进行处理,内存现在是一个线性的空间,谁想去哪儿就去哪儿,这并不利于我们管理,而且会导致内存的碎皮化问题,下节我们讲内存的分段与分页。
ps:上节说不知道为啥程序不运行了,最后发现是loader导入时导入的扇区数太少了,这节也是不知道为啥不执行了,后面发现是dd指令在将准备好的程序放入硬盘时只放了1024字节,但是程序有一千一百多字节,所以没放下。。。。
相关文章:

手写简易操作系统(五)--获得物理内存容量
前情提要 上一章中我们进入了保护模式,并且跳转到了32位模式下执行。这一章较为简单,我们来获取物理内存的实际容量。 一、获得内存容量的方式 在Linux中有多种方法获取内存容量,如果一种方法失败,就会试用其他方法。其本质上是…...

机器学习之DeepSequence软件使用学习3-预测突变效应
import theano import numpy as np import sys import pandas as pd import scipy #scipy 模块是 Python 中用于科学计算和数据分析的重要模块之一。它包含了许多高级的数学函数和工具,包括数值积分、优化、线性代数、统计等。 from scipy.stats import spearmanr #…...

Linux文件与文件系统的压缩
文章目录 Linux文件与文件系统的压缩Linux系统常见的压缩命令gzip,zcat/zmore/zless/zgrepbzip2,bzcat/bzmore/bzless/bzgreppxz,xzcat/xzmore/xzless/xzgrepgzip,bzip2,xz压缩时间对比打包命令:tar打包命令…...
ubuntu 中进入python 编辑如何退出到命令行
文章目录 在Python解释器(交互式命令行)中,你可以使用 exit()函数或 CtrlD(在Unix/Linux/macOS上)或 CtrlZ然后输入 Enter(在Windows上)来退出Python解释器并返回到命令行。 以下是具体的步骤&a…...

2024.3.12 C++
1.思维导图 2.自己封装一个矩形类(Rect),拥有私有属性:宽度(width)、高度(height),定义公有成员函数: 初始化函数:void init(int w, int h)更改宽度的函数:set_w(int w)更改高度的函数:set_h(int h) 输出该矩形的周长和面积函数:void show() #include <iostream…...

飞塔防火墙开局百篇——002.FortiGate上网配置——透明模式配置(Transparent)
透明模式配置 开启透明模式创建策略 在不改变现有网络拓扑前提下,将防火墙NGFW以透明模式部署到网络中,放在路由器和交换机之间,防火墙为透明模式,对内网网段192.168.1.0/24的上网进行4~7层的安全防护。 登陆FortiGate防火墙界面&…...
代码随想录算法训练营第52天|300.最长递增子序列 674.最长连续递增序列 718.最长重复子数组
300.最长递增子序列 这道题还挺简单的,咱们设置dp[i]表示到第i个数字时的递增子序列的最长的值,那么dp[i]就要遍历从0到i-1的数,也就是看看当前这个数字是否比前面的数字大,如果大的话就看看现在的子序列长度是否会长于前面那个数…...
分享一些开源的游戏仓库
1.CnC_Remastered_Collection 红色警戒95版本 https://github.com/electronicarts/CnC_Remastered_Collection gitee仓库分流:https://gitee.com/loswdarmy/CnC_Remastered_Collection 2.Far-Cry-1-Source-Full 孤岛惊魂1 https://github.com/StrongPC123/Far-Cry-…...

Java详解:单列 | 双列集合 | Collections类
○ 前言: 在开发实践中,我们需要一些能够动态增长长度的容器来保存我们的数据,java中为了解决数据存储单一的情况,java中就提供了不同结构的集合类,可以让我们根据不同的场景进行数据存储的选择,如Java中提…...
Centos7 使用docker来部署mondb
参考官方手册: https://www.mongodb.com/docs/manual/tutorial/install-mongodb-community-with-docker/#std-label-docker-mongodb-community-install 使用脚本快速安装docker curl -fsSL https://get.docker.com -o get-docker.sh | bash get-docker.sh使用 Doc…...
Java SE入门及基础(35)
接口 1. 概念 在软件工程中,软件与软件的交互很重要,这就需要一个约定。每个程序员都应该能够编写实现这样的约定。接口就是对约定的描述。 In the Java programming language, an interface is a reference type, similar to a class, that can con…...

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的常见车型识别系统(Python+PySide6界面+训练代码)
摘要:本文深入探讨了如何应用深度学习技术开发一个先进的常见车型识别系统。该系统核心采用最新的YOLOv8算法,并与早期的YOLOv7、YOLOv6、YOLOv5等版本进行性能比较,主要评估指标包括mAP和F1 Score等。详细解析了YOLOv8的工作机制,…...
Sqoop 学习
参考视频 大数据Sqoop教程丨从零开始讲解大数据业务及数据采集和迁移需求_哔哩哔哩_bilibili 介绍 Sqoop是Hadoop生态体系和RDBMS(关系型数据库)体系之间传送数据的一种工具 Hadop生态系统:HDFS,Hbase,Hive等 RDBMS包…...

Ollama 只安装 Ollama,本地快速部署谷歌开源大模型Gemma(基于Ollama)
参考:本地快速部署谷歌开源大模型Gemma(基于Ollama) - 知乎 确保系统更新: Bash sudo apt update && sudo apt upgrade 需要先下载Ollama,版本要求0.1.26及以上 运行curl -fsSL https://ollama.com/install.sh | sh 监听 Ollama API 接…...

一条 sql 语句可能导致的表锁和行锁以及死锁检测
锁 MDL 当对一个表做增删改查操作的时候,加 MDL 读锁;当要对表做结构变更操作的时候,加 MDL 写锁 ALTER TABLE tbl_name NOWAIT add column ... ALTER TABLE tbl_name WAIT N add column ... …...

prometheus 原理(架构,promql表达式,描点原理)
大家好,我是蓝胖子,提到监控指标,不得不说prometheus,今天这篇文章我会对prometheus 的架构设计,promql表达式原理和监控图表的绘图原理进行详细的解释。来让大家对prometheus的理解更加深刻。 架构设计 先来看看&am…...
Linux的目录结构(介绍主要的)
/:根目录,文件系统的起点,包含了所有目录和文件 /bin:存放基本的可执行命令,如ls,cp,rm /lib:主要存放动态链接库 /opt:供第三方软件安装的目录,通常将软件…...

推房子游戏c++
这段代码是一个推箱子游戏的实现。游戏中有一个地图,地图上有墙壁、人、箱子和目标位置。玩家通过键盘输入WASD或方向键来控制人物的移动,目标是将所有的箱子推到相应的目标位置上。 代码中的dt数组表示地图,每个位置上的字符表示对应的元素…...

docker学习入门篇
1、docker简介 docker官网: www.docker.com dockerhub官网: hub.docker.com docker文档官网:docs.docker.com Docker是基于Go语言实现的云开源项目。 Docker的主要目标是:Build, Ship and Run Any App, Anywhere(构建&…...
【Spring Boot 3】动态注入和移除Bean
【Spring Boot 3】动态注入和移除Bean 背景介绍开发环境开发步骤及源码工程目录结构总结动态注入Bean的方法动态移除Bean的方法注意事项背景 软件开发是一门实践性科学,对大多数人来说,学习一种新技术不是一开始就去深究其原理,而是先从做出一个可工作的DEMO入手。但在我个…...
RestClient
什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端,它允许HTTP与Elasticsearch 集群通信,而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级ÿ…...

华为云AI开发平台ModelArts
华为云ModelArts:重塑AI开发流程的“智能引擎”与“创新加速器”! 在人工智能浪潮席卷全球的2025年,企业拥抱AI的意愿空前高涨,但技术门槛高、流程复杂、资源投入巨大的现实,却让许多创新构想止步于实验室。数据科学家…...

以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:
一、属性动画概述NETX 作用:实现组件通用属性的渐变过渡效果,提升用户体验。支持属性:width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项: 布局类属性(如宽高)变化时&#…...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...
镜像里切换为普通用户
如果你登录远程虚拟机默认就是 root 用户,但你不希望用 root 权限运行 ns-3(这是对的,ns3 工具会拒绝 root),你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案:创建非 roo…...

ETLCloud可能遇到的问题有哪些?常见坑位解析
数据集成平台ETLCloud,主要用于支持数据的抽取(Extract)、转换(Transform)和加载(Load)过程。提供了一个简洁直观的界面,以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...
Neo4j 集群管理:原理、技术与最佳实践深度解析
Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...

Module Federation 和 Native Federation 的比较
前言 Module Federation 是 Webpack 5 引入的微前端架构方案,允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...

WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成
厌倦手动写WordPress文章?AI自动生成,效率提升10倍! 支持多语言、自动配图、定时发布,让内容创作更轻松! AI内容生成 → 不想每天写文章?AI一键生成高质量内容!多语言支持 → 跨境电商必备&am…...
【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具
第2章 虚拟机性能监控,故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令:jps [options] [hostid] 功能:本地虚拟机进程显示进程ID(与ps相同),可同时显示主类&#x…...