手写操作系统--读写硬盘操作

以下图片均来自王道考研视频我们先来看看硬盘的结构:

 这是物理图上的磁盘构造。

 一个盘片被分成一个个的磁道。

 在划分一下就会出现我们的扇区,每个扇区的大小是固定的,一般来说是512个字节。最内侧的扇区面积小,因此数据密度就密。在我们操作磁盘的时候,一般就以扇区为最小单位。

这样许多个盘片组合起来就是一整个磁盘。下图是一个解剖图:

 这些磁头都是连接在同一个磁臂上的。这样就组成了一个柱子形状,我们称之为柱面。有了上图的基础,我们就能用柱面号,盘面号,扇区号来唯一标识一块磁盘块。其实柱面号也可以理解为我们要指定的磁道是哪一个,也就是王道这句话:

 如果想读写一个磁盘大致的操作分为如下几步:

 我们有这些知识就足够我们理解磁盘读写了。

扇区也就是我们操作磁盘的最小单位,一次最少也要对一个扇区读写,最多能达到256个扇区。

想要操作硬盘读写,我们需要对端口进行输入输出,也就是IO端口形式。

这个端口不同于网络协议中的端口,它是外部设备内部的寄存器,是用来提供一个通道给我们操控这个外部设备的。也称之为端口。因此分配给硬盘的寄存器是需要我们查阅的。

硬盘读写是有两种形式:

-CHS模式  也就是/柱面/磁头/磁道  也就是用坐标系来标识一个磁盘块,这个模式比较复杂,一般不用。

在Linux中,硬件外部设备都被抽象成了文件,硬盘也不例外,每个扇区都可以任务是一个文件,因此产生出了另外一种模式 -LBA模式 /逻辑块地址/ 只需要知道想操作哪一块扇区就完事了很方便

LBA模式也有2种,一种是用28位比特来表示一个扇区,一个是48位比特来表示一个扇区,分别成为LBA28,LBA48,我们只用LBA28模式就好了。

下面我们介绍下硬盘控制器的端口:

 一个Primary通道上是可以挂2个硬盘,一个主盘,一个从盘,但是做实验时我们只有一个主盘因此暂时不管从盘的问题。磁盘是很复杂的东西,我们写操作系统小麻雀的时候,用以上的端口完全足够了。下面介绍下这些端口的作用:

0x1F0 是一个16位的端口,用来读写数据的

0x1F1 检测前一个指令的错误(一般不用,出错的时候用)

0x1F2 用于读写扇区的数量

0x1F3 起始扇区的0-7位

0x1F4 起始扇区的8-15位

0x1F5 起始扇区的16-23位

0x1F6 第四位是存的剩下4位(28比特的最后四位),图示如下:

 0x1F7 此端口是用来存储硬盘执行的命令只介绍三个我们用的:

0xEC 硬盘识别

0x20 读扇区

0x30 写扇区

基本上就介绍完了。下面我们开始上代码(我会给出相应详细的注释):

;第一步
;初始化一些寄存器(数据)
mov edi,0x1000  ;读到内存的哪个位置
mov ecx,2  ;起始扇区
mov bl,4    ;扇区数量
call read_disk  ;调用读扇区这个函数
;第二步实现read_disk函数
read_disk:
     mov dx,0x1f2  ;读写扇区数量,参考前面讲的寄存器
     mov al,bl    ;读取扇区数量放入al寄存器
     out dx,al  ;往端口写

     inc dx ;0x1f3
     mov al,cl ;起始扇区的低八位   
     out dx,al

     inc dx ;0x1f4
     shr ecx,8 ;讲中八位移到低8位的操作 右移操作符
     mov al,cl ;起始扇区的中八位   
     out dx,al

     inc dx ;0x1f5
     shr ecx,8 ;将高八位移到低8位的操作
     mov al,cl ;起始扇区的高八位   
     out dx,al

     inc dx ;0x1f6
     shr ecx,8
     and cl,0b1111 ;将高4位置为0
     
     mov al,0b1110_0000 ;0x1f6的device寄存器 参考上图
     or al,cl ;合二为一
    
     inc dx ;0x1f7
     mov al,0x20    ;读操作
     out dx,al
     
     xor ecx,ecx        ;ecx清0
     mov cl,bl        ;得到读写扇区的数量  循环变量作用于loop
     
     .read:
          push cx
          call .waits ;等待数据  
          call .reads; 读取扇区
          pop cx
          loop .read
      ret
      
      .waits:
           mov dx,0x1f7      ;使用status寄存器 检索硬盘状态
           .check:
               in al,dx       ;往端口读
               jmp $+2   ;空指令
               jmp $+2
               jmp $+2
               and al,0b1000_1000   ;将第4位和第八位清0
               cmp al,0b0000_1000   ;判断DRQ是否准备好数据  cmp指令影响ZF位
               jnz .check           ;如果不等于循环检查 直到DRQ为1为止
           ret
     
       .reads:
             mov dx,0x1f0    ;读硬盘操作
             mov cx,256      ;一次读取一个字 循环变量
             .readw:
                  in ax,dx
                   jmp $+2   ;空指令
                   jmp $+2
                   jmp $+2
                   mov [edi],ax  ;将读取的数据放到0x1000内存位置
                   add edi,2     ;每次加2
                   loop .readw
             ret

效果图可以参考内核加载器那篇文章,用的就是此套函数将加载器加载到0x1000的内存位置,写硬盘逻辑刚好是相反的,目前还没用到,就没有实现此功能。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值