- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
学习操作系统原理最好的方法是自己写一个简单的操作系统.
本讲代码文件为boot.asm,要读取的文件为data.txt.
在GrapeOS中用到的文件少且小,所有文件都放在了根目录下,数量不会超过16个,占用的簇不会超过254个。所以读取目录项只需要读取根目录的第1个扇区即可,读取FAT表项也只需读取FAT1表的第1个扇区即可。 以下是读取文件的流程图:
boot.asm中的代码如下:
;--------------------定义常量--------------------
;FAT16目录项中各成员的偏移量:
;名称 偏移 长度 描述
DIR_Name equ 0 ;11 文件名8B,扩展名3B
DIR_Attr equ 11 ;1 目录项属性
;保留位 12 10 保留位
DIR_WrtTime equ 22 ;2 最后一次写入时间
DIR_WrtDate equ 24 ;2 最后一次写入日期
DIR_FstClus equ 26 ;2 起始簇号
DIR_FileSize equ 28 ;4 文件大小
BOOT_ADDRESS equ 0x7c00 ;boot程序加载到内存的地址。
FILE_ADDRESS equ 0x1000 ;文件读到内存中的地址。
DISK_BUFFER equ 0x7e00 ;读磁盘临时存放数据用的缓存区,放到boot程序之后。
DISK_SIZE_M equ 4 ;磁盘容量,单位M。
FAT1_SECTORS equ 32 ;FAT1占用扇区数
ROOT_DIR_SECTORS equ 32 ;根目录占用扇区数
SECTOR_NUM_OF_FAT1_START equ 1 ;FAT1表起始扇区号
SECTOR_NUM_OF_ROOT_DIR_START equ 33 ;根目录区起始扇区号
SECTOR_NUM_OF_DATA_START equ 65 ;数据区起始扇区号,对应簇号为2。
SECTOR_CLUSTER_BALANCE equ 63 ;簇号加上该值正好对应扇区号。
FILE_NAME_LENGTH equ 11 ;文件名8字节加扩展名3字节共11字节。
DIR_ENTRY_SIZE equ 32 ;目录项为32字节。
DIR_ENTRY_PER_SECTOR equ 16 ;每个扇区能存放目录项的数目。
;--------------------MBR开始--------------------
org BOOT_ADDRESS
jmp boot_start
nop
;FAT16参数区:
BS_OEMName db 'GrapeOS ' ;厂商名称(8字节,含空格)
BPB_BytesPerSec dw 0x0200 ;每扇区字节数
BPB_SecPerClus db 0x01 ;每簇扇区数
BPB_RsvdSecCnt dw 0x0001 ;保留扇区数(引导扇区的扇区数)
BPB_NumFATs db 0x01 ;FAT表的份数
BPB_RootEntCnt dw 0x0200 ;根目录可容纳的目录项数
BPB_TotSec16 dw 0x2000 ;扇区总数(4MB)
BPB_Media db 0xf8 ;介质描述符
BPB_FATSz16 dw 0x0020 ;每个FAT表扇区数
BPB_SecPerTrk dw 0x0020 ;每磁道扇区数
BPB_NumHeads dw 0x0040 ;磁头数
BPB_hiddSec dd 0x00000000 ;隐藏扇区数
BPB_TotSec32 dd 0x00000000 ;如果BPB_TotSec16是0,由这个值记录扇区数。
BS_DrvNum db 0x80 ;int 13h的驱动器号
BS_Reserved1 db 0x00 ;未使用
BS_BootSig db 0x29 ;扩展引导标记
BS_VolID dd 0x00000000 ;卷序列号
BS_VolLab db 'Grape OS ';卷标(11字节,含空格)
BS_FileSysType db 'FAT16 ' ;文件系统类型(8字节,含空格)
;通过以上参数可知硬盘容量为4MB,共8K个扇区。扇区具体分布如下:
;区域名 扇区数 扇区号 字节偏移 说明
;引导扇区 1个扇区 扇区0 0x0000~0x01ff
;FAT1表 32个扇区 扇区1~32 0x0200~0x41ff 可记录8K-2个簇
;FAT2表 无 无 无 无
;根目录区 32个扇区 扇区33~64 0x4200~0x81ff 可容纳512个目录项
;数据区 8127个扇区 扇区65~0x1fff 0x8200~0x3fffff
;--------------------程序开始--------------------
boot_start:
;初始化寄存器
mov ax,cs
mov ds,ax
mov es,ax ;cmpsb会用到ds:si和es:di
;读取文件开始
;读取根目录的第1个扇区(1个扇区可以存放16个目录项,我们用到的文件少,不会超过16个。)
mov esi,SECTOR_NUM_OF_ROOT_DIR_START
mov di,DISK_BUFFER
call func_read_one_sector
;在16个目录项中通过文件名查找文件
cld ;cld将标志位DF置0,在串处理指令中控制每次操作后让si和di自动递增。std相反。下面repe cmpsb会用到。
mov bx,0 ;用bx记录遍历第几个目录项。
next_dir_entry:
mov si,bx
shl si,5 ;乘以32(目录项的大小)
add si,DISK_BUFFER ;源地址指向目录项中的文件名。
mov di,read_file_name_string ;目标地址指向文件在硬盘中的正确文件名。
mov cx,FILE_NAME_LENGTH ;字符比较次数为FAT16文件名长度,每比较一个字符,cx会自动减一。
repe cmpsb ;逐字节比较ds:si和es:di指向的两个字符串。
jcxz file_found ;当cx为0时跳转,cx为0表示上面比较的两个字符串相同。找到了文件。
inc bx
cmp bx,DIR_ENTRY_PER_SECTOR
jl next_dir_entry ;检查下一个目录项。
jmp file_not_found ;没有找到文件。
file_found: ;找到了文件
;从目录项中获取文件的起始簇号
shl bx,5 ;乘以32
add bx,DISK_BUFFER
mov bx,[bx+DIR_FstClus] ;文件的起始簇号
;读取FAT1表的第1个扇区(我们用到的文件少且小,只用到了该扇区中的簇号。)
mov esi,SECTOR_NUM_OF_FAT1_START
mov di,DISK_BUFFER ;放到boot程序之后
call func_read_one_sector
mov bp,FILE_ADDRESS ;文件内容读取到内存中的起始地址
;按簇号读文件内容
read_file:
xor esi,esi ;esi清零
mov si,bx ;簇号
add esi,SECTOR_CLUSTER_BALANCE
mov di,bp
call func_read_one_sector
add bp,512 ;下一个目标地址
;获取下一个簇号(每个FAT表项为2字节)
shl bx,1 ;乘2,每个FAT表项占2个字节
mov bx,[bx+DISK_BUFFER]
;判断下一个簇号
cmp bx,0xfff8 ;大于等于0xfff8表示文件的最后一个簇
jb read_file ;jb无符号小于则跳转,jl有符号小于则跳转。
read_file_finish: ;读取文件结束
jmp stop
file_not_found: ;没有找到文件
stop:
hlt
jmp stop
;读取硬盘1个扇区(主硬盘控制器主盘)
;输入参数:esi,ds:di。
;esi LBA扇区号
;ds:di 将数据写入到的内存起始地址
;输出参数:无。
func_read_one_sector:
;第1步:检查硬盘控制器状态
mov dx,0x1f7
.not_ready1:
nop ;nop相当于稍息 hlt相当于睡觉
in al,dx ;读0x1f7端口
and al,0xc0 ;第7位为1表示硬盘忙,第6位为1表示硬盘控制器已准备好,正在等待指令。
cmp al,0x40 ;当第7位为0,且第6位为1,则进入下一个步。
jne .not_ready1 ;若未准备好,则继续判断。
;第2步:设置要读取的扇区数
mov dx,0x1f2
mov al,1
out dx,al ;读取1个扇区
;第3步:将LBA地址存入0x1f3~0x1f6
mov eax,esi
;LBA地址7~0位写入端口0x1f3
mov dx,0x1f3
out dx,al
;LBA地址15~8位写入端口写入0x1f4
shr eax,8
mov dx,0x1f4
out dx,al
;LBA地址23~16位写入端口0x1f5
shr eax,8
mov dx,0x1f5
out dx,al
;第4步:设置device端口
shr eax,8
and al,0x0f ;LBA第24~27位
or al,0xe0 ;设置7~4位为1110,表示LBA模式,主盘
mov dx,0x1f6
out dx,al
;第5步:向0x1f7端口写入读命令0x20
mov dx,0x1f7
mov al,0x20
out dx,al
;第6步:检测硬盘状态
.not_ready2:
nop ;nop相当于稍息 hlt相当于睡觉
in al,dx ;读0x1f7端口
and al,0x88 ;第7位为1表示硬盘忙,第3位为1表示硬盘控制器已准备好数据传输。
cmp al,0x08 ;当第7位为0,且第3位为1,进入下一步。
jne .not_ready2 ;若未准备好,则继续判断。
;第7步:从0x1f0端口读数据
mov cx,256 ;每次读取2字节,一个扇区需要读256次。
mov dx,0x1f0
.go_on_read:
in ax,dx
mov [di],ax
add di,2
loop .go_on_read
ret
read_file_name_string:db "DATA TXT",0 ;要读取的文件在硬盘中存储的文件名,共11个字节,含空格。
times 510-($-$$) db 0
db 0x55,0xaa
关于代码的讲解基本都写在注释中了,结合之前讲的内容,大家应该能看懂.
本讲要读取的文件是data.txt,如何将该文件复制到虚拟硬盘的FAT16文件系统中呢?我们这里采用的方法是将该虚拟硬盘挂载到Linux系统上,然后就可以将data.txt复制到虚拟硬盘中了。前提是需要先将虚拟硬盘格式化,格式化的方法就是将boot程序写入到虚拟硬盘的第一个扇区。因为boot程序中含有FAT16的结构化数据,Linux系统就知道如何读写该文件系统了.
dd if=/dev/zero of=/media/VMShare/GrapeOS.img bs=1M count=4
nasm boot.asm -o boot.bin
dd if=boot.bin of=/media/VMShare/GrapeOS.img conv=notrunc
截图如下:
mount /media/VMShare/GrapeOS.img /mnt/ -t msdos -o loop
ll /mnt/
cp data.txt /mnt/
sync #数据同步,立马把数据写入硬盘。
ll /mnt/
umount /mnt/
截图如下:
上图中在复制完data.txt后,通过 ll /mnt/ 查看虚拟硬盘根目录,此时虽然看到的文件名是小写“data.txt”,但实际上在虚拟硬盘里存储的文件名已经是全部大写的了,在下面的分析中可以看到.
通过hexdump查看虚拟硬盘数据:
hexdump /media/VMShare/GrapeOS.img -C
截图如下:
截图中的部分数据如下:
000001f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 aa |..............U.|
00000200 00 00 00 00 00 00 04 00 05 00 ff ff 00 00 00 00 |................|
00000210 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00004200 44 41 54 41 20 20 20 20 54 58 54 20 00 00 00 00 |DATA TXT ....|
00004210 00 00 00 00 00 00 fa 4e 78 56 03 00 58 04 00 00 |.......NxV..X...|
00004220 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
前面我们计算过,FAT1表的起始扇区是扇区1,字节偏移是0x200,根目录区的起始扇区是扇区33,字节偏移是0x4200。 从上面的截图和数据可以看到:
在cmd命令行中启动QEMU的调试模式:
C:\Users\CYJ>qemu-system-i386 d:\GrapeOS\VMShare\GrapeOS.img -S -s
在Linux命令行中启动GDB:
[root@CentOS7 Lesson23]# gdb
(gdb) target remote 你的Windows的IP地址:1234
(gdb) b *0x7c00
(gdb) c
(gdb) x /32xb 0x1000 #在读文件前查看此时0x1000处的内存数据,可以看到都是0。
0x1000: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x1008: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x1010: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x1018: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
(gdb) c
运行几秒,然后Ctrl键+C键暂停运行,此时读取文件的程序已运行完毕。
(gdb) x /32xb 0x1000 #在读完文件后查看此时0x1000处的内存数据,可以看到已经不是0了。
0x1000: 0x78 0x38 0x36 0x20 0x28 0x61 0x6c 0x73
0x1008: 0x6f 0x20 0x6b 0x6e 0x6f 0x77 0x6e 0x20
0x1010: 0x61 0x73 0x20 0x38 0x30 0x78 0x38 0x36
0x1018: 0x20 0x6f 0x72 0x20 0x74 0x68 0x65 0x20
(gdb) x /32c 0x1000 #为了方便观察可以以字符形式展示数据。通过对比,下面的32个字符的确和data.txt中前32个字符相同。
0x1000: 120 'x' 56 '8' 54 '6' 32 ' ' 40 '(' 97 'a' 108 'l' 115 's'
0x1008: 111 'o' 32 ' ' 107 'k' 110 'n' 111 'o' 119 'w' 110 'n' 32 ' '
0x1010: 97 'a' 115 's' 32 ' ' 56 '8' 48 '0' 120 'x' 56 '8' 54 '6'
0x1018: 32 ' ' 111 'o' 114 'r' 32 ' ' 116 't' 104 'h' 101 'e' 32 ' '
(gdb) x /32c 0x1440 #查看文件的最后二十多个字符。通过对比可以看到和data.txt中的相同。
0x1440: 105 'i' 116 't' 115 's' 32 ' ' 90 'Z' 105 'i' 108 'l' 111 'o'
0x1448: 103 'g' 32 ' ' 90 'Z' 45 '-' 56 '8' 48 '0' 32 ' ' 118 'v'
0x1450: 97 'a' 114 'r' 105 'i' 97 'a' 110 'n' 116 't' 41 ')' 46 '.'
0x1458: 0 '\000' 0 '\000' 0 '\000' 0 '\000' 0 '\000' 0 '\000' 0 '\000' 0 '\000'
通过上述演示说明读取文件成功.
视频版地址: https://www.bilibili.com/video/BV1xN411K7Lc/ 配套的代码与资料在: https://gitee.com/jackchengyujia/grapeos-course GrapeOS操作系统交流QQ群:643474045 。
最后此篇关于自己动手从零写桌面操作系统GrapeOS系列教程——23.从硬盘读取文件的文章就讲到这里了,如果你想了解更多关于自己动手从零写桌面操作系统GrapeOS系列教程——23.从硬盘读取文件的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
这显然有可能导致一场激烈的 war ,但无论如何,我试一试……(目前)还没有任何明确的答案。 在我的一台机器上,我确实被切换/升级到 Matlab 2012b。这是一个漂亮的新桌面,好吧。但出于很多原
当我的 Docker 容器启动时,我收到以下通知: Docker Desktop has detected that you shared a Windows file into a WSL 2 co
我希望能够(从服务器)连接到远程 PC 并能够访问其文件。我正在尝试使用 PHP 或 JavaScript 来完成此操作。 所以我想知道 1) 是否可能以及 2) 如何做到。 有人有任何示例/解决方案
我想使用 DirectX 为 Windows 7 制作动画桌面背景。我正在使用 C#、SlimDX 和几个 Windows API 函数的 P/Invoke 导入。我对 native Windows
是否可以为 Mac 开发动态桌面?我所说的“动态”是指其图像采用系统参数(例如时间和作业信息)并使用此信息来更新图像的桌面。有关图像状态的信息也将在 session 之间保留。我不是在谈论电影或随机但
我正在 LibGDX 中构建一个仅限桌面的应用程序。该游戏内置了一个 map 编辑器,您可以切换到并更改游戏 map 。我想添加一个功能,如果用户单击关闭窗口并且有未保存的编辑,它会提示您是否要在关闭
有没有像样的Windows桌面JSON lines (JSONL)格式查看器? 这种格式也称为: 行分隔的 JSON (LDJSON) 换行符分隔的 JSON (NDJSON) 我正在寻找至少能够从
我有一个UITableView,它在单元格中显示大量图像,并且我对滚动性能并不满意。我的 UITableView 类似于 iPhone 上的照片应用程序。有谁知道为什么 iphone 照片应用程序滚动
let img1 = document.createElement('img'); img1.setAttribute('src', 'https://caniuse.com/img/browsers
我在同一窗体上有两个不同的网格控件。它们共享相同的上下文菜单。当我选择上下文菜单项时,我无法确定哪个控件是所有者。 最佳答案 ContextMenuStrip 类的 SourceControl 属性将
我已经安装了 Docker Desktop ( version : 2.3.0.4 ) 并启用了 Kubernetes . 我部署了几个 PODS,一切正常,从昨天开始,我遇到了下面提到的一个奇怪的问
我正在制作需要实现TableView的应用程序。 我想将背景图像应用到桌面 View 。 谁能帮我解决这个问题。我正在使用下面的代码来创建 TableView Titanium.UI.setBackg
我在同一窗体上有两个不同的网格控件。它们共享相同的上下文菜单。当我选择上下文菜单项时,我无法确定哪个控件是所有者。 最佳答案 ContextMenuStrip 类的 SourceControl 属性将
您能建议一些库在我的桌面 Java 应用程序中创建雷达图吗? 谢谢 最佳答案 似乎JFreeChart提供了SpiderWebPlot 。 images for which看起来像雷达图。 关于jav
这个问题已经有答案了: 已关闭10 年前。 Possible Duplicate: Not possible to launch a file on a network using Java Desk
就目前情况而言,这个问题不太适合我们的问答形式。我们希望答案得到事实、引用资料或专业知识的支持,但这个问题可能会引发辩论、争论、民意调查或扩展讨论。如果您觉得这个问题可以改进并可能重新开放,visit
我想知道是否可以(使用JAVA)识别用于发出请求的计算机类型,例如:服务器、台式机、PDA(平板电脑、手机等)? 谢谢! 最佳答案 取决于您使用什么来接受请求。对于 http 请求,信息位于请求 he
我添加了一个事件处理程序,用于滚动到我的桌面 View ,但它仅在我使用滚轮滚动时才起作用,但我也需要它在您单击并拖动滚动条时起作用。任何想法有什么问题吗?我尝试使用不同的方法来处理该事件,但没有一个
我想开始开发桌面 Java 应用程序,并且我想知道 Java 社区使用什么。 我从 Swing 开始,但发现它非常初级。 我习惯于在 Visual Studio 2010 中使用 .net c# WP
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于 Stack Overflow 来说是偏离主题的,
我是一名优秀的程序员,十分优秀!