Gos —— 获取物理内存容量
文章目录
- linux 中获取内存信息的方法
- 0xe820:遍历主机上全部内存
- 0xe801:分别检测低15MB和16MB~4GB的内存
- **ah = 0x88**:检测64MB内存
- 最终实现
- 参考文献
写在前面:自制操作系统Gos 第二章第六篇:主要内容是如何获取内存信息
linux 中获取内存信息的方法
大家可能在想,我们获取内存信息多简单啊,直接top
命令就可以了。确实,在Linux中输入这个命令就可以了。但是在我们自己的操作系统上可没人帮我们实现top
命令,所以我们必须明白这个top
命令的底层原理实现。
注:top这个例子可能不是很准确,大家懂我啥意思就好了
在x86体系下,由三种方法可以查看系统的内存,但是其本质都是依托BIOS的0x15
中断的三个子功能:
- eax = 0xe820:遍历主机上全部内存
- ax = 0xe801:分别检测低15MB和16MB~4GB的内存
- ah = 0x88:最多检测出64MB内存
BIOS中断是实模式下的方法,其可以返回已安装的硬件信息。本质呢,则是通过连续调用硬件的应用程序接口API来获取内存信息的。但是由于我们之后就要进入保护模式了,所以我们需要把结果存到内存中,这个等会儿在说。
0xe820:遍历主机上全部内存
BIOS中断0x15的子功能号0xe820
能够获取系统的内存布局,由于系统内存各部分的类型属性不同,BIOS就按照类型属性来划分这片系统内存,BIOS不断的将内存信息返回。这个返回的本质起始时一个地址范围描述符,其结构如下:
其中每个字段的含义如下:
- BaseAddrLow:基地址的低32位
- BaseAddrHigh:基地址的高32位
- LengthLow:内存长度低32位
- LengthHigh:内存长度高32位
- Type:本段内存的属性
其他几个都没啥好讲的,主要是这个Type字段,其含义如下:
值 | 描述 |
---|---|
1 | 这段内存可以被操作系统使用 |
2 | 内存使用中或者被系统保留,操作系统不可用 |
其他 | 未定义 |
而获取地址范围描述符就要守规矩,基本规则就是我们输入什么,它们返回什么。
首先是调用前输入:
寄存器 | 用途 |
---|---|
eax | 输入子功能号,由于是要用0xe820,所以此处是0xe820,子功能号更改一下就好了 |
ebx | ards后续值:因为每次只能返回一个,所以它里面并保存的就是后续值的地址,可以想象为链表 |
es : di | ards缓冲区:BIOS将获取到的内存信息写入此寄存器执行的内存 |
ecx | ards结构的字节大小,目前输入20就可以了 |
edx | 固定签名标记0x534d4150 |
而检测主要有以下几个步骤:
- 填写好输入的寄存器
- 执行中断
- 在CF位为0的情况下,获取相应的结果
而用代码表示则是这样:
xor ebx, ebx ;第一次调用时,ebx值要为0mov edx, 0x534d4150 mov di, ards_buf ;ards结构缓冲区mov eax, 0x0000e820 ;执行int 0x15后,eax值变为0x534d4150,所以每次执行int前都要更新为子功能号。mov ecx, 20 ;ARDS地址范围描述符结构大小是20字节int 0x15
我们输入之后直接启用中断就好,之后等待返回就可以了,返回规则如下:
寄存器或状态位 | 用途 |
---|---|
CF位 | 若CF为0表示调用成功,为1表示调用出错 |
EAX | 字符串SMAP的ASCII码 0x534d4150 |
es : di | ards缓冲区地址 |
ecx | BIOS写入到缓冲区中的字节数 |
ebx | 后续值的位置 |
代码实现如下:
.e820_mem_get_loop: ;循环获取每个ARDS内存范围描述结构mov eax, 0x0000e820 ;执行int 0x15后,eax值变为0x534d4150,所以每次执行int前都要更新为子功能号。mov ecx, 20 ;ARDS地址范围描述符结构大小是20字节int 0x15jc .e820_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
0xe801:分别检测低15MB和16MB~4GB的内存
现在我们来介绍一下第二种方法,其也是0xe820失败后我们应该采取的第一个补救措施。
为什么他不是我们获取内存信息的首要方法呢?这是因为其检测到的内存是分别存放到两组寄存器中的。低于15MB的内存以1KB为单位大小来记录,单位数量在寄存器ax
和cx
中记录,当然,这两个值一样的。16MB~4GB是以64KB为单位大小来记录的,单位数量在寄存器bx
和dx
中记录。
而我们想使用这个功能。只需要简单的填写一个ax
中的功能号就好了。
寄存器 | 描述 |
---|---|
ax | 输入子功能号0xe801 |
而在触发中断之后,BIOS会给我们返回以下信息:
寄存器或状态位 | 描述 |
---|---|
CF位 | CF为0表示调用成功,为1表示调用出错 |
ax | 显示15MB以下的内存容量,以1KB为单位 |
bx | 显示16MB~4GB的内存容量,以64KB为单位 |
cx | 同ax |
dx | 同bx |
实现思路和上面的0xe820,所以代码上面我们也很好实现啦:
.e820_failed_so_try_e801:mov ax,0xe801int 0x15jc .e801_failed_so_try88 ;若当前e801方法失败,就尝试0x88方法;1 先算出低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寄存器备份;2 再将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
ah = 0x88:检测64MB内存
这个方法应该是功能性最局限的了。思路和上面两个基本一样,只是输入和输出的东西不同了。
先看一下输入:
寄存器 | 用途 |
---|---|
ah | 子功能号:0x88 |
而输出的东西也很简单:
CF位 | 若CF为0表示调用未出错,CF为1表示出错 |
ax | 内存空间1MB之上的连续单位数量,以1KB为单位 |
其代码实现如下:
.e801_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以上的内存,故实际内存大小要加上1MB
最终实现
这样我们就获得了内存信息,之后我们把这个值存储到0xb00
这个位置:
; total_mem_bytes用于保存内存容量,以字节为单位,此位置比较好记。; 当前偏移loader.bin文件头0x200字节,loader.bin的加载地址是0x900,; 故total_mem_bytes内存中的地址是0xb00.将来在内核中咱们会引用此地址total_mem_bytes dd 0 loader_start:;------- int 15h eax = 0000E820h ,edx = 534D4150h ('SMAP') 获取内存布局 -------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 .e820_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 15h ax = E801h 获取内存大小,最大支持4G ------
; 返回后, ax cx 值一样,以KB为单位,bx dx值一样,以64KB为单位
; 在ax和cx寄存器中为低16M,在bx和dx寄存器中为16MB到4G。
.e820_failed_so_try_e801:mov ax,0xe801int 0x15jc .e801_failed_so_try88 ;若当前e801方法失败,就尝试0x88方法;1 先算出低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寄存器备份;2 再将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 15h ah = 0x88 获取内存大小,只能获取64M之内 ----------
.e801_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以上的内存,故实际内存大小要加上1MB.mem_get_ok:mov [total_mem_bytes], edx ;将内存换为byte单位后存入total_mem_bytes处。
参考文献
[1] 操作系统真相还原
Gos —— 获取物理内存容量相关推荐
- 一步步编写操作系统 34 内核利用bios中断获取物理内存大小
接上文,另一个获取内存容量的方法是bios 0x15中断的子功能0xE801. 此方法虽然简单,但功能也不强大,最大只能识别4G内存,不过这对咱们32位地址总线足够了.稍微有点不便的是,此方法检测到的 ...
- 一步步编写操作系统 32 linux内核获取内存容量的方法
操作系统是计算机硬件的管家,它不仅要知道自己的安装了哪些硬件,还得给出有效得当的管理措施,按照预定的一套管理策略使硬件资源得到合理的运用.但管理策略只是逻辑上的东西,是操作系统自圆其说的一套管理资源的 ...
- android获取电池信息;android获取电池容量、技术、电压、电量、温度等信息
android获取电池信息:android获取电池容量.技术.电压.电量.温度等信息 1.这里我仅展示工具类,需要注意的是这里的部分值要刷新后才能显示,添加刷新UI的方法即可,而且电量温度等都是变化的 ...
- 学习笔记:android下获取sdcard容量大小
在日常开发中,有时候需要对SDCard的容量先进行判断再存储文件.我们可以通过查看android关于setting功能的源代码,以便找到获取sdcard可用空间的方法. 获取sdcard容量大小的主要 ...
- Android 获取电池容量 mAh
1. Java 反射获取电池容量 目前手机出厂下配置电池容量主要是通过修改 power_profile.xml 的电池容量参数,一般Google 默认配置为 1000 mAh 故只要是出货的手机一般都 ...
- C# 利用WMI对象获取物理内存和可用内存大小
下面的代码演示的是使用WMI对象可获取取物理内存和可用内存大小,在使用WMI对象前,先要添加对System.Management的引用,然后就可以调用WMI对象,代码如下: //获取总物理内存大小Ma ...
- 《操作系统真象还原》第五章 ---- 轻取物理内存容量 启用分页畅游虚拟空间 力斧直斩内核先劈一角 闲庭信步摸谈特权级
文章目录 专栏博客链接 相关查阅博客链接 本书中错误勘误 部分缩写熟知 + 小建议 修改代码前的小闲聊 修改loader.S(读取内存大小) 检验是否成功读取内存大小 开始分页新篇章的分页理解 一级页 ...
- android 获取物理内存,Rowhammer:针对物理内存的攻击可以取得 Android 设备的 root 权限...
攻击者确实可以在物理存储单元中实现位翻转来达到侵入移动设备与计算机的目的 研究者们发现了一种新的在不利用任何软件漏洞情况下,利用内存芯片物理设计上的弱点来侵入 Android 设备的方式.这种攻击技术 ...
- 获取OneDrive容量5T及Office365
1.获取临时邮箱 地址1:http://abcda.tech/ 地址2:http://get365.pw/ 在网上右上角申请一个临时的邮箱,并在Office教育版申请地址输入你的临时邮箱账号,点击注册 ...
- 运维前线:一线运维专家的运维方法、技巧与实践3.1 数据中心搬迁准备
第3章 数据中心搬迁中的x86自动化运维 作者简介 吴传玉,具有10年以上x86服务器平台系统管理经验,熟悉Windows及Redhat Linux系列运维,自2008年接触VMware虚拟化产品,获 ...
最新文章
- BC:带你温习并解读《中国区块链技术和应用发展白皮书》—国内外区块链发展现状
- php mysql修改命令_PHP编程:mysql alter table命令修改表结构实例详解
- java字符串的课后作业
- 5分钟看懂微服务架构下的Consul 特性及搭建
- Exploring Pyramids【动态规划——区间DP】
- 我就拜你为师的飞秋爱好者
- Deformable CNNs论文笔记
- 解决dpdk中出现IOMMU not found的问题
- nginx----linux安装
- Anatomy of a Program in Memory
- Ubuntu关闭cups打印机服务
- Windows 更新错误 0x80073712
- oblog商业版本4.6注射漏洞,直接拿管理员
- SVG格式转json文件
- 北京遇上西雅图之不二情书
- Oracle RAC 12.1.0.2 High CPU Usage
- 《Jave并发编程的艺术》学习笔记(1-2章)
- HyperMesh 2D网格划分
- Uniapp苹果登录
- 印度紧盯中国,计划在东海岸建新海军基地
热门文章
- 联想电脑尺寸在哪里看_联想电脑型号怎么查看【详细介绍】
- 广东省计算机设计大赛文档,广东省大学生计算机设计大赛.doc
- Linux:友善之臂FriendlyARM Mini2440用MiniTools通过USB烧写系统失败解决方案
- 为什么vs数据库中文显示问号_oracle中文显示为问号
- 浪潮之巅-读书笔记二
- 保存360锁屏壁纸批量修改文件后缀名
- android 照片裁剪_如何在Android上裁剪和编辑照片
- Google Play上架总结(一)为什么要上架Google Play
- 把两个pdf合并成一个如何解决?
- matlab imshow加画网格,matlab能生成随机行走网格吗? - 仿真模拟 - 小木虫 - 学术 科研 互动社区...