MENU

Catalog

    让系统内核突破512字节的限制

    November 23, 2018 • Read: 233 • 操作系统

    之前做的系统内核,放在虚拟软盘的第一扇区,由于一个扇区只有512字节,因此系统内核的大小无法超过512字节。但是,一个拥有完整功能的内核不可能只有512字节,因此要想越过512字节的限制,具体做法就是做一个内核加载器(大小小于512字节),放入第一扇区,加载器加载入内存后,再将内核从软盘加载到系统内存,最后跳转到内核的加载地址

    假设把编译好的内核代码写入软盘的第一柱面,第2扇区,那么加载器的代码如下(boot.asm)

    org  0x7c00;
    
    LOAD_ADDR  EQU  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
    
    fin:
        HLT
        jmp  fin

    readFloppy这段代码的作用是从软盘的1柱面2扇区,将内核读取到系统内存的0x8000处,读取成功后,通过一个jmp跳转到内核的加载地址,将机器的控制权转交给内核。内核代码如下(kernel.asm)

    org 0x8000
    
    entry:
        mov ax,0
        mov ss,ax
        mov ds,ax
        mov es,ax
        mov es,ax
        mov si,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"

    下载nasm软件对汇编语言进行编译。使用命令分别编译加载器和内核:

    nasm boot.asm -o boot.bat 
    nasm kernel.asm -o kernel.bat

    编译好以后,通过java代码将编译好的内核写入虚拟软盘

    Floppy.java

    import java.io.DataOutputStream;
    import java.io.FileOutputStream;
    import java.util.ArrayList;
    import java.util.HashMap;
    
    public class Floppy {
        enum MAGNETIC_HEAD {
            MAGNETIC_HEAD_0, MAGETIC_HEAD_1
        };
    
        public int SECTOR_SIZE = 512;
        private int CYLINDER_COUNT = 80; // 80个柱面
        private int SECTORS_COUNT = 18;
        private MAGNETIC_HEAD magneticHead = MAGNETIC_HEAD.MAGNETIC_HEAD_0;
        private int current_cylinder = 0;
        private int current_sector = 0;
    
        private HashMap<Integer, ArrayList<ArrayList<byte[]>>> floppy = new HashMap<Integer, ArrayList<ArrayList<byte[]>>>(); // 一个磁盘两个面
    
        public Floppy() {
            initFloppy();
        }
    
        private void initFloppy() {
            // 一个磁盘有两个盘面
            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;
        }
    
        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 {
                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) {
                e.printStackTrace();
            }
        }
    }

    OPeratingSystem.java

    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    
    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() {
            writeFileToFloppy("kernel.bat", false, 1, 2);
            floppyDisk.makeFloppy("system.img");
        }
    
        public static void main(String[] args) {
            OperatingSystem op = new OperatingSystem("boot.bat");
            op.makeFllopy();
        }
    }

    运行的是OperatingSystem.java代码。将生成的虚拟软盘文件system.img加载到虚拟机中启动,就可以看到如下效果

    Archives Tip
    QR Code for this page
    Tipping QR Code
    Leave a Comment

    已有 1 条评论
    1. Capella Capella

      @(大拇指)