java开发系统内核:实现基于FAT12文件系统的dir命令

更详细的讲解和代码调试演示过程,请参看视频
Linux kernel Hacker, 从零构建自己的内核

如果你对机器学习感兴趣,请参看一下链接:
机器学习:神经网络导论

本节要实现的控制台命令是dir, 熟悉该命令的同学都了解,它的作用是列举出当前目录下的文件信息。问题在于,我们当前的操作系统根本没有硬盘,更没有文件系统,那么这个命令列举的文件从哪里来呢?由于我们的系统内核是存储在软盘上的,因此,我们直接把软盘当做系统硬盘,该命令列举的是存储在虚拟软盘上的文件。假设我们在虚拟软盘上存储了两个文件,分别为abc.exe, efg.sys,文件的大小分别为256字节和128字节,运行该命令后,控制台显示结果如下图:

这里写图片描述

我们先看看FAT12文件系统是如何记录文件信息的。每个存储在FAT12系统上的文件都有一个文件目录,用于存储文件的基本信息,这个目录的数据结构如下:

struct FILEINFO {
    unsigned char name[8], ext[3], type;
    char  reserve[10];
    unsigned short time, date, clustno;
    unsigned int  size;
};

这个结构体的头8个字节对应name,也就是文件名,也就是说存储的文件,它的名称绝不能对于8个字符。接下来三个字节对应的是文件扩展名,第12个字节,也就是type, 它对应的是文件的类型,它的值意义如下:
0x01 只读文件
0x02 隐藏文件
0x04 系统文件
0x08 非文件信息
0x10 目录。

接下来的10字节为保留,这是微软规定的。跟着的两个字节,首先是time 和 date 用于表示文件的生成时间,最后4个字节就是文件的大小。

当前,我们的虚拟软盘所存储的信息,其布局是这样的,最开始的512字节是引导扇区代码,接下来存储的就是系统内核,然后跟着是FAT12文件系统对应的文件目录,也就是上面描述的数据结构,存了几个文件,就有几个FILEINFO数据结构。由此先看看,我们当前的内核到底有多大,这样我们才能计算出文件目录在软盘中的位置,进而确定它加载到内存后,所对应的位置。

运行生成虚拟软盘的java工程,通过输出,我们可以看到,当前系统内核的大小总共是71个扇区,每个扇区大小是512字节,因此内核的总大小是71*512=0x8E00 字节。我们的内核加载到内存时,是从起始地址0x8000开始的,于是内核在内存中的末尾地址就是 0x10E00 = 0x8000 + 0x8E00, 由于我们把文件的目录信息直接跟在内核末尾的,因此文件目录信息的起始地址就是0x10E00.

接着我们看看,文件目录信息是如何写到虚拟磁盘上的:

import java.nio.ByteBuffer;


public class FileHeader {
    private byte[] header = new byte[32];
    
    public void setFileName(String s) {
        int len = s.length() > 8 ? 8 : s.length();
        for (int i = 0; i < len; i++) {
            header[i] = (byte)s.charAt(i);
        }
    }
    
    public void setFileExt(String s) {
        int len = s.length() > 3 ? 3 : s.length();
        for (int i = 0; i < len; i++) {
            header[8+i] = (byte)s.charAt(i);
        }
    }
    
    public void setFileType(Byte t) {
        header[11] = t;
    }
    
    public void setFileTime(byte[] time) {
        header[22] = time[0];
        header[23] = time[1];
    }
    
    public void setFileDate(byte[] date) {
        header[24] = date[0];
        header[25] = date[1];
    }
    
    public void setFileClusterNo(byte[] no) {
        header[26] = no[0];
        header[27] = no[1];
    }
    
    public void setFileSize(int size) {
        byte[] buf = ByteBuffer.allocate(4).putInt(size).array();
        for (int i = 0; i < 4; i++) {
            header[28+i] = buf[3 - i];
        }
        
        
    }
    
    public byte[] getHeaderBuffer() {
        return header;
    }
    
    
}

一个FileHeader 类用来表示一个文件目录,它对应前头提到的FILEINFO数据结构,它提供了几个接口,用来设置文件名,扩展名等相关信息。


public class DiskFileSystem {
    private Floppy floppyWriter;
    private int beginSec;
    private int fileHeaderCount = 0;
    private byte[] buffer = new byte[512];
    private int cylinder = 0;
    
    public DiskFileSystem(Floppy disk, int  cylinder, int sec) {
        this.floppyWriter = disk;
        this.beginSec = sec;
        this.cylinder = cylinder;
    }
    
    public void addHeader(FileHeader header) {
        if (fileHeaderCount >= 16) {
            flashFileHeaders();
            fileHeaderCount = 0;
            buffer = new byte[512];
            beginSec++;
        }
        
        byte[] headerBuf = header.getHeaderBuffer();
        for (int i = 0; i < 32; i++) {
            buffer[fileHeaderCount * 32 + i] = headerBuf[i];
        }
        
        fileHeaderCount++;
    }
    
    public void flashFileHeaders() {
        floppyWriter.writeFloppy(Floppy.MAGNETIC_HEAD.MAGNETIC_HEAD_0 , cylinder, beginSec, buffer);
    }
}

DiskFileSystem 用于把文件目录结构写入到磁盘的指定地方。该类的初始函数需要传入几个参数,disk 表示虚拟磁盘,cylinder 表示文件目录所要写入的柱面,sec 表示所在柱面的起始扇区。

这里写图片描述

上图表示的是内核写入到磁盘的情况,可以看到,内核最后写入到虚拟软盘的第4柱面,第17扇区。由于我们的文件目录紧跟着内核存储,因此,我们的文件目录要写入到第4柱面,第18扇区,于是我们在生成虚拟软盘时,在指定位置写下文件目录:

 public void makeFllopy()   {
        writeFileToFloppy("kernel.bat", false, 1, 1);
        
        //test file system
        
        DiskFileSystem fileSys = new DiskFileSystem(floppyDisk, 4, 18);
        FileHeader header = new FileHeader();
        header.setFileName("abc");
        header.setFileExt("exe");
        byte[] date = new byte[2];
        date[0] = 0x11;
        date[1] = 0x12;
        header.setFileTime(date);
        header.setFileDate(date);
        header.setFileSize(256);
        fileSys.addHeader(header);
        
        header = new FileHeader();
        header.setFileName("efg");
        header.setFileExt("sys");
        header.setFileSize(128);
        fileSys.addHeader(header);
        
        fileSys.flashFileHeaders();
        
        //test file system
        
        floppyDisk.makeFloppy("system.img");
    }

上面代码先创建了两个文件目录,这两个文件名分别为abc.exe 和 efg.sys, 这两个文件是虚拟的,我们只在磁盘上构造它们的目录,在磁盘上并没有这两个文件的实际数据。磁盘上有了文件目录后,我们要编写内核代码,让内核能够读取显示相关信息。

首先在global_define.h添加如下代码:

#define  ADR_DISKIMG  0x10E00

struct FILEINFO {
    unsigned char name[8], ext[3], type;
    char  reserve[10];
    unsigned short time, date, clustno;
    unsigned int  size;
};

其中ADR_DISKIMG 就是文件目录在内存中的起始地址,FILEINFO对应的就是我们前头提到过的FAT12文件系统对应的文件目录信息。当我们在控制台输入命令dir 后,控制台从指定位置,把FILEINFO结构数据读取出来,并把对应的文件名和文件大小显示出来,代码如下,在write_vga_desktop.c中:

void console_task(struct SHEET *sheet, int memtotal) {
....
struct FILEINFO* finfo = (struct FILEINFO*)(ADR_DISKIMG);

    for(;;) {
    ....
    else if (strcmp(cmdline, "dir") == 1) {
                       while (finfo->name[0] != 0) {

                       char s[13];
                       s[12] = 0;
                       int k;
                       for (k = 0; k < 8; k++) {
                           if (finfo->name[k] != 0) {
                               s[k] = finfo->name[k]; 
                           }else {
                               break;
                           }
                       }

                       int t = 0;
                       s[k] = '.';
                       k++;
                       for (t = 0; t < 3; t++) {
                           s[k] = finfo->ext[t];
                           k++;
                       }
                       
                       showString(shtctl, sheet, 16, cursor_y, COL8_FFFFFF, s);
                       int offset = 16 + 8*15;
                       char* p = intToHexStr(finfo->size);
                       showString(shtctl, sheet, offset, cursor_y, COL8_FFFFFF, p);
                       cursor_y = cons_newline(cursor_y, sheet);
                       finfo++;

                      }
    ....
    }
....
}

当控制台收到dir命令时,它先从地址ADR_DISKIMG开始,读取第一个文件目录信息,如果文件名的起始第一个字符不是0,那么表明接下来的32字节表示有效的文件目录信息,于是代码先获取文件名,然后获取文件扩展名,最后得到文件大小,然后依次把这些信息显示到控制台上。接着越过32字节,再判断接下来的32个字节是否表示有效的文件目录信息,如果是,再按照原有逻辑,继续显示相关信息。

我们在虚拟软盘中写入了两个文件目录信息,因此运行该命令后,控制台正确显示了相关文件的目录信息。

本节内容稍微有点烧脑,请参看视频,以便获得更详细的讲解和代码调试演示。

欢迎关注公众号,让我们一起学习,交流,成长:


文章公众号.jpg
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,444评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,421评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,363评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,460评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,502评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,511评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,280评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,736评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,014评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,190评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,848评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,531评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,159评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,411评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,067评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,078评论 2 352

推荐阅读更多精彩内容

  • 21.1文件系统的概念 21.1.1文件系统和文件 ■文件系统是操作系统中管理持久性数据的子系统,提供数据存储和访...
    龟龟51阅读 723评论 0 4
  • Ubuntu的发音 Ubuntu,源于非洲祖鲁人和科萨人的语言,发作 oo-boon-too 的音。了解发音是有意...
    萤火虫de梦阅读 99,226评论 9 467
  • linux资料总章2.1 1.0写的不好抱歉 但是2.0已经改了很多 但是错误还是无法避免 以后资料会慢慢更新 大...
    数据革命阅读 12,154评论 2 33
  • 早上我想起来背书,但是不需要扫地,需要做饭,需要上厕所需要把孩子叫醒,需要陪着孩子一起背书,也需要背自己的书。我需...
    105d45b91b02阅读 737评论 0 1
  • 学会用git了,记录一下,以后好参考。 创建项目文件夹,然后git init生成仓库 添加文件,git add增加...
    银河星海阅读 194评论 0 0