系统程序正常启动(系统程序无法正常启动)系统程序正常启动(系统程序无法正常启动)

关注健康
关注真实体验

系统程序正常启动(系统程序无法正常启动)

系统程序正常启动(系统程序无法正常启动)

30天写一个操作系统day01「笔记」


30天自制操作系统day02:将二进制操作系统映像用汇编/C重写

30天自制操作系统day03:如何用C来代替汇编写操作系统

30天自制操作系统day04:用C来控制画面显示

30天自制操作系统day05:操作系统界面上面显示文字和鼠标

30天自制操作系统day06:控制鼠标

30天自制操作系统day07:使鼠标指针可移动

30天自制操作系统day08:对键盘进行响应

30天自制操作系统day09: 内存管理

30天自制操作系统day10:利用图层重绘解决覆盖擦除问题

30天自制操作系统day11: 多窗口

30天自制操作系统day12: 初步认识定时器Timer并优化「1」

30天自制操作系统day13: 对超时器进行优化

30天自制操作系统day14: 在大分辨率的屏幕上打字

30天自制操作系统day15:多任务并行1

30天自制操作系统day16:多任务并行的完善

30天自制操作系统day17-day18:命令行窗口以及dir命令

30天自制操作系统,进行到今天,day19,就一多半了。

目前为止,实现了多任务并行控制器,并且利用这个多任务并行控制器完成了一个命令行程序。

到此,一个操作系统的基本雏形就完成了。为什么说包含了操作系统的基本雏形?

对照 linux操作系统0.11版本,你会发现:

linux0.11版本中的从内核态转化到用户态,其实就是从操作系统的主程序这个第0个任务转换到新的任务上。linux0.11版本中的内存管理,与 day09的内存管理非常相似。liinx0.11 版本中的多任务优先级控制,与day16,da15的优先级控制也比较相似,都是用TSS,GDT,都是用一个超时器来轮训所有的任务,但控制优先级的方式不同。

要说现在的操作系统缺少哪些必要模块?就是网络模块和文件系统了。

其实文件系统模块在day18已经着手开发了,但是也只是对文件的名字进行读取。

今天我们就来读取文件的内容并显示。

所以,今天主要是完成type软件,在type命令里实现对文件内容的读取,并显示。

按照一下顺序展开:

实现内容的读取并输出用FAT表对大文件读取并输出运行程序:读取文件并运行之把文件内容读取到内存里并输出到窗口中

我们看看windows上的type命令。


文件夹下有很多文件,其中有个文件叫做text.txt.txt,其内容是:30 days make a OS

使用type test.txt.txt就把文件内容显示出来了

我们今天的目标就是做一个type出来。

还是首先分析一下这个事情如何做:

type命令,不需要对接收键盘字符做任何改动。因为昨天的 dir命令已经完成了只用继续研究一下软盘上到底是如何存储文件内容的。文件内容的存储,与文件名字的存储方式类似么?只要得到文件内容存储的地址以及文件长度,就可以很轻松读取出文件内容了。

分析完,发现操作系统本身的代码逻辑不需要更改,只需要研究一下文件是如何存放在软盘上就行了。

昨天读取文件名字和大小时,用到了文件信息结构体

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

这个结构体里的clustno就是指文件在软盘里的存放的具体位置,它是簇号码。

有了clustno,就可以计算出具体的位置了

位置 = clustno x 512 + 0x3e00

比如如何读取得出clustno=2,那么所存放的位置就是2x512+0x3e00=0x4200了

那现在文件内容的首地址为0x4200,文件长度为size,那么就可以把文件逐个读出了

编写一个type命令出来:

console_task(){ ... // 如果是type命令if (cmdline[0] == 't' && cmdline[1] == 'y' && cmdline[2] == 'p' &&cmdline[3] == 'e' && cmdline[4] == ' ') {for (y = 0; y < 11; y++) {s[y] = ' ';}y = 0; // 从cmdline的第5位开始,记录要显示的文件名for (x = 5; y < 11 && cmdline[x] != 0; x++) {if (cmdline[x] == '.' && y <= 8) {y = 8;// 保证文件名在s中至少占3个字符} else {s[y] = cmdline[x];if ('a' <= s[y] && s[y] <= 'z') {// 如果是小写字符,则转化为大写 // 因为在内存里,统一保存的是大写s[y] -= 0x20;} y++;}}// 遍历225个文件信息结构for (x = 0; x < 224; ) {if (finfo[x].name[0] == 0x00) {break;}if ((finfo[x].type & 0x18) == 0) { //找到与s中保存的文件名同名的FILEINFOfor (y = 0; y < 11; y++) {if (finfo[x].name[y] != s[y]) {goto type_next_file;}}break; }type_next_file:x++;}if (x < 224 && finfo[x].name[0] != 0x00) {//获取文件大小y = finfo[x].size; //利用公式计算出文件位置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 {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);}}..

在45行找到了文件内容的首地址,在47行开始的for循环里,把内容里的字符逐个打印出来了。

我们运行一下试试效果:

视频加载中...

显示中有乱码,首先我们想到我们对制表符0x09,换行符0x0a,回车符号0x0d没有进行处理。

这些字符是需要专门处理的,那么再加上对这些特殊字符处理的操作:

if (x < 224 && finfo[x].name[0] != 0x00) {y = finfo[x].size;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;if (s[0] == 0x09) {//如果碰到文件内容里有制表符for (;;) {putfonts8_asc_sht(sheet, cursor_x, cursor_y, COL8_FFFFFF, COL8_000000, " ", 1);cursor_x += 8;if (cursor_x == 8 + 240) {cursor_x = 8;cursor_y = cons_newline(cursor_y, sheet);}if (((cursor_x - 8) & 0x1f) == 0) {break;}}} else if (s[0] == 0x0a) {// 换行符cursor_x = 8;cursor_y = cons_newline(cursor_y, sheet);} else if (s[0] == 0x0d) {// 回车符,回到行开头,我们在换行符里已经实现了// 这里不做操作}


改正type命令的bug:使用FA表,文件位置表,读取大文件

按照现在的显示程序,如果文件超过512个字节,很可能就会出错。

因为FAT的存在,File Allocation Table, 文件位置表。

为什么有这个表的存在?

因为我们往磁盘上存文件时,并不是把文件存放在连续的簇中的,我们可以把文件存放在不连续的簇中。

所以,读取文件时,每读取512字节后,都需要重新查表获取存放文件内容的下一个蔟号

这个表通常存放在文件开头部分。

FAT[2]是当读取完蔟号为2时对应的时,后面的内容所存放的簇号=FAT[2],内容的存放地址。

要想真正准确无误的把一个大于512字节的文件内容正确读出,应该在读取文件的一开始,就先从文件磁盘开头把FAT表读取出来。

其实从文件开头读取字符,还无法得到FAT表。还要对读取出来的字符进行如下方式的计算:

// 读取文件的fat表// img是文件的首地址// 比如haribote.sys的首地址就是0x0010 0200void file_readfat(int *fat, unsigned char *img){int i, j = 0; //遍历前1440*3个字节 //以内磁盘容量是2880个512 // 每3个字节,就可以得到fat表中的两个值,两个值可以指向512*2=1K的容量 // 这样1440*个字节,就可以得到长度为2880的fat表,可以指向1440K的容量 // 那么,如果内的硬盘是1T,你应该能够计算出,你的fat表的长度吧? // 其实就是fat表的长度为:1T/512for (i = 0; i < 2880; i += 2) { // 这里一次去3个字节:第0个,第1个,第2个 // 将第1个字节的低4位放在第0个字节的左边,形成一个三字节的数,这就是fat[i+0]fat[i + 0] = (img[j + 0] | img[j + 1] << 8) & 0xfff; // 将第1个字节的高四位放在第3个字节的右边,形成一个三字节的数,这就是fat[i+1]fat[i + 1] = (img[j + 1] >> 4 | img[j + 2] << 4) & 0xfff;j += 3;}return;}

利用以上函数,我们成功的读出了软盘的FAT表。

有了这个FAT表,我们就可以读取大文件的全部内容了。

// 利用fat表读取文件的全部内容void file_loadfile(int clustno, int size, char *buf, int *fat, char *img){int i; // 启动一个for循环for (;;) {if (size <= 512) {//如果文件还剩下没有读出的内容小于512,那么直接读出for (i = 0; i < size; i++) {buf[i] = img[clustno * 512 + i];}break;} // 如果还剩下的部分文件大于512for (i = 0; i < 512; i++) { // 则读取clustno*512位置的512个字符buf[i] = img[clustno * 512 + i];} // 读取了512,就在size中减去512,size中记录还有多少字节没读出size -= 512;buf += 512;// buf记录已经读出的字节数clustno = fat[clustno];// 从fat表中去取出下一个512字节内容所在的簇号}return;}

至此,我们就利用FAT表完成了对大文件的读取。

然后把新写的两个函数用在 console_task函数中

void console_task(struct SHEET *sheet, unsigned int memtotal){ ... //读取FAT表file_readfat(fat, (unsigned char *) (ADR_DISKIMG + 0x000200));...for (;;) {... } else if (strncmp(cmdline, "type ", 5) == 0) { ... if (x < 224 && finfo[x].name[0] != 0x00) {p = (char *) memman_alloc_4k(memman, finfo[x].size); // 利用fat表读取文件内容 file_loadfile(finfo[x].clustno, finfo[x].size, p, fat, (char *) (ADR_DISKIMG + 0x003e00));... memman_free_4k(memman, (int) p, finfo[x].size);} ...}}

这样通过FAT表来对文件内容的读取才是正确的。

FAT表就是今天新学的知识点了

运行应用程序

那么到这里,我们完成了文件内容的读取了。

完成文件内容的读取意味着什么?

意味着如果一个文件里存放的是代码。我们就可以把这个代码读取出来,读取到内存中

把代码读取到内存中意味着什么?意味着如果我们用一个TSS把代码所在地址放入GDT中,然后再加上一个jmp指令,这个代码就能运行了。

就是说:这个原本存放在文件里的代码,就可以被我们正确的运行了

如果想多任务的并行运行,就可以从任务管理器里拿出一个任务与TSS绑定,然后task_run就可以把任务放到并行运行的任务队列里了

这也太酷了吧。

我们总结一下:

我们可以把预先编写好的程序放入文件里通过解析FAT表,把文件读取到内存中地址A处把内存地址A放入GDT的1003*8位置,跳转JMP 1003*8,就可以运行文件里的程序了

这就是运行一个应用程序的步骤啊。

那就来写代码吧

假设我们已经写好了一个程序,保存在了文件HLT.HRB中

我们想通过命令hlt来运行文件HLT.HRB中的文件,如图所示:



代码可以这样写:

if (strcmp(cmdline, "hlt") == 0) {// 如果命令行输入hlt// 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'; //找到文件HLT.HRB的 FILEINFOfor (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); // 读取HLT.HRB的内内容到内存的p位置file_loadfile(finfo[x].clustno, finfo[x].size, p, fat, (char *) (ADR_DISKIMG + 0x003e00));// 把p放到GDT表的1003位置 // 因为任务管理器把3-1002号位置使用了,所以我们只能使用1003号位置了 set_segmdesc(gdt + 1003, finfo[x].size - 1, (int) p, AR_CODE32_ER);// 跳转到1003*8位置,执行HLT.HRB中的程序 farjmp(0, 1003 * 8); //执行完,释放文件内容所占内存memman_free_4k(memman, (int) p, finfo[x].size);} else {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);

读取文件后,只需要两步:放入GDT,跳转到GDT。

那如果想多任务并行的执行应用程序呢?按照一下4步

我们可以把预先编写好的程序放入文件里通过解析FAT表,把文件读取到内存中地址A处把内存地址A放入GDT,初始化一个TSS表,把TSS表的指令指针赋值A把TSS放入一个新任务中,task_run

比刚才多了一些操作,把我们的多任务管理器用上就行了。

用上多任务管理器,不用自己操作GDT和JMP,多任务管理器就帮我们自动的JMP了。

并且还是与其他程序并行的。

这太棒了

在自己的操作系统上运行程序了。

那如何制作HLT.HRB呢?

新建一个文件hlt.nas

在其内写入:

[BITS 32]fin:HLTJMP fin

这是一个无限循环程序,每次循环执行HLT。

然后,用 nask对其进行编译,生成hlt.hrb.

这个后缀我们自己可以定。可以把它定位hlt.exe,也可以定位hlt.dos。

自己随便定。

编译好之后,再把这个hlt.hrb文件复制到软盘中。

这里编译和复制到软盘中,Makefile文件里,应该加上这些代码:

把hlt.nas编译为hlt.hrb

hlt.hrb : hlt.nas Makefile$(NASK) hlt.nas hlt.hrb hlt.lst

把hlt.hrb复制到软盘中:

haribote.img : ipl10.bin haribote.sys hlt.hrb Makefile$(EDIMG) imgin:../../../tolset/z_tools/fdimg0at.tek \wbinimg src:ipl10.bin len:512 from:0 to:0 \copy from:haribote.sys to:@: \copy from:ipl10.nas to:@: \copy from:make.bat to:@: \copy from:hlt.hrb to:@: \ 这里,把 hlt.hrb复制到镜像文件haribote.img,自然就会被复制到软盘中了imgout:haribote.img

该改动的地方都改完了,我们运行一下,看看效果:

视频加载中...


到这里,今天的内容就结束了

总结在视频中,文章中就不说了

明天day20,我们开始着手在操作系统上,开发应用程序。

未经允许不得转载: 九月健康网» 系统程序正常启动(系统程序无法正常启动)
分享到: 更多 ( 0)