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