当前位置: 首页 > news >正文

MyOS(四):让内核突破512字节的限制

       我们当前的系统内核,必须包含在虚拟软盘的第1扇区,由于一个扇区只有512字节,因此,系统内核的大小不可能超过512字节。

       但是,一个拥有完善功能的内核不可能只有512字节,因此要想越过512字节的限制,具体的做法就是做一个内核加载器,放入到第一扇区,加载器加载入内存后,再将内核从软盘加载到系统内存,然后,跳转到内核的加载地址


假定我们把编译好的内核代码写入软盘的第1柱面,第2扇区

 

加载器的代码

boot.asm

org  0x7c00;

LOAD_ADDR  EQU  0X8000;当加载器读到内核代码的时候,将内核写入到系统内存0x8000开始的地方


;初始化
entry:
    mov  ax, 0
    mov  ss, ax
    mov  ds, ax
    mov  es, ax
    mov  si, ax


readFloppy:
    mov  CH, 1        ;CH 用来存储柱面号
    mov  DH, 0        ;DH 用来存储磁头号
    mov  CL, 2        ;CL 用来存储扇区号

    mov  BX, LOAD_ADDR  ;ES:BX 数据存储缓冲区

    mov  AH, 0x02      ;AH=02 表示要做的是读盘操作
    mov  AL, 1         ;AL表示要连续读取几个扇区
    mov  DL, 0         ;驱动器编号,一般我们只有一个软盘驱动器,所以写死为0

    INT  0x13          ;调用BIOS中断实现磁盘读取功能
   
    JC  fin ;如果读取出现了错误

    jmp  LOAD_ADDR ;跳转到0x8000就相当于把我们机器的控制权移交到0x8000开始的代码段



fin:
    HLT
    jmp  fin

readFloppy 这段代码从软盘的1柱面,2扇区,将内核读取到系统内存的0x8000处,读取成功后,通过一个jmp 跳转到内核的加载地址,将机器的控制权转交给0x8000开始的代码段

 

内核代码

kernel.asm

org   0x8000 ;告诉编译器我们这段代码将会被加载到0x8000位置。这样编译器在编译的时候,寻址的时候会以0x8000开始

;初始化
entry:
    mov  ax, 0
    mov  ss, ax
    mov  ds, ax
    mov  es, ax
    mov  si, msg


;将msg这句话打印到屏幕上
putloop:
    mov  al, [si]
    add  si, 1
    cmp  al, 0
    je   fin
    mov  ah, 0x0e
    mov  bx, 15
    int  0x10
    jmp  putloop

fin:
    HLT
    jmp  fin

msg:

    DB   "This is Hello World from kernel"

内核代码的作用是将一段话打印到屏幕上

 

下面,我们通过java,将加载器代码写入到主引导扇区,将内核代码写入到第1柱面,第2扇区

OperatingSystem.java
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;


public class OperatingSystem {

    private Floppy floppyDisk = new Floppy();
    private int  MAX_SECTOR_NUM = 18;


    private void writeFileToFloppy(String fileName, boolean bootable, int cylinder,int beginSec) {
        File file = new File(fileName);
        InputStream in = null;

        try {
            in = new FileInputStream(file);
            byte[] buf = new byte[512];
            if (bootable) {
                buf[510] = 0x55;
                buf[511] = (byte) 0xaa;
            }

            while (in.read(buf) > 0) {
                //将内核读入到磁盘第0面,第0柱面,第1个扇区
                floppyDisk.writeFloppy(Floppy.MAGNETIC_HEAD.MAGNETIC_HEAD_0, cylinder, beginSec, buf);
                beginSec++;

                if (beginSec > MAX_SECTOR_NUM) {
                    beginSec = 1;
                    cylinder++;
                }
            }
        } catch(IOException e) {
            e.printStackTrace();
            return;
        }
    }

    public OperatingSystem(String s) {
        writeFileToFloppy(s, true, 0, 1);
    }


    public void makeFllopy()   {
        //把kernel.bin写入到第一柱面,第二扇区
        writeFileToFloppy("kernel.bin", false, 1, 2);

        floppyDisk.makeFloppy("system.img");
    }



    public static void main(String[] args) {
        OperatingSystem op = new OperatingSystem("boot.bin");
        op.makeFllopy();
    }
}
Floppy.java
import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.HashMap;


public class Floppy {
    enum MAGNETIC_HEAD {
        //磁头编号0对应着上面的磁头(读上盘面),1对应着下面的磁头(读下盘面)
        MAGNETIC_HEAD_0,
        MAGETIC_HEAD_1
    };


    public int SECTOR_SIZE = 512;//1个扇区存储容量大小是512字节
    private int CYLINDER_COUNT = 80; //80个柱面
    private int SECTORS_COUNT = 18;//每个柱面都有18个扇区,也就是1个圆圈含有18个扇形
    private MAGNETIC_HEAD magneticHead = MAGNETIC_HEAD.MAGNETIC_HEAD_0;
    private int current_cylinder = 0;
    private int current_sector = 0;

    //HashMap来模拟整个盘片
    //byte[] 对应一个扇区
    //ArrayList对应一个磁道,或者叫一个柱面   一个ArrayList含有18个byte[]
    //外层的ArrayList含有80个里面的子ArrayList
    private HashMap<Integer,ArrayList<ArrayList<byte[]>> > floppy = new HashMap<Integer,ArrayList<ArrayList<byte[]>> >(); //一个磁盘两个面

    public Floppy() {
        //初始化
        initFloppy();
    }

    private void initFloppy() {
        //一个磁盘有两个盘面
        //初始化两个盘面,将2个盘面加入到floppy Hashmap中
        floppy.put(MAGNETIC_HEAD.MAGNETIC_HEAD_0.ordinal(), initFloppyDisk());
        floppy.put(MAGNETIC_HEAD.MAGETIC_HEAD_1.ordinal(), initFloppyDisk());
    }

    //初始化盘面
    private ArrayList<ArrayList<byte[]>> initFloppyDisk() {
        ArrayList<ArrayList<byte[]>> floppyDisk = new ArrayList<ArrayList<byte[]>>(); //磁盘的一个面
        //一个磁盘面有80个柱面
        for(int i = 0; i < CYLINDER_COUNT; i++) {
            floppyDisk.add(initCylinder());
        }

        return floppyDisk;
    }

    private ArrayList<byte[]> initCylinder() {
        //构造一个柱面,一个柱面有18个扇区
        ArrayList<byte[]> cylinder = new ArrayList<byte[]> ();
        for (int i = 0; i < SECTORS_COUNT; i++) {
            byte[] sector = new byte[SECTOR_SIZE];
            cylinder.add(sector);
        }

        return cylinder;
    }

    //读写的时候首先要确定磁头,即确定要读取的盘面是上面还是下面
    public void setMagneticHead(MAGNETIC_HEAD head) {
        magneticHead = head;
    }

    //设置磁道编号0-79
    public void setCylinder(int cylinder) {
        if (cylinder < 0) {
            this.current_cylinder = 0;
        }
        else if (cylinder >= 80) {
            this.current_cylinder = 79;
        }
        else {
            this.current_cylinder = cylinder;
        }
    }

    //确定要访问的扇区编号
    public void setSector(int sector) {
        //sector 编号从1到18
        if (sector < 0) {
            this.current_sector = 0;
        }
        else if (sector > 18) {
            this.current_sector = 18 - 1;
        }
        else {
            this.current_sector = sector - 1;
        }
    }

    //读扇区
    public byte[] readFloppy(MAGNETIC_HEAD head, int cylinder_num, int sector_num) {
        setMagneticHead(head);
        setCylinder(cylinder_num);
        setSector(sector_num);

        ArrayList<ArrayList<byte[]>> disk = floppy.get(this.magneticHead.ordinal());
        ArrayList<byte[]> cylinder = disk.get(this.current_cylinder);

        byte[] sector = cylinder.get(this.current_sector);

        return sector;
    }

    //写扇区
    public void writeFloppy(MAGNETIC_HEAD head, int cylinder_num, int sector_num, byte[] buf) {
        setMagneticHead(head);
        setCylinder(cylinder_num);
        setSector(sector_num);

        ArrayList<ArrayList<byte[]>> disk = floppy.get(this.magneticHead.ordinal());
        ArrayList<byte[]> cylinder = disk.get(this.current_cylinder);

        byte[] buffer = cylinder.get(this.current_sector);
        System.arraycopy(buf, 0, buffer, 0, buf.length);
    }

    public void makeFloppy(String fileName) {
        try {
            /*
                虚拟软盘是二进制文件
                前512*18字节的内容对应盘面0,柱面0的所有扇区内容
                接着的512*18字节对应盘面1,柱面0的所有扇区内容
                再接着的512*18字节对应盘面0,柱面1的所有扇区内容
                再接着的512*18字节对应盘面1,柱面1的所有扇区内容
                以此类推
             */
            DataOutputStream out = new DataOutputStream(new FileOutputStream(fileName));
            for (int cylinder = 0; cylinder < CYLINDER_COUNT; cylinder++) {
                for (int head = 0; head <= MAGNETIC_HEAD.MAGETIC_HEAD_1.ordinal(); head++) {
                    for (int sector = 1; sector <= SECTORS_COUNT; sector++) {
                        byte[] buf = readFloppy(MAGNETIC_HEAD.values()[head], cylinder, sector);

                        out.write(buf);
                    }

                }
            }

        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

 

生成system.img, 用virtualbox打开

成功

 

在这一步的基础上,我们就可以不断得去增加内核的功能,即使超过了512字节,也不用担心

 

参考:

https://blog.csdn.net/tyler_download/article/details/51970921

相关文章:

  • Go语言实现并发(协程)
  • 汇编语言相关知识
  • 面向对象的意义
  • 文本大数据挖掘项目(Go语言)
  • Python网络编程
  • CUDA与cuDNN
  • AI中的搜索(一)——启发式搜索 ((贪婪)最佳优先搜索 (Greedy)Best-First Search、A* 、迭代加深搜索 和 IDA* )
  • AI中的搜索(二)——对抗搜索(最小最大搜索Minimax、Alpha-Beta剪枝搜索、蒙特卡洛树搜索MCTS)
  • Web1.0 与 Web2.0 时代
  • HTTP服务器开发项目(Python)
  • IO多路复用(Select,Poll,Epoll)
  • Reactjs jsx
  • HTTP服务器开发项目之基础知识——传输层TCP协议 应用层HTTP协议(http请求报文应答报文详解)
  • 自制简易浏览器(Python)
  • 编译安装linux内核
  • (三)从jvm层面了解线程的启动和停止
  • echarts的各种常用效果展示
  • Essential Studio for ASP.NET Web Forms 2017 v2,新增自定义树形网格工具栏
  • java8-模拟hadoop
  • js操作时间(持续更新)
  • KMP算法及优化
  • React系列之 Redux 架构模式
  • tweak 支持第三方库
  • vue2.0项目引入element-ui
  • 包装类对象
  • 巧用 TypeScript (一)
  • 使用 Xcode 的 Target 区分开发和生产环境
  • 详解移动APP与web APP的区别
  • 小程序测试方案初探
  • Nginx惊现漏洞 百万网站面临“拖库”风险
  • ​2021半年盘点,不想你错过的重磅新书
  • ​低代码平台的核心价值与优势
  • #Linux(帮助手册)
  • #NOIP 2014#day.2 T1 无限网络发射器选址
  • #设计模式#4.6 Flyweight(享元) 对象结构型模式
  • $NOIp2018$劝退记
  • (三)c52学习之旅-点亮LED灯
  • (十一)JAVA springboot ssm b2b2c多用户商城系统源码:服务网关Zuul高级篇
  • (使用vite搭建vue3项目(vite + vue3 + vue router + pinia + element plus))
  • (四)搭建容器云管理平台笔记—安装ETCD(不使用证书)
  • (正则)提取页面里的img标签
  • (转)chrome浏览器收藏夹(书签)的导出与导入
  • .NET 材料检测系统崩溃分析
  • .Net中的设计模式——Factory Method模式
  • /使用匿名内部类来复写Handler当中的handlerMessage()方法
  • [ vulhub漏洞复现篇 ] Celery <4.0 Redis未授权访问+Pickle反序列化利用
  • [2010-8-30]
  • [BPU部署教程] 教你搞定YOLOV5部署 (版本: 6.2)
  • [BUUCTF NewStarCTF 2023 公开赛道] week3 crypto/pwn
  • [BZOJ 2142]礼物(扩展Lucas定理)
  • [C#]C#学习笔记-CIL和动态程序集
  • [C/C++] C/C++中数字与字符串之间的转换
  • [cocos2d-x]关于CC_CALLBACK
  • [Flutter]WindowsPlatform上运行遇到的问题总结
  • [iOS]如何删除工程里面用cocoapods导入的第三方库