自制操作系统日志——第十二天

news/2024/5/17 8:00:54/文章来源:https://blog.csdn.net/qq_43696276/article/details/126033981

自制操作系统日志——第十二天

从今天开始,我们将花费两天的时间来进行计算机中定时器的制作。有了定时器后,才能够为程序和cpu更加便利的进行计时。可能会稍难一些了!!! 做好准备,冲!!!!


文章目录

  • 自制操作系统日志——第十二天
  • 一、初识定时器
  • 二、设定多个计时器,并优化中断程序
  • 总结


一、初识定时器

定时器,就是指每隔一段时间就会发送一个中断信号给cpu。正因,有了定时器,cpu才不用花费额外的功夫去计量时间!

如果cou不使用计时器的话,那么cpu如果需要计算时间,只能怪依靠记住每一条指令的执行时间了。例如,向寄存器写入常数的mov 需要一个时钟周期,而某某函数又需要105个时钟周期等等。而这里的时钟周期,又并非是一个统一的固定值,例如cpu的主频100Mhz,一个始时钟周期是10纳秒;如果cpu主频是200MHZ 则一个时钟周期是5纳秒。。。

因此,即使使用cpu计量时间的话,倘若某个程序中时间计量出错,则就会导致需要使用时间的程序(例如:记录时间的电子表)变快或者变慢;且也不能使用hlt指令了,因为一旦cpu休眠就无法为程序计算时间了!!!

因此,我们需要进行管理定时器。至于如何管理呢? 我们只需对PIT(可编程的间隔型定时器)进行设定即可,而PIT又与中断管理表PIC的IRQ-0相连接。因此,我们只需增加对应的中断处理程序即可:使用的定时器编号应该是8254芯片。

PIT的设定规则:

  • AL = 0x34; OUT(0x43, AL)
  • AL=中断周期的低8位; OUT(0x40, AL)
  • AL=中断周期的高8位; OUT(0x40,AL)

这里,实际中断产生的频率 = 主频 / 设定数。
对于我们这次模拟的来说,如果设定值是1000 则频率为1.19318khz ;如果是11932的话 则为 100hz 约为10ms。
以下开始编写对应的程序:
timer.c

//定时器
#include "bootpack.h"#define PIT_CTRL    0x0043
#define PIT_CNT0    0x0040
//初始化,将中断周期设定为11932=0x2e9c;则实际中断频率= 主频/设定数 = 100HZ ;(这里主频约为11931800左右)
void init_pit(void)
{io_out8(PIT_CTRL, 0X34);io_out8(PIT_CNT0, 0x9c);io_out8(PIT_CNT0, 0x2e);return;
}void inthandler20(int *esp)
{io_out8(PIC0_OCW2, 0x60);//IRQ-0信号接收完后告知PICreturn;
}

naskfunc.nas:

 _asm_inthandler20:push espush dsPUSHADmov EAX,ESPPUSH EAXMOV AX,SSMOV DS,AXMOV ES,AXCALL _inthandler20POP EAXPOPADPOP DSPOP ESIRETD

dsctbl.c

    set_gatedesc(idt + 0x20, (int) asm_inthandler20, 2 << 3, AR_INTGATE32);set_gatedesc(idt + 0x21, (int) asm_inthandler21, 2 << 3, AR_INTGATE32);set_gatedesc(idt + 0x27, (int) asm_inthandler27, 2 << 3, AR_INTGATE32);set_gatedesc(idt + 0x2c, (int) asm_inthandler2c, 2 << 3, AR_INTGATE32);

主函数:

	init_pit();io_out8(PIC0_IMR, 0xf8); /* 开放PIC1和键盘中断(11111000),键盘是IRQ1 */io_out8(PIC1_IMR, 0xef); /* 开放鼠标中断(11101111) ,鼠标是IRQ12*/

make run 即可!!!

这里为了进一步的展示计时器的功能,我们制作一个计时器:
Bootpack.h:

//timer.c
struct TIMERCTL
{unsigned int count;
};
extern struct TIMERCTL timerctl;
void init_pit(void);
void inthandler20(int *esp);

timer.c:

struct TIMERCTL timerctl;#define PIT_CTRL    0x0043
#define PIT_CNT0    0x0040
//初始化,将中断周期设定为11932=0x2e9c;则实际中断频率= 主频/设定数 = 100HZ ;(这里主频约为11931800左右)
void init_pit(void)
{io_out8(PIT_CTRL, 0X34);io_out8(PIT_CNT0, 0x9c);io_out8(PIT_CNT0, 0x2e);timerctl.count = 0;return;
}void inthandler20(int *esp)
{io_out8(PIC0_OCW2, 0x60);//IRQ-0信号接收完后告知PICtimerctl.count++;return;
}

主函数:

	for(;;)   {sprintf(s, "%010d", timerctl.count);boxfill8(buf_win, 160, COL8_C6C6C6, 40, 28, 119, 43);putfonts8_asc(buf_win, 160, 40, 28, COL8_000000, s);sheet_refresh(sht_win, 40, 28, 120, 44);

make run一下后,可以看出大约每秒增加100:
在这里插入图片描述
进一步的,我们就可以利用定时器做一个基准测试程序,用于计算某个程序耗时多久。但是,接下来,先让我们做一下超时的功能试试手:

首先,在bootpack.c中添加如下与超时有关的信息:

struct TIMERCTL
{unsigned int count;unsigned int timeout; //用这个记录离超时还要多久时间,一旦变为了0,就向缓冲区发送数据struct FIFO8 *fifo;unsigned char data;
};

然后让我们修改一下timer.c函数吧:

void init_pit(void)
{io_out8(PIT_CTRL, 0X34);io_out8(PIT_CNT0, 0x9c);io_out8(PIT_CNT0, 0x2e);timerctl.count = 0;timerctl.timeout = 0;return;
}void inthandler20(int *esp)
{io_out8(PIC0_OCW2, 0x60);//IRQ-0信号接收完后告知PICtimerctl.count++;if(timerctl.timeout > 0)//如果超时{timerctl.timeout--;if(timerctl.timeout == 0){fifo8_put(timerctl.fifo, timerctl.data);}}return;
}void settimer(unsigned int timeout, struct FIFO8 *fifo, unsigned char data)
{int eflags;eflags = io_load_eflags();io_cli(); //此时还没有完全结束IRQ0因此,如果此时又中断进入会引起混乱的!!timerctl.timeout = timeout;timerctl.fifo = fifo;timerctl.data = data;io_store_eflags(eflags);return;
}

然后再改一下主函数部分:

	struct FIFO8 timerfifo;char s[40], keybuf[32], mousebuf[128], timebuf[8];fifo8_init(&timerfifo, 8, timebuf);settimer(1000, &timerfifo, 1);init_keyboard();enable_mouse(&mdec);for(;;)   {io_cli(); //IF=0if (fifo8_status(&keyfifo) + fifo8_status(&mousefifo) + fifo8_status(&timerfifo) == 0){}else if (fifo8_status(&timerfifo) != 0){i = fifo8_get(&timerfifo);//首先读入,为了设定起始点io_sti();putfonts8_asc(buf_back, binfo->scrnx, 0, 64, COL8_FFFFFF, "10[sec]");sheet_refresh(sht_back, 0, 64, 56, 80);}

由代码中可知,我们设定的是每10s发送一次信息,因此我们可以再约1000时候看到信息的显现:
在这里插入图片描述

二、设定多个计时器,并优化中断程序

为方便同时给多个程序进行即使准备,我们将建立起多个计时器。

这里,我们再设置多个计时器的过程中,为减少后续在此中断过程中所用的时间,我们将参照图层那一部分的构造进行设计。首先,我们来涉及一下结构体变量:
bootpack.h:

#define MAX_TIMER 500
struct TIMER
{unsigned int timeout, flags;//flags表示各个定时器的状态; timeout的含义,这里指予定时刻,通过settime中赋予的超时时间+当前时刻来计算,当到达啥时刻时算超时struct FIFO8 *fifo;//用于将超时的信息传给缓冲区。unsigned char data;
};
struct TIMERCTL
{unsigned int count, next, using;//using用于记录现在有几个定时器处于活动中struct TIMER *timers[MAX_TIMER];struct TIMER timers0[MAX_TIMER];
};

这里,多设计一个timers,用于将计时器按照到达时刻的先后顺序进行排序。然后再多加上一个next的参数,用于指向下一个超时的时刻,以便加快非超时时的判断过程。

然后修改timer.c:
首先进行修改pit的初始化:

void init_pit(void)
{int i;io_out8(PIT_CTRL, 0X34);io_out8(PIT_CNT0, 0x9c);io_out8(PIT_CNT0, 0x2e);timerctl.count = 0;timerctl.next = 0xffffffff;//因此最初没有正在运行的定时器timerctl.using = 0;for(i = 0; i < MAX_TIMER; i++){timerctl.timers0[i].flags = 0; //未使用}return;
}

然后,修改alloc函数,将已分配的计时器存入再timers0当中,并准备后续的timers排序做准备:

struct TIMER *timer_alloc(void)
{int i;for(i = 0; i < MAX_TIMER; i++){if(timerctl.timers0[i].flags == 0){timerctl.timers0[i].flags = TIMER_FLAGS_ALLOC;return &timerctl.timers0[i];}}return 0;//没找到
}

然后,进行修改settime,设定timers中的计时器,按照到达时刻的顺序的先后进行排序,到达时刻越晚的排在越后面:

//主程序中对于pit计时器是先注册并设置完一个之后再弄另一个的!!
void timer_settime(struct TIMER *timer, unsigned int timeout)
{int e, i, j;timer->timeout = timeout + timerctl.count;//从现在开始后多少秒以后算超时timer->flags = TIMER_FLAGS_USING;e = io_load_eflags();io_cli();//搜索注册位置for(i = 0; i < timerctl.using; i++)  //找到比当前计时器拟到达时刻还要晚的计时器,然后插入在此前面{if(timerctl.timers[i]->timeout >= timer->timeout){break;}}//i号之后全部移一位for(j = timerctl.using; j > i; j--){timerctl.timers[j] = timerctl.timers[j-1];}timerctl.using++;//插入空位timerctl.timers[i] = timer;timerctl.next = timerctl.timers[0]->timeout;io_store_eflags(e);return;
}

设定中断处理程序:

void inthandler20(int *esp)
{int i, j;io_out8(PIC0_OCW2, 0x60);//IRQ-0信号接收完后告知PICtimerctl.count++;if(timerctl.next > timerctl.count){return; //还不到下一个时刻,因此返回}for (i = 0; i < timerctl.using; i++) {// timers的定时器都是活动中的因此不需要确认flagsif(timerctl.timers[i]->timeout > timerctl.count ){break;}//超时timerctl.timers[i]->flags = TIMER_FLAGS_ALLOC;fifo8_put(timerctl.timers[i]->fifo, timerctl.timers[i]->data);}//正好有i个计时器所以移位timerctl.using -= i;for(j = 0; j < timerctl.using; j++){timerctl.timers[j] = timerctl.timers[i+j];}if(timerctl.using > 0){timerctl.next = timerctl.timers[0]->timeout;}else{timerctl.next = 0xfffffff;}return;
}

然后,我们继续修改一下主程序,实现三个计时器的功能,并且实现光标的闪烁!

void HariMain(void)
{//鼠标键盘,bootinfo等等的定义struct BOOTINFO *binfo = ( struct BOOTINFO *) ADR_BOOTINFO;struct FIFO8 timerfifo, timerfifo2, timerfifo3;char s[40], keybuf[32], mousebuf[128], timerbuf[8], timerbuf2[8], timerbuf3[8];struct TIMER *timer, *timer2, *timer3;略:io_out8(PIC0_IMR, 0xf8); /* 开放PIC1和键盘中断(11111000),键盘是IRQ1 */io_out8(PIC1_IMR, 0xef); /* 开放鼠标中断(11101111) ,鼠标是IRQ12*/fifo8_init(&timerfifo, 8, timerbuf);timer = timer_alloc();timer_init(timer, &timerfifo, 1);timer_settime(timer, 1000);fifo8_init(&timerfifo2, 8, timerbuf2);timer2 = timer_alloc();timer_init(timer2, &timerfifo2, 1);timer_settime(timer2, 300);fifo8_init(&timerfifo3, 8, timerbuf3);timer3 = timer_alloc();timer_init(timer3, &timerfifo3, 1);timer_settime(timer3, 50);for(;;)   {sprintf(s, "%010d", timerctl.count);boxfill8(buf_win, 160, COL8_C6C6C6, 40, 28, 119, 43);putfonts8_asc(buf_win, 160, 40, 28, COL8_000000, s);sheet_refresh(sht_win, 40, 28, 120, 44);io_cli(); //IF=0if (fifo8_status(&keyfifo) + fifo8_status(&mousefifo) + fifo8_status(&timerfifo)+ fifo8_status(&timerfifo2) + fifo8_status(&timerfifo3) == 0){else if (fifo8_status(&timerfifo) != 0){i = fifo8_get(&timerfifo);//首先读入,为了设定起始点io_sti();putfonts8_asc(buf_back, binfo->scrnx, 0, 64, COL8_FFFFFF, "10[sec]");sheet_refresh(sht_back, 0, 64, 56, 80);}else if(fifo8_status(&timerfifo2) != 0){i = fifo8_get(&timerfifo2); io_sti();putfonts8_asc(buf_back, binfo->scrnx, 0, 80, COL8_FFFFFF, "3[sec]");sheet_refresh(sht_back, 0, 80, 48, 96);} else if (fifo8_status(&timerfifo3) != 0) { //模拟光标i = fifo8_get(&timerfifo3);io_sti();if (i != 0) {timer_init(timer3, &timerfifo3, 0); //然后设置为0boxfill8(buf_back, binfo->scrnx, COL8_FFFFFF, 8, 96, 15, 111);} else {timer_init(timer3, &timerfifo3, 1); //然后设置为1boxfill8(buf_back, binfo->scrnx, COL8_008484, 8, 96, 15, 111);}timer_settime(timer3, 50);sheet_refresh(sht_back, 8, 96, 16, 112);}			}}
}	

以上就完成了对应的程序,让我们make run一下:
在这里插入图片描述
在这里插入图片描述


总结

至此,我们完成了计时器的第一部分,还有一点点需要继续优化的地方,明天加油吧!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_6517.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ConcurrentLinkedQueue解析

概述 ConcurrentLinkedQueue实际对应的是LinkedList,是一个线程安全的无界队列&#xff0c;但LinkedList是一个双向链表&#xff0c;而ConcurrentLinkedQueue是单向链表。ConcurrentLinkedQueue线程安全在于设置head、tail以及next指针时都用的cas操作&#xff0c;而且node里的…

00Android studio安装

目录一.下载Android studio二.安装Android studio三.打开软件一.下载Android studio 官网&#xff1a;https://developer.android.google.cn/studio 下载&#xff1a;由于是国外的网站&#xff0c;国内下载会比较慢 二.安装Android studio 打开&#xff1a; 点击【Next】 点击…

猿创征文|瑞吉外卖——管理端_员工管理

个人名片&#xff1a; 博主&#xff1a;酒徒ᝰ. 专栏&#xff1a;瑞吉外卖 个人简介&#xff1a;沉醉在酒中&#xff0c;借着一股酒劲&#xff0c;去拼搏一个未来。 本篇励志&#xff1a;一本好书&#xff0c;就像高级武功秘籍一样&#xff0c;哪怕只是从里面领悟到个一招半势&…

C# StringBuilder 底层深入原理分析以及使用详解

目录前言什么是StringBuilderStringBuilder的成员StringBuilder增加元素原理StringBuilder扩容原理Capacity&#xff1a;1&#xff0c;元素数量&#xff1a;0Capacity&#xff1a;1&#xff0c;元素数量&#xff1a;1Capacity&#xff1a;2&#xff0c;元素数量&#xff1a;2Ca…

开学季征文|卷生卷死之新学期大学生自救指南!!!

你好&#xff0c;这里是前情提要 正所谓 “ 宁可卷死自己&#xff0c;也要卷死同学 ” &#xff0c;在这个万物皆卷的时代&#xff0c;“卷”似乎早已与我们变得不可分割血脉相融&#xff0c;有道是卷卷更健康。我也知道卷卷更好&#xff0c;可是天不遂人愿&#xff0c;因为疫情…

Redis_09_Redis集群实现Sentinel哨兵应对高可用

文章目录一、前言二、Sentinel原理2.1 Sentinel原理2.2 Sentinel选主2.3 Sentinel功能小结三、Sentinel实践3.1 Sentinel配置3.2 实践&#xff1a;Sentinel基本使用3.2.1 实践&#xff1a;Sentinel搭建3.2.2 实践&#xff1a;主节点宕机之后的选主过程(Sentinel保证高可用)3.2.…

ERROR 2003 (HY000) Can‘t connect to MySQL server on ‘localhost3306‘ (10061)解决办法

这个解决办法是我根据网上一系列的方法准备突然成功的&#xff0c;所以我想可能是由于本身其不稳定造成的 首先&#xff0c;我在官网上下载了mysql文件&#xff0c;这个网上随便找都能找到怎么下载的 然后打开文件后&#xff0c;发现没有my.ini 所以我就找了一个文档放了进去…

【线性代数】MIT Linear Algebra Lecture 6: Column space and nullspace

Author&#xff5c; Rickyの水果摊 Time &#xff5c; 2022.9.12 Lecture 6: Column space and nullspace Lecture Info Instructor: Prof. Gilbert Strang Course Number: 18.06 Topics: Linear Algebra Official Lecture Resource: Resource Index of Linear Algebra …

HCIP-双机热备

一,双机热备原理 1.1双机热备简介FW部署在网络出口位置时,如果发生故障会影响到整网业务。为提升网络的可靠性,需要部署两台FW并组成双机热备。双机热备需要两台硬件和软件配置均相同的FW。两台FW之间通过一条独立的链路连接,这条链路通常被称之为“心跳线”。两台FW通过心…

美团面试官:高并发、任务执行时间短的业务怎样使用线程池?

前言 无论是互联网大厂还是一些中游公司的面试基本都会问到多线程与并发编程的知识&#xff0c;所以今天小编在这里做了关于这方面知识的一个笔记分享送给即将面试跳槽的程序员朋友们&#xff01; 首先关于多线程与并发的知识总结了一个思维导图&#xff0c;分享给大家 如果你…

【Pytorch】2022 Pytorch基础入门教程(完整详细版)

一、Pytorch 1.1 简介 Pytorch是torch的python版本&#xff0c;是由Facebook开源的神经网络框架&#xff0c;专门针对 GPU 加速的深度神经网络&#xff08;DNN&#xff09;编程。Torch 是一个经典的对多维矩阵数据进行操作的张量&#xff08;tensor &#xff09;库&#xff0…

从校园智能门锁预见万物互联的未来

随着物联网、移动互联网、大数据、云计算等信息技术的创新发展&#xff0c;被信息化驱动的教育行业实现了技术深化融合&#xff0c;智慧校园正逐步落地生根、开花结果。校园智能门锁是智慧校园的基础载体&#xff0c;也是实现教育信息化的基础载体。 NO.1校园智能门锁构建一体化…

【CSDN竞赛第五期】“三而竭”采用等比求和公式法的思考

原题题目 一鼓作气再而衰三而竭。 小艺总是喜欢把任务分开做。小艺接到一个任务&#xff0c;任务的总任务量是n。 第一天小艺能完成x份任务&#xff0c;第二天能完成x/k ... ...第t天能完成x/(k^(t-1))。 小艺想知道自己第一天至少完成多少才能完成最后的任务。 公式推导 第一…

[项目管理-25]:高效沟通的利器,结构思考力与树形结构化表达

作者主页(文火冰糖的硅基工坊)&#xff1a;文火冰糖&#xff08;王文兵&#xff09;的博客_文火冰糖的硅基工坊_CSDN博客 本文网址&#xff1a; 目录 前言&#xff1a; 第1章 结构化思考力概述 1.1 非结构化思考力的问题与结构化思路力的好处 1.2 什么是结构化思路力 1.3…

mysql中的mvcc机制

MVCC多版本并发控制 简述MySQL锁 在InnoDB引擎下&#xff0c;按锁的粒度分类&#xff0c;可以分为行锁和表锁。 行锁实际上是作用在索引之上的。当我们的SQL命中了索引&#xff0c;那锁住的就是命中条件内的索引节点(这就是行锁)&#xff0c;如果没有命中索引&#xff0c;那锁…

MySQL的主从复制

MySQL的主从复制 1、概述 主从复制是指将主数据库的 **DDL &#xff08;数据定义语句&#xff09;**和 **DML &#xff08;数据操作语句&#xff09;**操作通过二进制日志传到从库服务器中&#xff0c;然后在从库上对这些日志重新执行&#xff08;也叫重做&#xff09;&#…

【flask进阶】Flask实现自定义分页(python web通用)

&#x1f4cb; 个人简介 &#x1f496; 作者简介&#xff1a;大家好&#xff0c;我是阿牛&#xff0c;全栈领域新星创作者。&#x1f61c;&#x1f389; 支持我&#xff1a;点赞&#x1f44d;收藏⭐️留言&#x1f4dd;&#x1f4e3; 系列专栏&#xff1a;flask框架快速入门&a…

springboot学生综合素质测评系统java

本学生综合素质测评系统主要包括登录管理员模块如下&#xff1a;个人中心、学生管理、素质学习管理、我的学习管理、在线论坛、试卷管理、试题管理、系统管理、考试管理&#xff0c;学生&#xff1a;个人中心、我的学习管理、我的收藏管理、考试管理&#xff0c;前台首页&#…

主流的CPU架构

cpu架构 CPU架构是CPU厂商给属于同一系列的CPU产品定的一个规范&#xff0c;是为了区分不同类型CPU的重要标示。目前市面上的CPU分类主要分有两大阵营&#xff0c;一个是intel、AMD为首的复杂指令集CPU&#xff0c;另一个是以IBM、ARM为首的精简指令集CPU。两个不同品牌的CPU&a…

数据库系统概念(第1章 引言)

数据库系统概念(第1章 引言)目录1.1 数据库系统应用1.2 数据库系统的目标1.3 数据视图1.3.1 数据模型(data model)1.3.2 关系数据模型1.3.3 数据抽象(data abstraction)1.3.4 实例和模式1.4 数据库语言1.4.1 数据定义语言1.4.2 SQL数据定义语言1.4.3 数据操纵语言1.4.4 SQL数…