循序渐进,学习开发一个RISC-V 上的操作系统

第 15 章 软件定时器

 软件定时器的分类

按照定时器设定方式分:

  • 单次触发定时器:创建后只会触发一次定时通知事件,触发后该定时器自动停止(销毁)。
  • 周期触发定时器:创建后按照设定的周期无限循环触发定时器通知事件,直到用户手动停止。

按照定时器超时后执行处理函数的上下文环境分:

  • 超时函数运行在中断上下文环境中,要求执行函数的执行时间尽可能短,不可以执行等待其它事件等可能导致中断控制路径挂起的操作。优点是响应较迅速,实时性较高。
  • 超时函数运行在任务上下文环境中,即创建一个任务来执行这个 函数,函数中可以等待或者挂起,但实时性较差。

软件定时器的设计和实现

软件定时器的数据结构设计:

/* software timer */
struct timer {
	void (*func)(void *arg);
	void *arg;
	uint32_t timeout_tick;
};
extern struct timer *timer_create(void (*handler)(void *arg), void *arg, uint32_t timeout);
extern void timer_delete(struct timer *timer);

#define MAX_TIMER 10
static struct timer timer_list[MAX_TIMER];

注意:timer_create函数中timeout参数的单位是_tick(即软件定时器的时间设定都是tick的整数倍)

软件定时器的创建和删除

1、通过遍历timer_list数组,找到一个当前可用的软件定时器

2、对其进行初始化,把处理函数、指针参数、定时的时间进行赋值。返回当前软件定时器的指针。

3、删除软件定时器时,遍历timer_list数组,找到要删除的软件定时器,将其元素进行清除。

struct timer *timer_create(void (*handler)(void *arg), void *arg, uint32_t timeout)
{
	/* TBD: params should be checked more, but now we just simplify this */
	if (NULL == handler || 0 == timeout) {
		return NULL;
	}

	/* use lock to protect the shared timer_list between multiple tasks */
	spin_lock();

	struct timer *t = &(timer_list[0]);
	for (int i = 0; i < MAX_TIMER; i++) {
		if (NULL == t->func) {
			break;
		}
		t++;
	}
	if (NULL != t->func) {
		spin_unlock();
		return NULL;
	}

	t->func = handler;
	t->arg = arg;
	t->timeout_tick = _tick + timeout;

	spin_unlock();

	return t;
}

void timer_delete(struct timer *timer)
{
	spin_lock();

	struct timer *t = &(timer_list[0]);
	for (int i = 0; i < MAX_TIMER; i++) {
		if (t == timer) {
			t->func = NULL;
			t->arg = NULL;
			break;
		}
		t++;
	}

	spin_unlock();
}

软件定时器的实现、处理流程:

1、发生定时器中断后,跳转到timer_handler函数

2、timer_handler调用timer_check函数,查询是哪个定时器生效了。

3、timer_check函数遍历timer_list,找到当前tick >= t->timeout_tick,调用该定时器的处理函数,并将参数一并传入,处理完后清除该定时器的设定。并更新硬件定时器的时间。(可以看出软件定时器是依托于硬件定时器发生的)

reg_t trap_handler(reg_t epc, reg_t cause)
{
	reg_t return_pc = epc;
	reg_t cause_code = cause & 0xfff;
	
	if (cause & 0x80000000) {
		/* Asynchronous trap - interrupt */
		switch (cause_code) {

		case 7:
			uart_puts("timer interruption!\n");
			timer_handler();
			break;
    }
}

void timer_handler() 
{
	_tick++;
	printf("tick: %d\n", _tick);

	timer_check();

	timer_load(TIMER_INTERVAL);

	schedule();
}

static inline void timer_check()
{
	struct timer *t = &(timer_list[0]);
	for (int i = 0; i < MAX_TIMER; i++) {
		if (NULL != t->func) {
			if (_tick >= t->timeout_tick) {
				t->func(t->arg);

				/* once time, just delete it after timeout */
				t->func = NULL;
				t->arg = NULL;

				break;
			}
		}
		t++;
	}
}

软件定时器的优化

上述设计采用数组的方式,对于内存的使用和遍历的时间复杂度都有很大优化空间

对于内存的使用,我们可以采用链表的方式来动态分配,这样更加灵活。

对于遍历的时间复杂度,第一我们可以让定时器按照超时时间进行排序(因为系统时间tick总是从小到大);第二我们可以采用跳表的管理方式,牺牲一部分内存来换取时间。

如图所示,要寻找超时时间为20的定时器,采用二级跳表只需要类似于二分查找法的时间复杂度,大大降低了查找的时间复杂度。

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值