您好,欢迎访问三七文档
当前位置:首页 > 行业资料 > 其它行业文档 > 30天自制操作系统日志第13天
操作系统实验日志学号20160810520姓名甘昆禄专业年级班级智能1601实验日期2018.12.19实验项目第13天:定时器(2)一、实验主要内容1、简化字符串显示将HariMain中显示字符串的步骤:1:设置背景色2:描绘字符串3:刷新画面我们简化这一过程,封装为一个用于字符串显示系列操作的putfont_asc_sht函数。这时,主函数HariMain里只需调用传参即可:2、调整FIFO缓冲区(1)在HariMain主函数中我们判断计时器的状态,是否超时,就是判断缓冲区有无信息进来。其中的判断部分:倘若有一百多个计时器同时运行,我们的if语句中的岂不是要写一百多个连接判断,由此,我们将计时器的缓冲区设置为一个即可,在超时的时候,我们往FIFO缓冲区写入不同的数据即可区分哪个计时器超时了。修改后的计时器超时的判断:3、测试性能就是计算一下我们的程序花了多少时间,因为每次都不同,而且时间很短,所以我们计算10秒钟内for循环进行多少次就可以了。那就在for循环里加上count++就可以了。我们不需要计时器一开始就在画面上不断的刷新count的值,我们等待10秒后再显示出来。需要注意的是count在到达3秒的时候需要重新复位一下,原因是电脑在初始化的时候只要某些条件改变,花费的时间就会大大增加,在计时的时候,我们鼠标键盘的输入的时候发送的中断,也会影响计时器的速度。也就是算7秒内count增加的次数,就可以估算大概时间。修改count的显示:然后我们发现这几次的改进count的值越变越大了,也就是相同的7秒时间内,count++计算的次数变多了,速度越来越快了。4、调整FIFO缓冲区(2)我们接收键盘鼠标的输入,以及计时器状态的读取,都频繁与这三个缓冲区交互,我们尝试将缓冲区的个数设置为一个,但是设置为一个缓冲区如何区分是键盘还是鼠标或是计时器的数据呢,我们可以使用汇编中的段这一概念,将一段连续的内存空间划分为独立的三段,一段用于键盘输入,一段用于鼠标输入,一段用于计时器。计时器的处理我们需要继续改进,为了知道下一个即将超时的计时器,我们需要频繁的移动数组进行检查,我们其实可以在每个计时器结构体体添加一个指向下一个即将超时的计时器指针,链表的优势就体现出来了。/*fifo.c*/structFIFO32{int*buf;intp,q,size,free,flags;};我们修改fifo.c,以后不再使用FIFO8了,而是使用FIFO32:/*FIFO*/#includebootpack.h#defineFLAGS_OVERRUN0x0001voidfifo32_init(structFIFO32*fifo,intsize,int*buf)/*FIFO缓冲区的初始化*/{fifo-size=size;fifo-buf=buf;fifo-free=size;/*空*/fifo-flags=0;fifo-p=0;/*写入位置*/fifo-q=0;/*读取位置*/return;}intfifo32_put(structFIFO32*fifo,intdata)/*给FIFO发送数据并储存在FIFO中*/{if(fifo-free==0){/*没有空余空间,溢出*/fifo-flags|=FLAGS_OVERRUN;return-1;}fifo-buf[fifo-p]=data;fifo-p++;if(fifo-p==fifo-size){fifo-p=0;}fifo-free--;return0;}intfifo32_get(structFIFO32*fifo)/*从FIFO取得一个数据*/{intdata;if(fifo-free==fifo-size){/*当缓冲区为空的情况下返回-1*/return-1;}data=fifo-buf[fifo-q];fifo-q++;if(fifo-q==fifo-size){fifo-q=0;}fifo-free++;returndata;}intfifo32_status(structFIFO32*fifo)/*报告已经存储了多少数据*/{returnfifo-size-fifo-free;}mouse.c修改:/*鼠标控制*/#includebootpack.hstructFIFO32*mousefifo;intmousedata0;voidinthandler2c(int*esp)/*来自PS/2鼠标的中断*/{intdata;io_out8(PIC1_OCW2,0x64);/*把IRQ-12接收信号结束的信息通知给PIC1*/io_out8(PIC0_OCW2,0x62);/*把IRQ-02接收信号结束的信息通知给PIC0*/data=io_in8(PORT_KEYDAT);fifo32_put(mousefifo,data+mousedata0);return;}#defineKEYCMD_SENDTO_MOUSE0xd4#defineMOUSECMD_ENABLE0xf4voidenable_mouse(structFIFO32*fifo,intdata0,structMOUSE_DEC*mdec){/*将FIFO缓冲区的信息保存到全局变量里*/mousefifo=fifo;mousedata0=data0;/*鼠标有效*/wait_KBC_sendready();io_out8(PORT_KEYCMD,KEYCMD_SENDTO_MOUSE);wait_KBC_sendready();io_out8(PORT_KEYDAT,MOUSECMD_ENABLE);/*顺利的话,ACK(0xfa)会被发送*/mdec-phase=0;/*等待鼠标的0xfa的阶段*/return;}intmouse_decode(structMOUSE_DEC*mdec,unsignedchardat){if(mdec-phase==0){/*等待鼠标的0xfa的阶段*/if(dat==0xfa){mdec-phase=1;}return0;}if(mdec-phase==1){/*等待鼠标第一字节的阶段*/mdec-buf[0]=dat;mdec-phase=2;return0;}if(mdec-phase==2){/*等待鼠标第二字节的阶段*/mdec-buf[1]=dat;mdec-phase=3;return0;}if(mdec-phase==3){/*等待鼠标第二字节的阶段*/mdec-buf[2]=dat;mdec-phase=1;mdec-btn=mdec-buf[0]&0x07;mdec-x=mdec-buf[1];mdec-y=mdec-buf[2];if((mdec-buf[0]&0x10)!=0){mdec-x|=0xffffff00;}if((mdec-buf[0]&0x20)!=0){mdec-y|=0xffffff00;}mdec-y=-mdec-y;/*鼠标的y方向与画面符号相反*/return1;}/*应该不可能到这里来*/return-1;}bootpack.h中对应TIMER结构体修改如下:structTIMER{unsignedinttimeout,flags;structFIFO32*fifo;intdata;};修改后HariMain的判断:这次改善以后循环里的判断语句只用判断一次了,就提升了不少的速度。5、加快中断处理上次我们有些中断处理是会有移位操作,对于定时器少的情况,移位对性能的影响不大,但对于多任务,很多应用程序同时运行,每个程序都使用定时器,如果还使用移位的话,就有点浪费时间了。尤其在中断处理程序中进行大量的移位,更是不优雅。为了加快中断处理,我们考虑把中断处理中的移位处理去掉,可是怎么去呢?当各个timer是以顺序表的结构存在时,每当一个timer超时,管理剩下的timer就必须移位。那怎么办?解决方法应该从数据结构入手。我们知道,顺序表的数据结构的优点是可以随机地插入,但缺点就是插入和删除很复杂。而链表的则插入和删除非常便利。并且此处timer的增删不是固定的顺序,而是需要挨个比较超时时间,所以这里顺序表的结构没有优势,此处换成链表。我们用structTIMER表示一个定时器structTIMER{structTIMER*next;unsignedinttimeout,flags;structFIFO32*fifo;intdata;};我们的具体做法是,当一个定时器超时时,通过next将下一个要超时的定时器的地址赋给存放所有定时器地址的数组的第一位,那么下次超时出现时,数组的第一位存的就是当前发生超时的定时器的地址。也就是我们不必再维护一个存有正在使用定时器地址的数组了,只需设一个变量t0用来存储下一个要超时的定时器的地址,所有这些简化都要归功于一开始创建各个定时器时,根据超时时间,用各自的属性next,以链表的方式从早到晚链接起来,设想一下,第一个(也就是最早的)定时器超时了,将它的next赋给t0,那么t0存的就是下一个超时的定时器的地址,依次进行下去。这样就简化了不少,而且逻辑清晰。6、使用“哨兵”最后作者介绍了一个常用程序技巧——”哨兵“。经过上面的优化后,为了产生上面的线性表,每当一个定时器被定义出来时,都要找准自己的位置,插入到线性表中,这时会有四种情况:1、插入的定时器是第一个定时器2、插入位置在线性表的最前面3、插入位置在中间4、插入位置在线性表最后面我们每定义一个定时器,都要进行四种情况的判断,有点不优雅。如果使用了哨兵,会去掉情况1和4。在初始化时,将时刻0xffffffff(这个时刻是最大的超时时间)的定时器连到最后一个定时器上,它一直处于后面,只是个附带物,是个留下来看家的留守者,这个留守者正是”哨兵“。由于有超时时间为0xffffffff的定时器——哨兵存在,所以情况1不会存在,同样,要插入的定时器也不会插在最后,哨兵已经上岗了。这样四种情况的判断缩减到了两种,有利于提升性能。二、遇到的问题及解决方法1、这个count和上一天内容的timerctl.count有什么区别?这个count是一个全局变量,每进行一次for(;;)的循环就会自加一,跟中断处理没有直接关系,中不中断count都会自加一,只是中断会影响for(;;)自循环的速度从而影响count++的速度。而timerctl.count是一个结构体的成员,定时器每产生一次中断(这里设定好的是1秒100次),进入到中断函数中,timerctl.count就会自加一,是一个用来作为时间进度的一个变量。两者没有关系。三、程序设计创新点1、一个简单的开机动画(雏形,主要做好跟今天定时器有关部分)截几个图,看看效果吧刚运行时显示两个窗口(后期改为扑克图片,这里用窗口凑合):然后用定时器让这两个窗口不断的移动,产生重叠交错的效果,如下:这样一段时间(自定义的开机动画时间)后,界面到输入密码窗口(这个也后期实现),如下:然后点击该界面进入用户视图,来到操作系统,如下:主要思路:新增加了三个图层,两个作动画,一个做密码图层,完成之后这三个图层就置为-1隐藏。用定时器使动画窗口定时自动移动,每到一次定时,坐标就改变,同时改变显示。设置变量ix,iy用来表示x,y变化方向,当移动到下方边界时,ix应该置为-1,使得窗口往上移动,而不是继续向下,其他同理。主要代码及说明:多设置的一些图层:接下来就进行图层初始化,和前面图层设置一样(密码图层大小为全屏),不再赘述制作密码窗口略有不同,如下:全图层为一种显色,为动画的背景,动画后再增加文字和功能各图层初始位置和高度如下(动画结束前,鼠标低于密码图层,不显示鼠标):下面是动画功能的实现:其中dhx,dhy是动画窗口一的移动后的坐标(dhx2等是窗口2的,其他同理)还有ix,iy是用来判断移动方向的,yc用来判断移动的次数(有计
本文标题:30天自制操作系统日志第13天
链接地址:https://www.777doc.com/doc-5996374 .html