30天自制操作系统(第12天)

12天 定时器(1

12.1 使用定时器
根据定时器分配的中断号为IRQ0(《深入理解linux内核》P158页),将中断周期设定为11932,换算成16进制为0x2e9c。
IRQ0的中断周期变更: 1、AL=0x34:OUT(0x43,AL); 2、AL= 中断周期的低 8 位; OUT(0x40,AL);3、 AL= 中断周期的高 8 ; OUT(0x40,AL);
/* time.c */
#define PIT_CTRL	0x0043
#define PIT_CNT0	0x0040

void init_pit(void){
	io_out8(PIT_CTRL, 0x34);
	io_out8(PIT_CNT0, 0x9c);// 0x9c=中断周期的低8位
	io_out8(PIT_CNT0, 0x2e);// 0x2e=中断周期的高8位
}

/*参考鼠标和键盘的中断处理程序*/
void inthandler20(int *esp){
	io_out8(PIC0_OCW2, 0x60); /* 把IRQ-00信号接收完了的信息通知给PIC */
	/* 暂时什么也不做 */
	return;
}

/* dsctbl.c */
void init_gdtidt(void)
{
    (中略)
    /* IDT的设定 */
    set_gatedesc(idt + 0x20, (int) asm_inthandler20, 2 * 8, AR_INTGATE32); /* 这里! */
    set_gatedesc(idt + 0x21, (int) asm_inthandler21, 2 * 8, AR_INTGATE32);
    set_gatedesc(idt + 0x27, (int) asm_inthandler27, 2 * 8, AR_INTGATE32);
    set_gatedesc(idt + 0x2c, (int) asm_inthandler2c, 2 * 8, AR_INTGATE32);
    return;
}

/* naskfunc.nas */
/* 计时器中断 */
_asm_inthandler20:
		PUSH 	ES
		PUSH 	DS
		PUSHAD
		MOV 	EAX,ESP
		PUSH 	EAX
		MOV 	AX,SS
		MOV 	DS,AX
		MOV 	ES,AX
		CALL 	_inthandler20
		POP 	EAX
		POPAD
		POP 	DS
		POP 	ES
		IRETD
在bootpack.c头文件、汇编naskfunc.nas、Makefile等文件中添加相关函数。像Makefile文件是转换.c文件,在该节中增加了time.c文件,所以需要完善。其他修改内容就不一一介绍。

12.2 计量时间

创建一个TIMERCTL计时器结构体,并对其进行初始化。
struct TIMERCTL timerctl;

void init_pit(void){
	io_out8(PIT_CTRL, 0x34);
	io_out8(PIT_CNT0, 0x9c);
	io_out8(PIT_CNT0, 0x2e);
	timerctl.count=10;
	return;
}

void inthandler20(int *esp){
	io_out8(PIC0_OCW2, 0x60); /* 把IRQ-00信号接收完了的信息通知给PIC */
	timerctl.count++;
	return;
}
12.3 超时功能
在原TIMERCTL结构体的基础上,增加了部分元素。
struct TIMERCTL{
	unsigned int count;// 计数
	unsigned int timeout;//记录离超时还有多长时间
	struct FIFO8 *fifo;//参考鼠标和键盘,使用缓冲区打印数据
	unsigned char data;//数据
};

//设置计时器
void settimer(unsigned int timeout, struct FIFO8 *fifo, unsigned char data){
	int eflags=0;
	eflags=io_load_eflags();
    io_cli();
	timerctl.fifo=fifo;
	timerctl.data=data;
	timerctl.timeout=timeout;
	io_store_eflags(eflags);
	return;
}

12.4 使用多个定时器

为实现多个计时器的设定,遂将TIMERCTL结构体进行拆分成TIMERCTL和TIMER结构体。并在TIMERCTL结构体中设定了500个计时器。
#define MAX_TIMER 500
struct TIMER{
	unsigned int timeout, flags;// flags记录各计时器状态
	struct FIFO8 *fifo;
	unsigned char data;
};

struct TIMERCTL{
	unsigned int count;
	struct TIMER timer[MAX_TIMER];
};
下面就是对TIMERCTL结构体初始化,并实现分配、释放、初始化和设置一个计时器的功能,并完善12.2节中的inthandler20函数。
// 初始化所有计时器
void init_pit(void){
	int i;
	io_out8(PIT_CTRL, 0x34);
	io_out8(PIT_CNT0, 0x9c);
	io_out8(PIT_CNT0, 0x2e);
	timerctl.count=0;
	for(i=0;iflags=0;/* 未使用 */
	return;
}

// 初始化一个计时器
void timer_init(struct TIMER *timer, struct FIFO8 *fifo, unsigned char data){
	timer->fifo=fifo;
	timer->data=data;
	return;
}

// 设置一个计时器
void timer_settime(struct TIMER *timer, unsigned int timeout){
	timer->timeout=timeout;
	timer->flags=TIMER_FLAGS_USING;
	return;
}

void inthandler20(int *esp){
	io_out8(PIC0_OCW2, 0x60); /* 把IRQ-00信号接收完了的信息通知给PIC */
	timerctl.count++;
	int i;
	for(i=0;i

12.5 加快中断处理(1

在12.4节中void inthandler20(int *esp)函数,需要递减timerout和判断,非常的费时。所以在该节中将timerout的原定义为所剩时间改成既定时间,意思就是超过timeout这个时间就需要向缓冲区中写入数据。
// 设置一个计时器
void timer_settime(struct TIMER *timer, unsigned int timeout){
	timer->timeout=timeout+timerctl.count;/*这里*/
	timer->flags=TIMER_FLAGS_USING;
	return;
}

//计时器中断,当timeout==0时则向timerctl.fifo中写入timerctl.data
void inthandler20(int *esp){
	io_out8(PIC0_OCW2, 0x60); /* 把IRQ-00信号接收完了的信息通知给PIC */
	timerctl.count++;
	int i;
	for(i=0;i

12.6 加快中断处理(2

考虑到每次中断都要执行500次 =MAX_TIMER的次数)if语句,如果在TIMERCTL结构体中定义next记录下一个中断的时间是不是就减少了判断次数?就像C语言中的min函数,取所有计数器中timeout的最小值作为next值。
// 设置一个计时器
void timer_settime(struct TIMER *timer, unsigned int timeout){
	timer->timeout=timeout+timerctl.count;
	timer->flags=TIMER_FLAGS_USING;
	if(timer->timeouttimeout;/*这里*/
	return;
}

//计时器中断,当timeout==0时则向timerctl.fifo中写入timerctl.data
void inthandler20(int *esp){
	io_out8(PIC0_OCW2, 0x60); /* 把IRQ-00信号接收完了的信息通知给PIC */
	timerctl.count++;
	if(timerctl.next>timerctl.count)return;/*这里*/
	int i;
	timerctl.next=0xffffffff;/*这里*/
	for(i=0;itimerctl.timer[i].timeout)
					timerctl.next=timerctl.timer[i].timeout;
			}
		}
	}
	return;
}

12.7 加快中断处理(3

由于可以创建500个计时器,这500个计时器该如何进行管理呢?是否可以参考图层的管理方式,在结构体中加入各计时器的内存位置?这样会更方便排序和读取数据。
struct TIMERCTL{
	//count计数 next记录下一个时刻 using记录活动计时器的个数 using是计时器的使用数量
	unsigned int count, next, using;
	struct TIMER *timers[MAX_TIMER];// 记录各计时器的内存位置
	struct TIMER timers0[MAX_TIMER];
};
上述结构体的元素出现变更,所以使用该结构的函数也需要进行完善(下面列举了改动较大的函数)。
// 初始化所有计时器
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;itimeout=timeout+timerctl.count;
	timer->flags=TIMER_FLAGS_USING;
    /*从这里开始*/
	int i, j;	
	int e=io_load_eflags();
	io_cli();
	for(i=0;itimeout>=timer->timeout)
			break;
	}
	for(j=timerctl.using;j>i;j--)/* i号之后全部后移一位 */
		timerctl.timers[j]=timerctl.timers[j-1];
	timerctl.using++;
	timerctl.timers[i]=timer;/* 插入到空位上 */
	timerctl.next=timerctl.timers[0]->timeout;
	io_store_eflags(e);
    /*到这里结束*/
	return;
}

//计时器中断,当timeout==0时则向timerctl.fifo中写入timerctl.data
void inthandler20(int *esp){
	int i, j;
	io_out8(PIC0_OCW2, 0x60); /* 把IRQ-00信号接收完了的信息通知给PIC */
	timerctl.count++;
	if(timerctl.next>timerctl.count)return;
    /*从这里开始*/
	for(i=0;itimeout>timerctl.count)
			break;
	}
	timerctl.using-=i;
	for(j=0;j0){//如果还有使用的计时器,则需要更新next数据,否则将next置为最大值
		timerctl.next=timerctl.timers[0]->timeout;
	}else{
		timerctl.next=0xffffffff;
	}
    /*到这里结束*/
	return;
}

你可能感兴趣的:(其他)