内核定时器


内核定时器

内核定时器依赖于内核软中断,如下代码

void __init init_timers(void)
{
	open_softirq(TIMER_SOFTIRQ, run_timer_softirq)
}
//当TIMER_SOFTIRQ触发时会执行run_timer_softirq函数
void run_timer_softirq(struct softirq_action *h)
{
	struct tvec_base *base = this_cpu_ptr(&tvec_bases);
	struct tvec_base是个Per-CPU变量,只在本地CPU有效,
	void __run_timers(struct tvec_base *base)
	{
		spin_lock_irq(&base->lock)
		//找到超时的timer
		base->running_timer = timer
		detach_expired_timer(timer, base) 
		//将timer从base链表中删除
		spin_unlock_irq(&base->lock)
		call_timer_fn(timer, fn, timer->data)
		spin_lock_irq(&base->lock)
		base->running_timer = NULL
		spin_unlock_irq(&base->lock)
	}
	return __run_timers(base);
}

程序在执行timer的会掉函数时会设置当前base的running_timer为当前执行函数的timer,但是此timer已经在链表中删除

定义定时器

  1. 方法1
DEFINE_TIMER(timer, cb, expires, data)
  1. 方法2
struct timer_list timer;
setup_timer(&timer, (*cb)(data), data)
timer.expires = jiffies + 5*HZ
  1. 方法3
struct timer_list timer
init_timer(&timer)
timer.timer.expires = jiffies + 5*HZ
timer.timer.data = data
timer.timer.function = cb

添加定时器

void add_timer(struct timer_list *timer)

调用如上函数实现将一个定时器添加到内核定时器链表中,也可以通过mod_timer重新注册定时器,

删除定时器

int del_timer(struct timer_list *timer)
int del_timer_sync(struct timer_list *timer)

通过del_timer函数注销一个定时器,如果定时器存在返回1,否则返回0,del_timer_sync是用在SMP系统中的,在单核系统中和del_timer是一样的,del_timer_sync会判断当前定时器是否在其他CPU上运行,如果运行等待其运行完成后删除此定时器。

判断定时器是否挂起

int timer_pending(struct timer_list *timer)

用于判断一个定时器是否存在在链表中,但是此定时器有可能正在运行

函数分析

void add_timer(struct timer_list *timer)
{
	__mod_timer(timer, timer->expires)
}

int __mod_timer(struct timer_list *timer)
{
	base = lock_timer_base(timer)
	//获取tvec_base变量,我们知道base为PerCPU变量,这个函数也顺便给base加锁。
	if (timer_pending(timer)) {
		deteach_timer(timer, 0)
		ret = 1
	}
	//如果timer已经存在,则删除,此处的操作是在加锁状态先的,因此和softirq里面不会重入
	new_base = __get_cpu_var(tvec_base)
	if (base != new_base) {
		//我们不能修改一个正在运行的timer的base,否则del_timer_sync会出问题,后面我们再说这个。
		if (base->running_timer != timer) {
			timer->base =NULL;
			//由于本地中断已经在76行处关闭,因此不开本地中断
			spin_unlock(&base->lock)
			base = new_base;
			//由于前面已经禁用了本地中断,因此这只需加锁
			spin_lock(&base->lock)
			timer->base = base
		}
	}
	//如上代码如果当前timer base不是timer->base则更新timer->base
	internal_add_timer(base, timer)
	//将timer加入本地base中
	spin_unlock_irqrestore(&base->lock, flag)
	//1,释放锁
	//2,恢复中断
	//3,启用内核抢占
	//由以上代码我们可以知道,同一个timer只能被提交一次,
	//但是同一个timer是有可能在不同的cpu上同时运行的,我们可以试想这个一个场景,
	//CPU0注册一个timer,并已经在CPU0上运行了,
	//CPU1也注册了这个timer,因为运行的timer已经在链表上删除了且没有获取base->lock,
	//因此lock_timer_base和timer_pending都可以向下执行并注册成功
	//假设此timer的时间非常短,CPU1的timer在注册完成后由于系统的一次中断立刻执行,
	//此时同一个timer在CPU0和CPU1并发
	//以上是个人的一个理解,如果错误,还请指出
}
//由以上可以知道,在哪个CPU运行增加timer的代码,则timer的回掉函哪个CPU上运行。
del_timer(struct timer_list *timer)
{
	if (timer_pending(timer)) {
		base = lock_timer_base(timer, &flag);
		ret = detach_if_pending(timer, base, true);
		spin_unlock_irqrestore(&base->lock, flag);
	}
	return ret
	//由此函数我们可以知道,此函数只判断timer是否在链表中,然后就直接删除,但是删除时此函数的回掉有可能正在运行,
	//在判断timer_pending(timer)时,理论上来说应该加锁保护,但是此处没有加锁保护,是因为在detach_if_pending中会再次判断是否pending如果pending则函数直接退出,
}
del_timer_sync(struct timer_list *timer)
{
	for(;;) {
		int try_to_del_timer_sync(timer)
		{
			base = lock_timer_base(timer, &flag);
			//判断当前timer是否在运行,如果在运行则等待
			if (base->running_timer != timer) {
				//detach_if_pending如果timer pending返回0
				//如果timer删除成功返回1
				ret = detach_if_pending(timer, base, true);
			}
			spin_unlock_irqrestore(&base->lock, flags)
			return ret;
		
		}
		//如果timer删除成功返回0或者1,负责等待删除
		int ret = try_to_del_timer_sync(timer);
		if (ret >= 0)
			return ret;
		cpu_relax();
	}
}