准备工作:Mac环境下的工具介绍(2013-12-09)

Advertisements

首先是环境,我用的是Max os Maverick 64,就是10.9,硬件(有点低0.0,对于小菜的我已经完全够用啦):
10.9刚发布,新出来的东西总问题一大堆,然后解决办法没几个。但是还是能用了,编译器NASM(xcode中command line tools带的那个)xcode真的很大,但是不可否认,安装比vs2010快多了。基本指令是dd指令,和cat指令,这两个和linux下差不多.
首先建立一个.img的镜像文件:

注意,if后面的参数是个文件夹,用空的,of参数是输出的img文件 count 是大小,自己十几次就知道了
然后编写ipl
注意,后缀用.s而不用nas哦,因为.s的文件vi编辑器会自动语法高亮
然后要说一下ipl中的一句话

这句话汇编通不过:提示这一行有非法操作符。。我也不知道咋回事。。。。。
改成:
Center

就可以顺利的汇编成bin文件了
接着,用CAT指令把bin塞到img中

然后用qemu执行以下

结果如图:
Center 1
用到的工具软件我会上传,安装比较简单,都是dmg的,双击就好。

Mac环境搭建

先说结果,就是作者在网站上放了os x的工具(hrb.osask.jp,也有linux下的工具,可以自己去下载),也就是说我白忙活了三天。。。
再说一下这几天都干啥了,主要是想把c语言和nasm汇编连在一起。这个很多人都做过,但在网上现有的资料很少有在os X上做的的,也或者做了大家都没人说。。。。先贴代码:

这是c代码,调用swap交换两个值,为了不调用标准库,我没写显示函数,而是用了一个死循环代替,如果程序停住了,说明运行成功,再贴下汇编,这是我第一次写汇编哦。。啦啦啦啦啦

代码很简单,但是和书上格式有些不同,作者说的他用的是nask是他自己改版的nasm所以有些关键字用不了。。。

然后是编译成obj文件,这个很纠结,一开始不会用gcc编译出32位obj后来发现要加:

就可以了。

编译过程如下图:

整个编译连接过程,最后光标停止,说明函数执行成功,如果nasm中写了什么中断或者什么其他系统不允许的可能会有总线错误(bus error)或者段错误(详情可以去看《c专家编程》,有相关说明)。
值得注意的是nasm -f 的参数:

这个参数纠结了好久,最后还是看帮助搞定的,因为linux下都是elf,但是os x用elf参数最后ld会报错,说找不到xxx函数定义。。
ld的相关问题:

1,2和3的问题原因都是-f参数选的不对,或者gcc编译出来的是64位obj,nasm只能编译出来32或者16位目标代码。

如果和系统可运行程序不对应,ld不会给你链接的哦。。
最后是objcopy,这个是GNU 的binutils的工具包的一部分。作用是操作二进制文件,可以任意改格式,具体参考说明,吧之前链接好的用objcopy 生成纯二进制文件后,和作者的比较发现,不一样,运行时qemu卡死,得到结论就是这两天又白忙活了。。。还好算是找到了工具,也有源代码,值得好好学习。

Mac下工具的使用

现在来介绍官网上下的工具怎么用首先是官网地址,书上有个注释上有:hrb.osask.jp

Center

翻译成中文大概是这个样子滴。
上面有两个文件可以下载,一个是工具,一个是工具的源代码,很好的学习资料
下面把工具复制出来

Center 1

看到很多可执行文件。感觉好舒服。
然后把我们随便一个project复制到z_tools的同级目录下
project的内容可以修改,因为批处理可以下岗了:

Center 2

然后可能是难度最大的部分出现了。修改makefile
如果没用过makefile可以先找点资料看看。很简单的语法,很强大的功能:
修改完成后是:

乱码是原来的日语。。华丽的忽视掉,一排#之间的就是修改过的地方,
然后就是make run了。。
结果如下:

Center 3

保护模式

本节摘要:软盘,启动,保护模式的初次使用

软盘?不需要!

昨天一天看了5天内容,把觉得有些可能不好理解的写下来,内容不分先后,感觉作者写的通俗易懂,而且代码以及工具在xp下运行流畅,根本不需要软盘,直接在工具提供的虚拟机上跑就可以,下面来描述下昨天的学习心得和问题0.0。

首先是内存,对内存的管理是至关重要的,所以我们应该先了解一下内存的具体分布:

这个图是我找的,如果有打错的地方或者有什么变动,请留言,不胜感激。
这就是内存的分配,对于小菜理解有些困难。
接下来可能就是从加电到系统启动的过程的理解了:
实模式下(为保护模式做准备),启动并加载过程如下图:

至于为什么到0x07c00,原因是“两头约定”(就是设计BIOS的大伯们和设计操作系统的大叔们商量好,把启动程序放那,一后大家开发都方便)。
里面用到的汇编命令,可以去看一些汇编的书,王爽老师的那本书不错,还有《深入理解操作系统》里面的知识和这里用到的也很合拍,都是好书,可以看看。

IPL,BIOS

本节摘要:IPL机制,BIOS介绍

IPL

其实还是前五天的,现在继续说,第三天,p49页下面部分说“0x8000到0x81ff是启动区”而0x7c00到0x7dff也是启动区,这个地方困扰了我好久,不过今天好像看到类似的说法了:在linux中启动区启动后会将启动区复制到0x90000的地方,原因也没说,知识含糊的说为后面的栈操作做准备,其实无论怎么放,这只是表明,IPL功能已经工作了,成功的实现了转移,我们可以自己来控制电脑了。
在IPL的实现过程中,我发现BIOS中断的威力相当大,就像平时我们用库函数一样,各种寄存器就是这些函数的参数,可以实现硬件的各种操作,准备明天起早找点BIOS中断的资料贴一下,如果能熟练运用会有很牛的感觉,已经接近硬件了,很兴奋。。。汇编是一把利器。

汇编与C语言

本节摘要:汇编调用C语言

汇编语言函数用c语言调用

其实我们可以把这些在完成操作系统编写时写的函数称为库函数(注意:不是标准库函数),但是c在调用库函数(普通函数),压栈的顺序是从右向左的,这个是肯定,所以,参数出栈的时候先出来的是后面的参数,明白了这个问题,就很好理解io_out()函数参数的出栈和参数的使用了,还有函数如果有返回值,保存在eax(32位返回值),64位返回值保存在 eax 和edx中,edx保存高32位,eax保存低32位。
知道了这些,参数和返回值就已经搞定了,剩下的就是定义和具体代码的实现了,什么利用中断啊,MOV给你MOV给我啊的什么就可以自由发挥了。。看起来已经很酷了。。
函数定义,搜了一下,发现定义都不同,但都是写global声明下函数名(名字前面加下划线),然后以函数名为标号写下函数体,返回用ret(但有些函数好像不能用RET返回。。具体以后再说)。
还有就是函数指针,由于博主以前研究了好长时间指针,虽然不能说特别明白,但是看这个书上的指针还是没什么障碍,像什么2[p]这种访问方式,以前也都见过,如果有人这里有疑问推荐几本书:《c专家编程》《c陷阱与缺陷》《c和指针》都有详细描述,自己写个程序试试就行了,这里不再赘述。。。。

GDT和IDT

本节摘要:GDT和IDT介绍

GDT和IDT

全局描述表(GDT ,Global Descriptor Table):首先看到全局说明这货很重要,应该是掌握了一些,其次这个表是个数组,所以,这是一个重要的数据结构。

GDT数组中装的是段描述符【段地址,段的最大长度,访问权限】。因为这个描述符太大了,没有这么大的寄存器(64位),所以就把这个描述符放在内存里,成为了重要的GDT,intel的大叔们设计了一个寄存器GDTR(LGDT为装入此寄存器指令)用来存放GDT的入口地址,因为GDT可以设计到内存的任何位置,而GDTR可以帮助快速定位,这样段寄存器的13位就能索引到GDT了。GDT是保护模式必备的数据结构。能够很好的划分内存,控制程序的内存方位(不然会出大问题)。

中断描述符表(IDT Interrupt Descriptor Table):之前已经见识过中断的强大了,这个描述符表是管理中断的,很明显是高手中的高手,其实他是一个8字节的描述符数组,也是一种重要的数据结构。IDT只要保存256个描述符就够了,因为最多也不会有超过256种中断,甚至IDT可以少于256,只要够用就行。同样,IDT可以放在内存的各个角落,只要你愿意,但是一定要是线性的,不然就找不到了(这是个数组,当然线性了),IDT也有专门的寄存器存放入口地址,叫IDTR,这个寄存器中含有IDT表32位的基地址(高32位)和16位的长度(限长)值(低16位)。IDT表基地址应该对齐在8字节边界上以提高处理器的访问效率。LIDT和SIDT指令分别用于加载和保存IDTR寄存器的内容。LIDT指令用于把内存中的限长值和基地址操作数加载到IDTR寄存器中。该指令仅能由当前特权级CPL是0的代码执行,通常被用于创建IDT时的操作系统初始化代码中。如果有越界访问IDT的现象会触发一个保护性异常。

中断向量表:实模式下的前1K个字节为中断向量表,每项有四字节(2字节的段地址,2字节的偏移地址,来定位实模式下的1M空间),用来指向中断处理程序的位置,但是在保护模式下,4字节不够用,需要8字节,所以改名IDT,而且位置也不局限于开始的0x00000处,使用全新的方式完成中断处理。

IDT中的每一项叫“门”(门描述符),很明显是传送门。

中断处理

本节摘要:中断处理

中断处理

我感觉中断处理应该是系统底层设计的关键,因为这是在控制硬件,和给c提供接口,如果接口搞不好,一路兵败如山倒。

分割编译其实就是c语言初等知识,就是别写的太乱,每个文件放不同的东西,头文件加以说明和声明,以及一些宏定义或者什么的,可以去查查相关c语言的资料。

今天让我纠结了一下午的东西在p113,也就是调整栈内容,用以组合出GDTR的48位数据那个地方,很纠结,现在来说明一下:

首先是c语言函数调用时参数传递问题,先从右边开始压栈,直到所有参数完成,但是在CALL的时候还要对CS:EIP进行压栈,这是个啥东西我也不清楚,所以我们可以看到取参数都从【ESP+4】开始取的,取参数的位置是【ESP+4】,【ESP+5】….取多少位就一直加加加。还有就是超过8位以上的数据在内存中存放的规律,如果是8位以下的一个内存单元就可以了,但要是16位32位呢,这就涉及到了大端机(Big-endian)和小端机(Little-endian)的区别了,首先我们以0x12345678为例,32的数据存储:

就是高地址存高位还是低位的区别,不是很好理解,慢慢熟悉就好了,具体机器是大端还是小端写个c程序就行了,用一个联合来测试一下,具体自己发挥,《c语言深度解剖》有相关描述,好多书都提到过。
下面是113页困扰我很久的问题的具体解析:

这就是那个GDT初始化函数调用时栈的内容,首先很纠结的就是栈底在高地址,栈是向下生长的,所以,看着很别扭,还有就是esp(栈顶指针)不是指向第一个参数的,剩下的问题就很好说了,如果要从内存0x02abf8读取两个字节,那么跟着0x2abf8后面的f9会被连带访问而不是f7。。。。。
接下来说PIC,这是个硬件,如果有点电路知识就应该没啥障碍了,是一个存储设备,cpu可以从他取数据,具体怎么去要看这个片子功能的设计和相关信号的传递方法,没有什么需要解释的,设计完全是为了便于开发。
中断程序的制作应该是核心问题,以及缓冲区的使用,明天再说。感谢收看,

中断

本节摘要:中断处理程序

中断处理程序

今天看的还是比较顺利,很快一天的就看完了,总结一下今天的主要知识。

首先是中断处理程序,中断处理程序尽量高效,短小精悍,所以像显示啊什么的操作尽量不再中断处理程序中出现,因为一旦进入中断处理程序,其他中断将会被屏蔽,因为如果不屏蔽就会出现乱套的现象,一个中断还没完另一个已经来了,你说你干不干,干哪个,所以,尽量减少中断处理时间,这样就可以减小中断同时发生而后发生的没办法处理的现象,所以,缓冲区出现了用武之地。

之前一直不知道缓冲区是干嘛的,今天终于见到其真正的用途了,就是保存中断信息,然后慢慢处理,之后讲到FIFO缓冲区(讲的不严谨点就是队列),队列的大小随意定义,为了减少数据移动,使用了循环队列,高效,但结构里面有一个len用来判断对是否满,可以用头指针和尾指针的位置关系来判断,但没有多大效率上和空间上的区别,所以应该都可以,这里的队列采用数组而不是链表,数组在空间上连续,所以读的速度更快。效率才是硬道理!
发现了对中断的处理和处理时对下一个中断的到来以及处理安排上是很重要的,需要我们严加设计的,应该也是系统设计的重要部分,还有书中出现的各种编号(像不同端口的地址什么的),不知道具体去哪找,不同的cpu不知道有没有什么区别,这是不是就Intel那个好几千页的手册所要说明的。。
还有就是鼠标和键盘的电路是一个芯片组上的。。。。。

这几天基本都是用c语言设计,感觉只用汇编写了那些最基本的函数,如果系统想要更强大是不是应该多用汇编写点更多的函数以供调用。

明天继续。。。。。

鼠标

本节摘要:鼠标显示

啊哈,鼠标

今天看的有点纠结,因为竟然看困了,很少有这种情况,主要是有好多东西作者并没有做深入介绍,只是说这样是对的,至于为啥这样以后再说,这让我感觉很不爽。具体来说说。

首先,先解决了鼠标运动的问题,我感觉这个不是什么大问题,只是读取数据那需要点技巧,就是验证数据的有效性,以前用单片机发送串口数据时也用到过类似的做法,作者提出的是检查第一个字的低八位以及高两位,如果高两位是0,而低八位是8,那么数据没问题,但并不一定绝对没问题,比如发送的数据为 0x08 0x00 0x11下一组为0x08 0x08 0x01时也就是队列里面是0x08 0x00 0x11 0x08 0x08 0x01时如果红色字体丢失任意一组数据,其结果和下一组结果都是错误的,但发生的概率很小,我们也没必要要求硬件每发送一个数据都要加上校验位,那样冗余太大,也浪费资源,而且估计鼠标也不会总坏。。。

个人来讲,我不想将桌面作为系统开发的前期所应该做的(等我自己写的时候不会这么弄),一个黑漆漆的命令行而具有更强大的功能,我觉得更好,而且将桌面和系统剥离开,也就是linux那样,感觉更加自由。哈哈,这都是自己的想法,没有任何科学根据。接下来就到了让我纠结的地方了,就是作者解释了之前没解释的汇编代码:

好吧,我的理解就只能到上面这个地步,不知道再继续往下看还能多领悟点不了。。

为什么是向键盘发送指令来控制A20GATE呢?百度到的原因是为了控制是否使用超过1M以上的内存,并和16位兼容,IBM的老爷爷们使用键盘控制器剩下的一根信号线来控制A20,A20不是控制是否进入保护模式。

原始16位处理器访问最高地址为0xFFFF:0xFFFF=0xFFFF0+0xFFFF=0x10EFFEF,很明显多于1M对于多出来的0xEFFEF的访问就需要另外的地址线。但是系统的做法是当程序员访问多于1M的地址时,CPU将地址按1M取模。。这样就不会超过1M了,这种技术被称为wrap-around

到了80286出现了一些问题,当程序员试图访问1M到0x10EFFEF时系统并没有循环回去而是直接访问,这导致了和之前产品不兼容,于是为了兼容,设计了第21根信号线就是上面提到的键盘控制器多余的这根,称为A20来控制是否访问1M以后的地址,当设置为打开时可以访问到多于1M的地址,关闭时则只能循环回0按8086的方式访问。

以上都是实模式下的,在保护模式下,CPU访问的内存增加,如果这个20位的控制线不被打开,那我们的第20号位地址将被视为无效,内存将会被切割成小碎片,系统将只能访问以基数兆的内存。。所以必须要打开A20才能完整访问。至于A20和键盘的关系。。。就是没啥关系,只不过用一个控制器。。。

一下内容来自互联网(没有验证准确性):

多数PC都使用键盘控制器(8042芯片)来处理A20Gate。 从理论上讲,打开A20Gate的方法是通过设置8042芯片输出端口(64h)的2nd-bit,但事实上,当你向8042芯片输出端口进行写操作的时候,在键盘缓冲区中,或许还有别的数据尚未处理,因此你必须首先处理这些数据。 流程如下:  
1. 禁止中断;  
2. 等待,直到8042 Inputbuffer为空为止;
3. 发送禁止键盘操作命令到8042Input buffer;
4. 等待,直到8042 Inputbuffer为空为止;
5. 发送读取8042 OutputPort命令;
6. 等待,直到8042 Outputbuffer有数据为止;
7. 读取8042 Outputbuffer,并保存得到的字节;
8. 等待,直到8042 Inputbuffer为空为止;
9. 发送Write 8042Output Port命令到8042 Input buffer;  
10. 等待,直到8042 Inputbuffer为空为止;  
11. 将从8042 OutputPort得到的字节的第2位置1(OR 2),然后写入8042 Input buffer;  
12. 等待,直到8042 Inputbuffer为空为止;  
13. 发送允许键盘操作命令到8042Input buffer;  
14. 打开中断。

今天就这样。。明天继续看。。。谢谢收看。。

内存管理

内存管理简介

对于一个系统来说,资源是最重要的,管理资源应该说就像计划你口袋里面的钱怎么花一样(不太准确。。但是重要性是相似的)。
首先是检查内存大小,BIOS应该是提供内存大小检查功能的,但是不同的BIOS查找内存大小的方式不同,所以我们应该以一种更为通用的方法进行,以获得更好的系统兼容性,而且现在的CPU都配有各种缓存,一级,二级,三级。。。先要禁止高速缓存,才能确保访问的变量全部在内存中,所以,先禁用缓存(设置CR0的某个标志位为指定值,本书还检查了一下机器是否有高速缓存,这个现在基本不用检查了,我还真就没见过386长什么样)。禁用掉高速缓存我们就要对内存挨个访问了,看看到底有没有这个位置,对0xaa55aa55的反转和比较再反转,我感觉没有什么具体意义,你可以改成任意的数字,如果访问的内存地址不存在,则返回记录到的最大的位置值,后面的程序是优化次功能程序,如果只为了检查内存的大小,完全可不用一个一个来,而是十个十个来或者一万个一万个来,只检查最后的几位就行,这个的速度会按照相应的倍数提升而准确度也会随之下降。如果要做内存健康检查就要一个一个来了。。。书中还有一个涉及到编译器的优化问题,这个会在后续的博客中详细介绍。。
接下来,检查完内存就要对内存分配进行管理了,这个应该是个很困难的问题,书中的方法有两种。
第一种:首先设置内存分配的最小单位,书中为4KB,把所有能用的字节都统计出来划分成4KB一段的好多个内存块,然后用一个字符数组来记录那些很忙,那些很闲,然后分配给需要的程序,这样的代价是每4K就需要8BIT的空间来标记这块的可用性(8/(4×1024)=1/512)所以代价是恒定的,不论多大内存N GB,都需要(N/512GB的内存来记录),这样的好处也是第二种方法的缺点就是不用做拼接,碎片问题容易解决。据说内存碎片是个很可怕的问题,尤其是对于长时间运行的服务器。。。
第二种:用一个数据结构记录所有内存的使用情况,这个数据结构内包含一个可用空间大小的记录,还有就是N个用于记录内存起始地址和可用大小的子结构数组了。。

第二种方法速度快,占用空间小,但是分配时产生的细小的内存空间没有被使用,和回收内存时的合并操作都相当麻烦。。所以有利有弊,当内存碎片过多,而系统回收算法不够强壮时有可能是灾难性的。。
至于设计系统时用什么样的内存管理方法,目前还没想好,总之,这是个很关键的问题。。会找一些资料,在后续的博文中陆续推出,欢迎收看

内存管理和叠加处理

本节摘要:内存管理和叠加处理

内存管理和叠加处理

今天的代码比较多,对于写过程序的人来说应该还是比较容易看懂的。

首先说的内存管理,昨天说道把很多内存分成0x1000字节大小的块,然后分配给程序使用,但是不一定所有的程序用到的都是0x1000的整数倍字节,所以就要设计一个取整的方法,就有了书中提到的聪明做法(size+0xfff)&0xfffff000;这个做法或者称为算法的正确性证明很简单,带两个数试一下就知道玄机了,就是把一个12位非零二进制数向前进位,而零不进位,最后把后3位归零。我们还可以把大于x的任何数进位,小于等于x的舍去(size+0x1000-x-1)&0xfffff000;哈哈,应该是对的,我也没有证明。。不过x=0是和原式一样的。。举一反三山寨之本。

接着就到了我很疑惑的地方了。叠加处理,但是!!!注意!!!作者开始给结构体显示的分配内存了,就是图层的控制结构体,我不明白,之前写的那些结构体也没分配内存,为啥突然从这开始分配了,而作者根本没提,难道是因为昨天才写的内存管理,今天就开始用了?那之前的程序怎么办呢。我一会儿还是好好研究一下。

感觉要设计系统,就应该对这个系统的宏观结构应该完全清楚,骨架清晰,内容就可以随意发挥了。现在是骨架还没搞清楚,所以博猪没有一开始就写自己山寨版,而是想把书看完,然后找点操作系统相关的书再看看然后再做个试验品出来玩玩。。哈哈。。不知道思路正确不正确。

至于这个叠加处理,其基本目的是控制多个图层,设计相应的数据结构。而刷新那里作者反复的实验,但其本质就是减少冗余计算,就是说有些计算能剩就不做,就像一个简单的图像卷积(好吧,博猪以前做过一段时间图像处理,听不懂的可以完全忽略。。)如果按照算法做,时间复杂度是 $O(x\times y\times size\times size)$(其中x,y是图像的大小x*y个像素,size是卷积核宽度),但经过优化可以做到只要$O(x\times y)$,而方法就是要用到前一步的计算结果,减少冗余的计算量(好吧,这是博猪第一次面试的面试题,很幸运。公司没要我0.0)。好,叠加的优化原理是哪个地方改变了,就刷新改变的区域的像素。至于实现细节,书中的代码有详细介绍,关键是确定叠加区域,然后重绘叠加区域。。。 

定时器

本节摘要:闪烁和定时器

闪烁和定时器

今天的主要内容是屏幕刷时的闪烁问题,和定时器的小部分内容。
首先说明定时器应该是系统重要部分,所以明天会好好记录一下计时器的心得。
关于屏幕刷新时闪烁,这个之前也遇到过(之前做单片机数据绘图时,因为单位时间数据量很大,一直刷新画面就会导致波形图闪烁,而解决这个问题的方法是使用双缓存技术),而这次不同的是如果系统桌面都闪烁,就没人会使用我们的系统了。。所以问题很严重,需要马上解决,作者的解决方法是改变刷新的范围和层数,减少无谓的刷新。其实作者从开始所提出的桌面显示的完整体系就是一种最简单的设计(作者想带着我们一步一步走向高端,但如果看完全书后再去写的话就不需要走弯路了),我们设计时完全可以自己发挥,设计出更高效更华丽的桌面环境,作者最后提到的在内存中开辟map区域其实就是一种高效的方法,而这个方法可以在一开始显示的时候就设计进去,所以,桌面显示并不应该成为我们学习的重点(在系统体系完成后再详细设计规划)。
相反,计时器才是我们应该好好研究的,这是系统的根本之一。

计时器

本节简介:计时器

计时器

昨天一直在搭建环境,想在mac下实现书上制作镜像和编译连接相关文件的功能,无奈,弄了一天还是不太好用,看到另外一个微博上的童鞋用Linux完成了相关操作,想试一下,但os x上没有objcopy等命令,而且gcc好像也是改版的,而且现在被我折腾的连命令行下使用command line tools 都有问题,所以,我还是回来把笔记补上。一会儿再去弄,争取放假之前能把环境搞定,这样寒假就可以专心研制自己的系统了,而不用把时间花到细枝末节上。
昨天的内容讲的是计时器,我一开始没觉得定时器有啥功能,无非是弄个钟表出来,后来我看到了超时(timeout)恍然觉得这个东西很主要,作者并没有直接说去定时器在以后的实现中有什么作用,但是一直对中断处理程序进行优化可以看出,这个东西以后要被不停地使用,而且作者提出定时器后,马上有多弄了几个,而且最多能实现500个定时器,第十二天的主要工作就是优化处理速度,因为中断处理时所有的中断信号都被屏蔽了,所以必须快速的处理,恢复中断响应。
下面的图是作者最开始的设计:

其中条状的是定时器的执行时间,横坐标是时间,纵坐标是各定时器,不论有多少个定时器,都要循环500次(也就是最多个的情况),所以这对要求效率的工作并不是个好主意,于是开始优化。
首先可以将定时器排队,就像我们有两场球赛在不同的频道,一个十分钟后在c1频道,一个二十分钟后在c2频道,我们只要盯着那个十分钟后开始的c1频道就可以了,你可以每两分钟过来看一下,如果c1开始了,我们才会去关注c2是否开始(寝室有一哥们能同时看三场球。。。。)也就是说c1不发生,理论上来说c2绝不会发生,所以就优化出了下图:

Center 1

上图横轴是时间,没有纵轴,每个小格子就是不同的定时器之间的间隔,每个定时器都是从头开始到对应的线,也就是把所有定时器从小到大排列起来,然后弄到一根轴上,这样就可以只监视下一个要超时的定时器就可以了。
而上图还是要监视500个,于是下一步的优化就是记录当前计时器的个数,减少循环,之前还有一步就是尽量减少循环中的计算,包括比较(if语句)尽量减少少数情况的判断,提高速度。
涉及到的主要问题就是速度优化,下面列举一些常用到的比较简单的循环体的优化方式:

  1. 将在循环里面多次计算,但是结果不会改变的计算,移到循环外面去。
  2. 减少函数调用
  3. 减少内存访问
  4. 减少对少数特殊情况的测试

目前就这些,以后用到定时器会继续说。。

      

FIFO

本节简介:FIFO

FIFO介绍

首先是FIFO缓冲区的一个通用化,这样就相当于在一个队列里面加入了很多中断处理收到的数据,CPU可以根据自己的节奏一个一个来处理,包括鼠标,键盘,定时器,以后还有可能是其他的设备,这样做的好处就是先后顺序很明确,谁先来就先处理谁,如果有多个缓冲区就无法确定哪个应该先处理,哪个应该暂时等待,而且这样设计更有利于编写程序,一举多得。
接着就是比较重要的性能测试,说实话,这个是非常重要而且想让我们的程序飞起来就必须反复去做的,作者也在这方面写了很多,但是方法很简单

1:写程序,运行,记录时间,观察代码
2:优化代码
3:继续跑程序,记录时间,返回第二步

什么时候感觉已经很快了,这就算结束了,但是最最最重要的是优化代码,这个一句话两句话根本说不明白,我也是个水菜鸟,所以这里就不瞎说了,大家自己看书学习吧,哈哈,反正觉得效率才是硬道理,效率和稳定,我觉得是核心种的核心,无论什么原因这两个都不能变。
具体方法,作者提到了一个链表,这个就是数据结构的应用,希望以后设计操作系统的时候能用到更多的其他数据结构来优化程序性能

高分辨率

本节简介:高分辨率的使用

高分辨率

高分辨率,这个是个必须要说的话题,因为好像没有啥电脑显示器用 $320\times 200$ 的分辨率了,所以提高分辨率势在必行。。哈哈
一个重要的操作就是查询VBE
中断编号 0x10
参数ES:DI指向的地址将会被VBE的内容覆盖
参数AX存储一个返回值,如果是0x004f就是有VBE不是这个值,就意味着VBE不可用
画面模式信息:

然后就是现实键盘输入,移动对话框,不在赘述

多任务(I)

本节简介:多任务

多任务一

今天的内容是多任务,所谓多任务就是“同时”执行多个任务,作者写的是在单核cpu上轮流执行多个任务,在短时间进行各种切换,但是实际上还是串联的,虽然表面上看的是同时执行,不知道现在的多核处理器是如何完成多任务的,我一会儿google一下,写出来,应该有很些不同。

先说今天的内容,对于任务切换,处理器是有代价的,也就是说,切来切去是要消耗时钟周期的,例如如果切换一次需要消耗额外的1s但是你每2s切换一次,也就是执行一个任务一共用了3s(因为任务完成后总要切换,所以就可以把切换时间算入总的执行时间)但真正的执行只是2s这样效率只有66.67%如果1s切换一次,就是有50%了。。所以几秒切一次很重要,如果10000s切换一次,效率近似与99.99%但是估计使用者会等疯,卡死了。

上面的1s切换时间是我扯淡的,没有那么慢,书上说千分之一秒,估计现在的处理器会更快。

重要概念:TSS
TSS:(Task Status Segment)任务状态段,用于记录任务的执行情况,和已经执行的进度,就像打游戏的读档,就可以直接玩上次存档的地方了,而不是从头接着打,而这个档就是TSS负责记录各种状态(装备等级,任务进度什么乱七八糟的)任务切换时查询TSS然后继续运行。

CPU特殊机制:执行带有段地址的命令式就会去GDT种查询一下,看是否是另外的程序,如果是,就进行任务切换。

GDT是个很重要的管理者,应该去画个图把整个体系操作系统体系画出来,这样以后设计的时候就能参考蓝图一步步设计了。看完这本书就去画。

提高运行速度,其实作者把修改刷新显示频率作为第一步优化,我觉得这个完全在定时器的时候就应该修改系统原始设计,因为屏幕的刷新率一般就那么几种,刷的过快没效果而且消耗资源,过慢当然也不行,既然写到这,那就算优化吧,边做边修改,也是一种好方法,没谁能一开始就制定天衣无缝的计划。

第二点提高速度很精彩,就是把任务切换定时器从缓冲区中分离,也就是键盘,鼠标,普通定时器等中断在FIFO的缓冲区,而任务切换的定时器不在其中,不论其中是否有未处理的中断时间一到,马上切换,但是,问题是,如果FIFO中又任务A的中断处理,但是此时任务A恰好切换到B。这岂不是坑爹了。不太明白,看看查查资料有没有详细的介绍。

还有就是return不能随便用,在开发系统的时候,还有函数调用时对EFLAG寄存器的影响,都是应该注意的。

多任务(II)

多任务二

今天是多任务第二讲,比昨天的层次更高,更加接近实际使用的设计。

首先是任务的自动化,所谓任务就是正在执行的程序,程序成千上万,可以自己随意安装,所以,管理就不能靠人工修改代码,而要自动管理,自动分配空间,自动注册相关设置,所以就要建立一个结构体来管理众多任务,记录当前任务的信息。
任务的休眠,有些任务需要等待数据,或者等待中断程序,等待的时候CPU等就会跟着闲着,这可不好,我们必须让CPU尽可能的处于运行状态,所以就有了任务的休眠,让一个等待的任务不再处于运行状态,而让那些高计算量的任务过来运行,而当任务休眠时,数据到来,马上唤醒任务,让他继续执行。就是这样的。

接下来就涉及到任务的优先级了,谁应该有更多的系统资源,比如你听音乐和写程序,一输入代码音乐就卡这可不好,分成1,2,3,4,5…12各种等级,我们可以音乐12级,文本编辑5级,这样优先执行音乐,音乐不会卡,文本就算卡也没办法。。自找的。。跟我们音乐大哥抢资源是抢不过滴。

其实这并不是科学的划分,如果超过12个任务,必然有同级任务存在,那到时候怎么办呢,音乐和电影抢资源,谁应该赢呢,即使这个问题解决了,优先级运行时间划分也存在问题,如果安装1级1s(假设),2级2秒,10级十秒这种划分也不是很科学的,因为2级和1级只差一级,但2级运行时间是1级的一倍,而3级和2级之间也只差一级,但却只有1.5倍所以这也是个问题。。这个姑且放一下,以后再考虑。

作者解决音乐和电影的方法是把等级再分层,类似与金字塔结构,每层有不同的任务,任务永远只在顶层切换,而不会轮到下层,知道上层都运行结束,也就是顶层消灭了,才会轮到现在的顶层,之前的第二层。而如果高层来人了,当前层当前运行任务结束后,无条件切换到上层。

都是些理论,不知道现在实际种工作的操作系统是怎么弄得,查到资料了再写出来。

一个半成品

本试验品和书中系统并不完全一致,是在原有基础上按照自己的习惯而成,由于水平和工作原因,未完成内存管理和文件系统,有兴趣者可以通过以下网址(https://github.com/TonyTan1991/Jupiter)[https://github.com/TonyTan1991/Jupiter],欢迎留言,欢迎参与项目并改进。

最后修改日期:2020年9月15日

说点什么

avatar
  Subscribe  
提醒