haribote timer.c 定时器管理程序阅读注释

[ 1] haribote ipl09.nas 引导程序阅读注释
[ 2] haribote asmhead.nas 从实模式进入保护模式程序阅读注释
[ 3] haribote dsctbl.c 设置GDT和IDT程序阅读注释
[ 4] haribote memory.c 内存管理程序阅读注释
[ 5] haribote int.c 可编程中断控制器(PIC)初始配置程序阅读注释

篇幅较长,可通过浏览器的搜索功能(Ctrl + f)搜索函数名了解相应函数的实现机制,如 init_pit。

[6] haribote timer.c 定时器管理程序阅读注释
timer.c
/* timer.c, 定时器程序接口 */

#include "bootpack.h"

/* 定时器控制寄存器8254控制器端口地址和通道0计数器端口地址 */
#define PIT_CTRL 0x0043
#define PIT_CNT0 0x0040

/* 管理定时器的全局变量 */
struct TIMERCTL timerctl;

/* 标识管理定时器结构体已分配状态和正在使用状态 */
#define TIMER_FLAGS_ALLOC 1
#define TIMER_FLAGS_USING 2

/* init_pit,
 * 初始化定时器控制器8254,配置其发起中断请求的超时时间;
 * 并启动一个"永不超时的定时器"作为哨兵定时器。
 *
 * 永不超时定时器共包含0xffffffff个计数,haribote定时器
 * 处理函数每约10ms发生一次计数,则0xffffffff约为497年。*/
void init_pit(void)
{
    int i;
    struct TIMER *t;

    /* 将 写方式控制字34h 写入8254控制寄存器。
     * 34h: 
     * 通道计数器0以方式2工作;读/写计数器寄存器时先读/
     * 写低字节,再读/写高字节;计数器计数进制为16进制。*/
    io_out8(PIT_CTRL, 0x34);

    /* 写通道0计数器寄存器,低字节0x9c,高字节0x2e,0x2e9c=11932.
     *
     * 由计数器寄存器初值可得8254通道0计数器向8259A输出中断信
     * 号的周期T=11932 * 1/(8254计数频率), 即中断频率f=8254计
     * 数频率/11932(8254计数频率=1193.18KHz)。
     *
     * 即经以下配置后8254定时器中断频率f=1193.18KHz/11932约100Hz,
     * 即约10ms发生一次定时器中断。*/
    io_out8(PIT_CNT0, 0x9c);
    io_out8(PIT_CNT0, 0x2e);
    
    timerctl.count = 0;
    /* 将所有定时器置为未使用状态 */
    for (i = 0; i < MAX_TIMER; i++) {
        timerctl.timers0[i].flags = 0;
    }
    
    /* 在定时器内存中分配一个空闲定时器,将其超时值设置为最大超时
     * 值(相当于永不超时),将其状态标识为正在使用的状态, 将其指向
     * 下一个定时器的指针置为0。*/
    t = timer_alloc();
    t->timeout = 0xffffffff;
    t->flags = TIMER_FLAGS_USING;
    t->next = 0;
    
    /* 管理定时器的全局变量使用t0指针指向定时器的第一个定时器,
     * next字段值为t0指针指向定时器的超时值。*/
    timerctl.t0 = t;
    timerctl.next = 0xffffffff;
    return;
}

/* timer_alloc,
 * 分配一个空闲的定时器管理结构体用作定时器管理。
 * 成功则返回该空闲元素地址; 失败返回0. */
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;
            timerctl.timers0[i].flags2 = 0;
            return &timerctl.timers0[i];
        }
    }
    return 0;
}

/* timer_free,
 * 释放timer指向的定时器结构体,将其状态设置为未使用状态。*/
void timer_free(struct TIMER *timer)
{
    timer->flags = 0;
    return;
}

/* timer_init,
 * 设置timer所指定时器的循环队列和超时数据。
 * 
 * 定时器超时时,将数据data发往fifo所指缓冲队列中,其他程序(任务)就可从该
 * 队列里读到data数据,从而可根据该data做出后续动作。*/
void timer_init(struct TIMER *timer, struct FIFO32 *fifo, int data)
{
    timer->fifo = fifo;
    timer->data = data;
    return;
}

/* timer_settime,
 * 设置timer所指定时器的超时值为timeout(单位10ms),
 * 并将timer插入到以超时值升序排列的定时器链表中。*/
void timer_settime(struct TIMER *timer, unsigned int timeout)
{
    int e;
    struct TIMER *t, *s;

    /* 设置timer指向定时器的超时值=超时值+已计时值,
     * 再次设置timer所指定时器为正使用的状态。*/
    timer->timeout = timeout + timerctl.count;
    timer->flags = TIMER_FLAGS_USING;

    /* 备份EFLAG到e,并设置EFLAG禁止CPU处理中断,
     * 调用io_store_eflags(e)即可恢复EFLAG。*/
    e = io_load_eflags();
    io_cli();

    /* timerctl.t0指向定时器链表头定时器 */
    t = timerctl.t0;
    if (timer->timeout <= t->timeout) {
        /* 若新设置的定时器比链表头的超时值小,
         * 则将该定时器查入到链表头。*/
        timerctl.t0 = timer;
        timer->next = t;
        timerctl.next = timer->timeout;
        io_store_eflags(e);
        return;
    }
    /* 以定时器超时值升序的顺序将timer插入链表中 */
    for (;;) {
        s = t;
        t = t->next;
        if (timer->timeout <= t->timeout) {
            /* 将timer插入到链表相应位置上 */
            s->next = timer;
            timer->next = t;
            io_store_eflags(e);
            return;
        }
    }
}

/* inthandler20,
 * 定时器中断C处理函数。
 *
 *
 * 定时器中断约每10ms会向8259A IRQ0发起一次中断,见 init_pit()。
 * 定时器中断发生时会调用注册在IDT[20h]中的 _asm_inthandler20,
 * 该程序会调用此处的定时器中断C处理函数 inthandler20 以完成中
 * 断处理,即该函数约每10ms该函数就会被调用一次。*/
void inthandler20(int *esp)
{
    struct TIMER *timer;
    char ts = 0;

    /* 初始化中断控制器8259A后(见 int.c/init_pic),
     * 再写8259A端口地址表明往8259A写OCW1-OCW3组。
     * PIC0_OCW2 && 0x60表明对OCW2编程,0x60为EOI
     * 命令,告知8259A结束此次中断。*/
    io_out8(PIC0_OCW2, 0x60);

    /* 更新系统当前已累计 计时值(单位10ms) */
    timerctl.count++;

    /* 若定时器中的最小超时值还未超时则直接返回 */
    if (timerctl.next > timerctl.count) {
        return;
    }
    
    /* 若最小超时定时器已超时,则遍历定时器链表直到遍
     * 历到非超时定时器才结束遍历。*/
    timer = timerctl.t0;
    for (;;) {
        /* 若遇到非超时定时器则退出遍历 */
        if (timer->timeout > timerctl.count) {
            break;
        }
        /* 将超时定时器状态设置为已分配状态 */
        timer->flags = TIMER_FLAGS_ALLOC;

        /* 若当前定时器不为任务定时器,则表明需定时往其队列中发送数据;
         * 若当前定时器为任务定时器,则表明当前任务运行时间完毕则置任
         * 务切换标志ts=1。*/
        if (timer != task_timer) {
            /* fifo.c */
            fifo32_put(timer->fifo, timer->data);
        } else {
            ts = 1;
        }
        /* 指向下一个超时值最小的定时器 */
        timer = timer->next;
    }

    /* 更新定时器中当前具最小超时值的定时器 */
    timerctl.t0 = timer;
    timerctl.next = timer->timeout;

    /* 若当前任务定时器也超时了,则调用task_switch切换任务。*/
    if (ts != 0) {
        /* mtask.c */
        task_switch();
    }
    return;
}

/* timer_cancel,
 * 取消timer所指定时器,成功返回1,失败返回0。*/
int timer_cancel(struct TIMER *timer)
{
    int e;
    struct TIMER *t;

    /* 备份EFLAG并置位EFLAG禁止CPU处理中断,
     * 调用io_store_eflags(e)即可恢复EFLAG。*/
    e = io_load_eflags();
    io_cli();

    /* 若timer所指定时器处于正使用状态, */
    if (timer->flags == TIMER_FLAGS_USING) {
        if (timer == timerctl.t0) {
            /* 若timer所指定时器为链表头定时器,
             * 则更改链表头定时器为下一个定时器。*/
            t = timer->next;
            timerctl.t0 = t;
            timerctl.next = t->timeout;
        } else {
            /* 若timer所指定时器不为链表头定时器,则遍历到该头结点。
             * t指向timer所指定时器的前一个定时器。*/
            t = timerctl.t0;
            for (;;) {
                if (t->next == timer) {
                    break;
                }
                t = t->next;
            }
            /* 让timer所指定时器上一个定时器指向其下一个定时器 */
            t->next = timer->next;
        }
        /* 将timer所指定时器的状态设置为已分配,
         * 回复EFLAG寄存器的值,允许CPU处理中断。*/
        timer->flags = TIMER_FLAGS_ALLOC;
        io_store_eflags(e);
        return 1;
    }
    io_store_eflags(e);
    return 0;
}

/* timer_cancelall,
 * 取消所关联队列首地址为fifo的所有定时器。
 * 经该函数取消的定时器结构体的状态恢复未使用状态。*/
void timer_cancelall(struct FIFO32 *fifo)
{
    int e, i;
    struct TIMER *t;

    /* 备份EFLAG到e,设置EFLAG禁止CPU处理中断。*/
    e = io_load_eflags();
    io_cli();
    
    /* 取消所有还未被取消且队列首地址为fifo的定时器,
     * 将这些定时器的状态设置为未使用状态。*/
    for (i = 0; i < MAX_TIMER; i++) {
        t = &timerctl.timers0[i];
        if (t->flags != 0 && t->flags2 != 0 && t->fifo == fifo) {
            timer_cancel(t);
            timer_free(t);
        }
    }
    /* 恢复EFLAG */
    io_store_eflags(e);
    return;
}
bootpack.h
/* ... */

/* timer.c */
/* haribote os所支持定时器最大数 */
#define MAX_TIMER 500

/* struct TIMER,
 * 管理定时器的结构体类型。*/
struct TIMER {
/* 指向下一个定时器,下一个定时器是
 * 超时值刚好大于(或等于)自身超时值的定时器。*/
    struct TIMER *next;
    
/* 定时器超时值(以10ms为单位) */
    unsigned int timeout;

/* flags=0/1/2,本定时器未使用/已分配/正使用;
 * flags2=1,定时器自动取消。*/
    char flags, flags2;

/* 定时器超时队列,
 * 用于缓存定时器超时后的超时信号data。
 *
 * 任务定时器超时后,则可直接执行任务切换;
 * 其他类型定时器超时后,则将相应的超时信号
 * data写入fifo指向的缓存队列中,当其他任务
 * 从该队列中读出data时,便知道相应的定时器超时了。*/
    struct FIFO32 *fifo;
    int data;
};
/* struct TIMERCTL,
 * 定时器管理结构体类型。*/
struct TIMERCTL {
/* count,当前系统已计时值(单位为10ms);
 * next,即将超时定时器的超时值(单位10ms)。*/
    unsigned int count, next;

/* 指向timers0内存段中超时值最小的定时器 */
    struct TIMER *t0;

/* 定时器内存空间 */
    struct TIMER timers0[MAX_TIMER];
};
extern struct TIMERCTL timerctl;
void init_pit(void);
struct TIMER *timer_alloc(void);
void timer_free(struct TIMER *timer);
void timer_init(struct TIMER *timer, struct FIFO32 *fifo, int data);
void timer_settime(struct TIMER *timer, unsigned int timeout);
void inthandler20(int *esp);
int timer_cancel(struct TIMER *timer);
void timer_cancelall(struct FIFO32 *fifo);

/* ... */
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值