30天自制操作系统——第十八天运行应用程序

本文介绍在自制操作系统中实现读取文件内容和运行应用程序的步骤。首先讲解了如何根据扇区号计算磁盘映像中的文件地址,并介绍了文件系统的基本概念,如FAT表。接着,展示了如何读取文件内容,以及在操作系统中运行一个简单的CLI应用程序,该程序在执行后会禁止中断处理,导致系统看似死机。

读取文件内容

我们在运行应用程序之前,先来实现读取文件内容的功能吧。

在Windows的命令行窗口中也有这样的功能,当我们输入“type 文件名”时,就会显示文件的内容。

在这里插入图片描述

定义一个文件信息结构:

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

其中clustno变量,代表文件从哪个扇区号开始存放。

我们可以根据下面的公式,根据扇区号计算出文件在磁盘映像中的地址:
磁盘映像中的地址=clustno∗512+0x003e00 磁盘映像中的地址 = clustno*512 +0x003e00 =clustno512+0x003e00

这里有的同学会好奇,最后为什么是加上0x003e00呢?

这个放在后面说明,我们先用这个公式把文件中的内容读取出来。

bootpack.c节选:

/* 判断是否是type命令 */
if (cmdline[0] == 't' && cmdline[1] == 'y' && cmdline[2] == 'p' &&  cmdline[3] == 'e' && cmdline[4] == ' ')	
	/* s[0~10]用空格填充,用于存放文件名 */
    for (y = 0; y < 11; y++) {
	s[y] = ' ';
	}
	y = 0;
     /* 读取cmdline[5~] 复制到 s[0~] */
	for (x = 5; y < 11 && cmdline[x] != 0; x++) {
        /* 遇到“.”接下来就是扩展名 */
		if (cmdline[x] == '.' && y <= 8) {
			y = 8;
		} else {
			s[y] = cmdline[x];
            /* 将小写字母转换成大写字母 */
			if ('a' <= s[y] && s[y] <= 'z') {				
				s[y] -= 0x20;
			} 
			y++;
		}
	}
	/* 开始寻找与输入的文件名相同的文件 */
	for (x = 0; x < 224; ) {
		if (finfo[x].name[0] == 0x00) {
			break;
		}
	if ((finfo[x].type & 0x18) == 0) {
		for (y = 0; y < 11; y++) {
			if (finfo[x].name[y] != s[y]) {
				goto type_next_file;
			}
		}
		break; /* 找到文件了 */
	}
		x++;
}
	/* 找到了与输入文件名相同的文件 */
	if (x < 224 && finfo[x].name[0] != 0x00) {			
		y = finfo[x].size;
        /* 公式:磁盘映像中的地址 = clustno*512 +0x003e00 */
		p = (char *) (finfo[x].clustno * 512 + 0x003e00 + ADR_DISKIMG);
		cursor_x = 8;
		for (x = 0; x < y; x++) {
			/* 逐字输出来 */
			s[0] = p[x];
			s[1] = 0;
			putfonts8_asc_sht(sheet, cursor_x, cursor_y, COL8_FFFFFF, COL8_000000, s, 1);
			cursor_x += 8;
			if (cursor_x == 8 + 240) {
				cursor_x = 8;
				cursor_y = cons_newline(cursor_y, sheet);
			}
		}
	 } else {
		/* 没有找到与输入文件名相同的文件 显示File not found*/
		putfonts8_asc_sht(sheet, 8, cursor_y, COL8_FFFFFF, COL8_000000, "File not found.", 15);
		cursor_y = cons_newline(cursor_y, sheet);
	 }

运行一下,使用type命令读取make.bat文件——

在这里插入图片描述

增加文件系统

Windows系统管理磁盘的时候,文件并不是保存在连续的扇区里的,大于512字节(一个扇区是512字节)的文件常常被保存在不同的扇区里。

这种没有存放在连续扇区的情况,被称为“磁盘碎片”。平常我们会使用一些磁盘整理的工具,就是将这些不连续的扇区重新排列,减少磁盘碎片的情况。

那么不在连续的扇区,怎么知道文件的下一段保存在哪呢?

文件下一段保存的位置在磁盘中是有记录的,而这个记录就叫做FAT。

FAT(file allocation table),也叫做文件配置表,在磁盘映像中0x0020(十进制512)~ 0x0013ff(十进制5119)的位置,正好占9个扇区,它专门用来记录文件在磁盘中存放的位置。


常见的操作系统Windows、Linux都有自己的文件系统,文件系统简单的说就是在存储设备上组织文件的方法。目前我们制作做的操作系统,还没有自己的文件系统呢。

我们来看看Windows操作系统的文件系统有哪些呢——

Windows操作系统常见的文件系统格式有FAT12、FAT16、FAT32、NTFS、exFAT等,其中FAT12是专门为软盘设计的。

FAT12文件系统格式:在这里插入图片描述

上表中可以看出软盘里有两个FAT,分别是FAT文件分配表1和FAT文件分配表2,两者是相互备份的关系,数据内容完全一致。

FAT表从0号开始进行编号,其中0号地址与1号地址被系统保留用于存储特殊标志内容,从2号开始,每个地址对应数据区的簇号。

FAT12每个文件内容逻辑上是以链表的形式组织的:

在这里插入图片描述

这里我们来说明开头提到的计算磁盘映像地址的公式,最后加0x003e00的原因。
磁盘映像中的地址=clustno∗512+0x003e00 磁盘映像中的地址 = clustno*512 +0x003e00 =clustno512+0x003e00
原来是因为FAT表前两位0和1号去干其他事了,从2号开始才对数据区进行编号。根据第三节的内容(不记得的同学往回翻一下),文件的内容会写在0x004200以后的地方,即数据区的起始位置是0x004200,而2*512 + 0x003e00 = 0x004200,所以最后是加0x003e00。

FAT12的每个表项都占用12bit(1.5字节),这也就是FAT12名字的由来,而FAT16、FAT32也是这么来的。

这里有同学会好奇,为什么是1.5字节呢?

正常情况下,使用WORD需要两个字节保存一个扇区号,而软盘中的FAT是经过压缩的。经过压缩后3个字节可以保存2个扇区号,所以保存一个扇区号的空间就变为1.5字节。所以在读取FAT文件时,要先进行解压缩。

一个FAT是9个扇区,不压缩的话就占12个扇区,一共两个FAT,总共也就节省了6个扇区的空间,这么压缩、解压缩的折腾,个人感觉意义不大。

bootpack.节选:

/* 对FAT解压缩 */
void file_readfat(int *fat, unsigned char *img)
{
	int i, j = 0;
	for (i = 0; i < 2880; i += 2) {
		fat[i + 0] = (img[j + 0]      | img[j + 1] << 8) & 0xfff;
		fat[i + 1] = (img[j + 1] >> 4 | img[j + 2] << 4) & 0xfff;
		j += 3;
	}
	return;
}

/* 将文件内容读入内存 */
void file_loadfile(int clustno, int size, char *buf, int *fat, char *img)
{
	int i;
	for (;;) {
		if (size <= 512) {
			for (i = 0; i < size; i++) {
				buf[i] = img[clustno * 512 + i];
			}
			break;
		}
		for (i = 0; i < 512; i++) {
			buf[i] = img[clustno * 512 + i];
		}
		size -= 512;
		buf += 512;
		clustno = fat[clustno];
	}
	return;
}

运行应用程序

运行什么应用程序好呢,之前我们制作了一个最简单的操作系统(只有三行),我们在这个基础上加上一个CLI指令,让中断处理禁止。

应用程序内容:

[BITS 32]
	CLI
fin:
	HLT
	JMP fin

将它保存为hlt.nas,再用nask汇编生成hlt.hrb。

我们先将文件内容读入到内存中,再为内存创建一个内存段,这里使用ORG0生成。

console.c节选

if (strcmp(cmdline, "hlt") == 0) {
	/* 启动应用程序hlt.hrb */
	for (y = 0; y < 11; y++) {
		s[y] = ' ';
	}
	s[0] = 'H';
	s[1] = 'L';
	s[2] = 'T';
	s[8] = 'H';
	s[9] = 'R';
	s[10] = 'B';
	for (x = 0; x < 224; ) {
		if (finfo[x].name[0] == 0x00) {
			break;
		}
		if ((finfo[x].type & 0x18) == 0) {
			for (y = 0; y < 11; y++) {
				if (finfo[x].name[y] != s[y]) {
					goto hlt_next_file;
				}
			}
			break; /* 找到文件了 */
		}
hlt_next_file:
		x++;
	}
    
    /* 找到了与输入文件名相同的文件 */
	if (x < 224 && finfo[x].name[0] != 0x00) {		
		p = (char *) memman_alloc_4k(memman, finfo[x].size);
		file_loadfile(finfo[x].clustno, finfo[x].size, p, fat, (char *) (ADR_DISKIMG + 0x003e00));
        /* 创建内存段,将hlt.hrb注册为GDT的1003号 */
		set_segmdesc(gdt + 1003, finfo[x].size - 1, (int) p, AR_CODE32_ER);
		/* 跳转并运行 */
        farjmp(0, 1003 * 8);
		memman_free_4k(memman, (int) p, finfo[x].size);
	} else {
		/* 没有找到与输入文件名相同的文件 显示File not found */
		putfonts8_asc_sht(sheet, 8, cursor_y, COL8_FFFFFF, COL8_000000, "File not found.", 15);
		cursor_y = cons_newline(cursor_y, sheet);
	}
		cursor_y = cons_newline(cursor_y, sheet);
}

运行之前我们先想一下,应用程序在在开头加了CLI,正常运行会禁止中断处理操作。在命令行窗口中输入HTL命令后,如果运行成功,那么鼠标就没法移动了,而且就算按下Tab键,也无法从命令行窗口切换到task_a窗口。

是不是这样呢,运行一下看看——

在这里插入图片描述

执行hlt,真的完全停止了,就好像系统死机了一样。今天就到这了,明天继续。

https://gitee.com/mint1993/myos.git

评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值