第13天:定时器(2)

13.1、简化字符串显示

boxfill8(buf_back, binfo->scrnx, COL8_008484, 32, 16, 32 + 15 * 8 - 1, 31);
putfonts8_asc(buf_back, binfo->scrnx, 32, 16, COL8_FFFFFF, s);
sheet_refresh(sht_back, 32, 16, 32 + 15 * 8, 32);
// sht:图层地址;x,y:绘制的开始坐标;c:绘制的颜色;b:背景颜色;s:字符串;l:字符串长度
void putfonts8_asc_sht(struct SHEET *sht, int x, int y, int c, int b, char *s, int l)
{
	boxfill8(sht->buf, sht->bxsize, b, x, y, x + l * 8 - 1, y + 15);
	putfonts8_asc(sht->buf, sht->bxsize, x, y, c, s);
	sheet_refresh(sht, x, y, x + l * 8, y + 16);
	return;
}

13.2、重新调整FIFO缓冲区

13.2.1、合并定时器的缓冲区

现在是一个定时器一个缓冲区,每次判断缓冲区为空时,if条件都要写的特别长,每次都要去不同的地址空间去查询写入的数据,这就拖慢了速度。
如果能够把所有定时器的数据都写入一个缓冲区就好了。问题就是一个缓冲区怎么区分数据的,写入的数据不同不就行了。

// 三个定时器数据都写入同一个缓冲区,不同定时器写入数据不同
fifo8_init(&timerfifo, 8, timerbuf);
timer = timer_alloc();
timer_init(timer, &timerfifo, 10);
timer_settime(timer, 1000);
timer2 = timer_alloc();
timer_init(timer2, &timerfifo, 3);
timer_settime(timer2, 300);
timer3 = timer_alloc();
timer_init(timer3, &timerfifo, 1);
timer_settime(timer3, 50);

13.2.2、合并所有的缓冲区

尝到甜头了,尝试把所有缓冲区都给统一到一个缓冲区里,利用写入数据来区分不同的作用:

data用途
0~1光标闪烁用定时器
33秒定时器
1010秒定时器
256~512键盘输入(从键盘控制器读入的值再加上256)
512~767鼠标输入(从键盘控制器读入的值再加上512)

之前使用的缓冲区都是无符号的char类型,不能支持256以上的定时器,所以把缓冲区也要修改成int类型,后面读入缓冲区数据都修改成int类型:

struct FIFO32 {
	int *buf; // 修改成int
	int p, q, size, free, flags;
};

13.3、加快中断处理

在前一天,排序号的定时器,前面的超时之后,要把后面的定时器都往前移动一下,这个移动时很耗时的,来修改一下,提高效率。
在每个定时器结构体中加一个struct timer 类型的next指针。

#define MAX_TIMER		500
struct TIMER {
	struct TIMER *next; // 添加next指针
	unsigned int timeout, flags;
	struct FIFO32 *fifo;
	int data;
};
struct TIMERCTL {
	unsigned int count, next, using;
	struct TIMER *timers[MAX_TIMER];
	struct TIMER timers0[MAX_TIMER];
};

通过next把所有的定时器串起来,就是线性链表。

中断处理函数:

void inthandler20(int *esp)
{
	int i;
	struct TIMER *timer;
	io_out8(PIC0_OCW2, 0x60);
	timerctl.count++;
	if (timerctl.next > timerctl.count) {
		return;
	}
	timer = timerctl.timers[0]; /* 最前面的定时器 */
	for (i = 0; i < timerctl.using; i++) {
		if (timer->timeout > timerctl.count) {
			break;
		}
		/* 超时 */
		timer->flags = TIMER_FLAGS_ALLOC;
		fifo32_put(timer->fifo, timer->data);
		timer = timer->next; /* 指向下一个定时器 */
	}
	timerctl.using -= i;

	/* 新移位 */
	timerctl.timers[0] = timer;

	/* timerctl.next最近超时的定时器 */
	if (timerctl.using > 0) {
		timerctl.next = timerctl.t0->timeout;
	} else {
		timerctl.next = 0xffffffff;
	}
	return;
}

主要逻辑是:当timer指向定时器超时后,就响应,然后调整timer指向下一个定时器,调整后的如果没有超时,就把该定时器放在排序后数组的首位。

设定定时器顺序的代码修改:

void timer_settime(struct TIMER *timer, unsigned int timeout)
{
	int e;
	struct TIMER *t, *s;
	timer->timeout = timeout + timerctl.count;
	timer->flags = TIMER_FLAGS_USING;
	e = io_load_eflags();
	io_cli();
	timerctl.using++;
	if (timerctl.using == 1) {
		/* 只有一个定时器 */
		timerctl.timers[0] = timer;
		timer->next = 0; /* 指向空 */
		timerctl.next = timer->timeout; // 最近超时的定时器
		io_store_eflags(e);
		return;
	}
	t = timerctl.timers[0]; // 最前面的定时器
	if (timer->timeout <= t->timeout) {
		/* 插入最前面 */
		timerctl.timers[0] = timer;
		timer->next = t; /* 下一个是t */
		timerctl.next = timer->timeout;
		io_store_eflags(e);
		return;
	}
	/* 寻找插入位置 */
	for (;;) {
		s = t; // 第一个定时器
		t = t->next; // 第二个定时器
		if (t == 0) {
			break; /* 到最后面了(s指的就是最后一个定时器) */
		}
		if (timer->timeout <= t->timeout) {
			/* 插入到s和t位置中间 */
			s->next = timer; /* s链接到timer */
			timer->next = t; /* timer指向t */
			io_store_eflags(e);
			return;
		}
	}
	/* 插入到最后 */
	s->next = timer;
	timer->next = 0;
	io_store_eflags(e);
	return;
}
// 以上整个排序过程就是线性链表的插入操作

修改之后发现啊,原来用于排序的timers已经没啥用了,就用来指出第一个,那我们还定义这么多变量干啥,直接把timers数组换成t0

struct TIMERCTL {
	unsigned int count, next, using;
	struct TIMER *t0;
	struct TIMER timers0[MAX_TIMER];
};

13.4、使用“哨兵”简化程序

刚设置定时器时,排序有以下几种情况:
1、是第一个
2、插入最前面
3、插入到中间
4、插入到末尾

这里给简化以下:
方法:设置一个哨兵 = 0xffffffff

那么1、3情况将不存在

修改过程:

// 初始化
void init_pit(void)
{
	int i;
	struct TIMER *t;
	io_out8(PIT_CTRL, 0x34);
	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; /* 未使用 */
	}
	t = timer_alloc(); /* 先获取一个当哨兵 */
	t->timeout = 0xffffffff; // 哨兵设置的非常大
	t->flags = TIMER_FLAGS_USING;
	t->next = 0; /* 下一个指向空 */
	timerctl.t0 = t; /* 现在只有哨兵,所以排最前面 */
	timerctl.next = 0xffffffff; /* 只有哨兵在等待超时 */
	return;
}
// 设置定时器排序
void timer_settime(struct TIMER *timer, unsigned int timeout)
{
	int e;
	struct TIMER *t, *s;
	timer->timeout = timeout + timerctl.count;
	timer->flags = TIMER_FLAGS_USING;
	e = io_load_eflags();
	io_cli();
	t = timerctl.t0;
	if (timer->timeout <= t->timeout) {
		/* 插入最前面 */
		timerctl.t0 = timer;
		timer->next = t; /* 后面跟着t */
		timerctl.next = timer->timeout;
		io_store_eflags(e);
		return;
	}
	/* 插入s、t中间 */
	for (;;) {
		s = t;
		t = t->next;
		if (timer->timeout <= t->timeout) {
			/* 插入s、t之间 */
			s->next = timer; /* s后面跟着要插入的timer */
			timer->next = t; /* timer后面跟着t */
			io_store_eflags(e);
			return;
		}
	}
}
// 发现少了两种情况

中断处理函数:


void inthandler20(int *esp)
{
	struct TIMER *timer;
	io_out8(PIC0_OCW2, 0x60);	
	timerctl.count++;
	if (timerctl.next > timerctl.count) {
		return;
	}
	timer = timerctl.t0; /* 第一个赋值给timer */
	for (;;) { // 这里不需要using进行循环判断了,因为定时器不会超过哨兵的大小,通过下面if判断就可以了
		if (timer->timeout > timerctl.count) {
			break;
		}
		/* 超时 */
		timer->flags = TIMER_FLAGS_ALLOC;
		fifo32_put(timer->fifo, timer->data);
		timer = timer->next; /* 超时响应后,timer指向下一个定时器,这就省略了之前using -= i */
	}
	timerctl.t0 = timer; //t0 指向第一个没有超时的定时器
	timerctl.next = timer->timeout; // 设置最近的超时时间
	return;
}

这一节没有太大变化,主要是简化了代码

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值