MacOS环境-手写操作系统-25-实现定时器

简介: MacOS环境-手写操作系统-25-实现定时器

实现定时器

1.简介

当指定的时间过去后 系统会触发你给定的回调函数


Timer功能实在是太重要了 如果没有定时器 操作系统很多任务都做不了


至少你编程画个时钟 搞个闹钟程序什么的 你就没法实现


从这节开始 我们看看timer功能是怎么实现的


2.代码

主控制器的IRQ0对应的就是时钟中断


只要我们做好相关配置 那么在指定间隔内 IRQ0导线就会像CPU发送中断信号


首先我们代码要做的是初始化8259A时 打开这一中断功能


在内核实现的汇编部分做如下修改(kernel.asm)


init8259A:
....
mov  al, 11111000b ;允许键盘和时钟中断
     out  021h, al
     call io_delay
....

注意看 三个0表示打开主8259A的IRQ0 IRQ1 IRQ2三根信号线


IRQ0对应的就是时钟中断 打开IRQ0后 当中断信号发送到CPU时


我们需要CPU调用我们提供的中断程序 因此还需做以下修改


LABEL_IDT:
%rep  32
    Gate  SelectorCode32, SpuriousHandler,0, DA_386IGate
%endrep

;响应时钟中断的中断描述符
.020h:
    Gate SelectorCode32, timerHandler,0, DA_386IGate

.021h:
    Gate SelectorCode32, KeyBoardHandler,0, DA_386IGate

%rep  10
    Gate  SelectorCode32, SpuriousHandler,0, DA_386IGate
%endrep

.2CH:
    Gate SelectorCode32, mouseHandler,0, DA_386IGate

我们在中断向量表中 增加了一个用于调用时钟中断的描述符 该描述符对应的中断调用 名字叫timerHandler

我们继续看timerHandler的实现

_timerHandler:
timerHandler equ _timerHandler - $$
     push es
     push ds
     pushad
     mov  eax, esp
     push eax
     
     call intHandlerForTimer
     
     pop  eax
     mov  esp, eax
     popad
     pop  ds
     pop  es
     iretd

这段代码的实现跟以前的中断处理方法一样 先把寄存器压到堆栈上保存起来


然后调用C语言实现的中断处理函数intHandlerForTimer


在查看C语言对intHandlerForTimer的实现之前 我们需要搞清楚的是


如何配置时钟中断 使其在一秒内发生几次中断合适?


我们这里暂定1秒内产生100次中断 这种中断频率足以满足我们系统开发需求


于是我们需要做相应配置 配置的方法是 像8259A芯片的对应端口发送指定数据


首先需要向端口0x43发送一个数值0x34 紧接着向端口0x40发送两个数据0x9c,0x2d


这样时钟中断就能在1秒内发生100次了


配置代码如下(Timer.h)

#define PIT_CTRL   0x0043
#define PIT_CNT0   0x0040


void init_pit(void);

struct TIMERCTL {
    unsigned int count;
    unsigned int timeout;
    struct FIFO8 *fifo;
    unsigned char data;
};

struct TIMERCTL* getTimerController();

void settimer(unsigned int timeout, struct FIFO8 *fifo, unsigned char data);

init_pit(void) 就是用来实现时钟中断配置的


它的作用是向指定端口发送指定数据 TIMERCTL 数据结构叫时钟管理器


其中的count用来记录时钟中断发送了多少次


timeout用来计时 一旦timeout为0


管理器将触发指定动作 其他的数据在后面我们会详细解释


时钟中断的初始化实现


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;
}


初始化就如同前面我们所说的 向指定端口发送一些指定数据而已


设置好中断机制后 我们可以实现超时功能 也就是通过时钟管理器设置一个时间片


一旦时间片结束后 让时钟管理器触发我们提供的一个函数


这个时间片的大小对应的就是TIMECTRL结构体里面的timeout


C语言模块是怎么响应中断信号的


void intHandlerForTimer(char *esp) {
    io_out8(PIC0_OCW2, 0x60);
    timerctl.count++;
    if (timerctl.timeout > 0) {
        timerctl.timeout--;
        if (timerctl.timeout == 0) {
            fifo8_put(timerctl.fifo, timerctl.data);
        }
    }
    return;
}


每次响应中断信号时 先向8259A发送一个命令 命令的数值是0x60


要求8259A下次继续发送中断信号 如果不这么做 下次芯片就不给我们发送信号了


然后把时钟管理器的count计数加一 对应中断响应的次数


每次中断发送 我们都把时间片对应的数值减一


如果时间片减少到0 表明超时 此时向时钟管理器附带的FIFO队列写入一个数据


FIFO队列我们在实现鼠标响应的章节介绍过


这个队列的作用主要用来通知内核 超时发生了在内核的主循环里面会不停的监控时钟管理器这个队列是否为空


如果是空 那么内核就认为没有超时事件发生


如果队列里面有数据 那表明超时事件发生了


怎么设定超时对应的时间片呢?


我们在时钟管理器的实现中 增加一个settimer函数


void settimer(unsigned int timeout, struct FIFO8 *fifo, unsigned char data) {
    int eflags;
    eflags = io_load_eflags();
    io_cli();//暂时停止接收中断信号
    timerctl.timeout = timeout;//设定时间片
    timerctl.fifo = fifo;//设定数据队列,内核在主循环中将监控这个队列
    timerctl.data = data;
    io_store_eflags(eflags);//恢复接收中断信号
    return;
}
struct TIMERCTL* getTimerController() {
    return &timerctl;
}


io_load_eflags() 和 io_store_eflags() 这两个函数我们以前提到过


CPU会根据一系列状态信息来调整自己的运行状况


例如当前中断功能是否打开 是否有计算溢出等等


这些状态信息都存储在指定寄存器的指定比特位中(CPU上的相关硬件)


io_load_eflags() 就是获取这些信息 把他们存储到变量eflags中


io_store_eflags(eflags) 是把前面存储的状态信息重新设置回去


io_cli()的作用是让CPU停止接收一切中断信号 也就是设置对应的比特位让CPU运行时忽略到来的中断请求


io_store_eflags作用就是重新恢复原来状态 让CPU重新接收中断信号


内核主循环的处理


....
static struct FIFO8 timerinfo;
static char timerbuf[8];
....
void CMain(void) {
    ....
    init_pit();
    fifo8_init(&timerinfo, 8, timerbuf);
    settimer(500, &timerinfo, 1);
    ....
}


内核启动时 初始化时钟控制器 并初始化一个FIFO队列和用于该队列的缓冲区


通过settimer函数 设置一个5秒的超时时间片 数值1对应TIMERCTL结构体里面的data

void CMain(void) {
    ....
    int data = 0;
    int count = 0;
    struct TIMERCTL *timerctl = getTimerController();

    for(;;) {
       char* pStr = intToHexStr(timerctl->timeout);
       boxfill8(shtMsgBox->buf, 160, COL8_C6C6C6, 40, 28, 119, 43);
       showString(shtctl, shtMsgBox, 40, 28, COL8_000000,pStr);

       io_cli();
       if (fifo8_status(&keyinfo) + fifo8_status(&mouseinfo) +
 fifo8_status(&timerinfo)  == 0) {

           io_sti();
       } ....

      else {
      //超时发生后进入这里
           io_sti();
           showString(shtctl, sht_back, 0, 0, COL8_FFFFFF, "5[sec]");
      }
}

在进入内核主循环前 先获取时钟控制器


然后把控制器对应的时间片信息转换成字符串后 显示到Message Box 窗体里


每次循环时 看看控制器对应的数据队列里面是否有数据


如果有数据表明超时发送 于是进入最后的else 部分 在else 里面


我们在桌面的左上角打印出一个字符串”5[sec]”。


3.运行

目录
相关文章
|
20天前
|
监控 Linux 云计算
Linux操作系统在云计算环境中的实践与优化###
【10月更文挑战第16天】 本文探讨了Linux操作系统在云计算环境中的应用实践,重点分析了其在稳定性、安全性和高效性方面的优势。通过具体案例,阐述了Linux如何支持虚拟化技术、实现资源高效分配以及与其他开源技术的无缝集成。文章还提供了针对Linux系统在云计算中的优化建议,包括内核参数调整、文件系统选择和性能监控工具的应用,旨在帮助读者更好地理解和应用Linux于云计算场景。 ###
23 3
|
1月前
|
存储 C语言 iOS开发
MacOS环境-手写操作系统-48-让内核从错误中恢复
MacOS环境-手写操作系统-48-让内核从错误中恢复
35 0
|
1月前
|
存储 API C语言
MacOS环境-手写操作系统-46,47-C语言开发应用程序
MacOS环境-手写操作系统-46,47-C语言开发应用程序
33 0
|
1月前
|
编译器 API C语言
MacOS环境-手写操作系统-45-C语言开发应用程序
MacOS环境-手写操作系统-45-C语言开发应用程序
42 0
|
1月前
|
小程序 iOS开发 MacOS
MacOS环境-手写操作系统-44-运行简单的程序
MacOS环境-手写操作系统-44-运行简单的程序
19 0
|
1月前
|
存储 Java iOS开发
MacOS环境-手写操作系统-43-dir命令的实现 和 文件写入
MacOS环境-手写操作系统-43-dir命令的实现 和 文件写入
29 0
|
5天前
|
安全 Linux 数据安全/隐私保护
Vanilla OS:下一代安全 Linux 发行版
【10月更文挑战第30天】
18 0
Vanilla OS:下一代安全 Linux 发行版
|
8天前
|
人工智能 安全 Linux
|
29天前
|
Unix 物联网 大数据
操作系统的演化与比较:从Unix到Linux
本文将探讨操作系统的历史发展,重点关注Unix和Linux两个主要的操作系统分支。通过分析它们的起源、设计哲学、技术特点以及在现代计算中的影响,我们可以更好地理解操作系统在计算机科学中的核心地位及其未来发展趋势。
|
3月前
|
编解码 安全 Linux
基于arm64架构国产操作系统|Linux下的RTMP|RTSP低延时直播播放器开发探究
这段内容讲述了国产操作系统背景下,大牛直播SDK针对国产操作系统与Linux平台发布的RTMP/RTSP直播播放SDK。此SDK支持arm64架构,基于X协议输出视频,采用PulseAudio和Alsa Lib处理音频,具备实时静音、快照、缓冲时间设定等功能,并支持H.265编码格式。此外,提供了示例代码展示如何实现多实例播放器的创建与管理,包括窗口布局调整、事件监听、视频分辨率变化和实时快照回调等关键功能。这一技术实现有助于提高直播服务的稳定性和响应速度,适应国产操作系统在各行业中的应用需求。
105 3
下一篇
无影云桌面