linux时间管理

jiffies与jiffies_64

64位系统:两个是同一个变量(unsigned long long)

32位系统:jiffies为unsigned long是jiffies_64的低32位

设置:

可以在.config中设置CONFIG_HZ表示时钟中断的频率

1.时间比较

timer_after(a,b)、timer_before(a,b)…(其中a,b为jiffies)

为啥要用这些而不能直接比较?

避免了jiffies溢出导致判断失误(虽然不太容易溢出)

2.时间转换

1.ms与jiffies之间

如:jiffies_to_msecs

2.struct timespec/struct timeval与jiffies转换
struct timespec {
        time_t  tv_sec;         /* seconds */
        long    tv_nsec;        /* nanoseconds */
};
struct timeval {
	__kernel_time_t		tv_sec;		/* seconds */
	__kernel_suseconds_t	tv_usec;	/* microseconds */
};

如:timespec_to_jiffies、timeval_to_jiffies

3.可以通过clock_gettime获取此时jiffies对应的timespec
#include <time.h>

int clock_gettime(clockid_t  clk_id,  struct  timespec
*res);

3.延时操作

1.长延时,进程调度方式(通过jiffies,默认HZ = 1000,也就是最多ms级别)

提供了msleep()、msleep_interrupt()

原理:(msleep为例)

msleep(1);
------------------------------------
set_current_state(TASK_UNINTERRUPT);//设置当前进程状态(为不可打断状态,只要进程不是TASK_RUNING状态就会从运行队列中删除。
//如果没有set_current_state那么当前进程仍处于runing状态,还是会得到cpu运行
schedule_timeout(jiffies+HZ);//运用了后面会讲到的定时器
2.短延时,忙等待方式

提供了mdelay(),udelay(),ndelay()

为啥要用忙等待:

​ 1.短延时时间短,CPU忙等待可以容忍

2.系统中进程切换花费时间为几个到上百us,那么如果用调度方式可能延时时间到还在进程切换

4.内核定时器

1.定时器的管理:(基于per_cpu)

​ 每个cpu中都会有一个tvec_base结构体数组管理注册的定时器

struct tvec_base {
	spinlock_t lock;
	struct timer_list *running_timer;
	unsigned long timer_jiffies;
	unsigned long next_timer;
	unsigned long active_timers;
	unsigned long all_timers;
	int cpu;
	struct tvec_root tv1;//tv1是管理定时时间为最近0-255个时钟周期的定时器
	struct tvec tv2;
	struct tvec tv3;
	struct tvec tv4;
	struct tvec tv5;
} ____cacheline_aligned;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QEzVbkEH-1679396761983)(C:\Users\cww\AppData\Roaming\Typora\typora-user-images\image-20221213210834738.png)]

如果有相同时间的定时器就用双链表挂起

2.如何实现

1.内核初始化

void __init init_timers(void)//被start_kernel调用
{
	/* ensure there are enough low bits for flags in timer->base pointer */
	BUILD_BUG_ON(__alignof__(struct tvec_base) & TIMER_FLAG_MASK);

	init_timer_cpus();
	init_timer_stats();
	timer_register_cpu_notifier();
	open_softirq(TIMER_SOFTIRQ, run_timer_softirq);//申请了软中断
}

在open_softirq(TIMER_SOFTIRQ, run_timer_softirq);这句有没有发现就像softirq_init中为tasklet做的注册action,只是定时器要自己注册。

2.驱动注册定时器

4.14之前:
	1.init_timer(&timer)
	timer.function = GetIntrTimerCallback;
	2.void GetIntrTimerCallback(unsigned long devAddr)//中断调用
4.14之后:
	1.timer_setup(&timer, GetIntrTimerCallback, 0);
//将定时器对象加入到当前cpu的tvec_base结构体数组中

3.调用栈

也就是每次内核定时器硬件中断都会在下半部执行 run_timer_softirq,然后在其中调用__run_timers。

这里面的操作就是遍历当前cpu中的tvec_base,如果找到了时间到了的定时器那么先将其从tvec_base中删除,然后再执行其注册函数。(所以定时器是一次性的,如果想重复使用必须在中断函数中再次注册)

static void run_timer_softirq(struct softirq_action *h)
{
	struct tvec_base *base = __this_cpu_read(tvec_bases);

	hrtimer_run_pending();

	if (time_after_eq(jiffies, base->timer_jiffies))
		__run_timers(base);
}

__run_timers://这里就会传入当前cpu的tvec_base然后遍历,注册的那个定时器时间到了就执行。
static inline void __run_timers(struct tvec_base *base)
{
	struct timer_list *timer;

	spin_lock_irq(&base->lock);
	if (catchup_timer_jiffies(base)) {
		spin_unlock_irq(&base->lock);
		return;
	}
	while (time_after_eq(jiffies, base->timer_jiffies)) {
		struct list_head work_list;
		struct list_head *head = &work_list;
		int index = base->timer_jiffies & TVR_MASK;

		/*
		 * Cascade timers:
		 */
		if (!index &&
			(!cascade(base, &base->tv2, INDEX(0))) &&
				(!cascade(base, &base->tv3, INDEX(1))) &&
					!cascade(base, &base->tv4, INDEX(2)))
			cascade(base, &base->tv5, INDEX(3));
		++base->timer_jiffies;
		list_replace_init(base->tv1.vec + index, head);
		while (!list_empty(head)) {
			void (*fn)(unsigned long);
			unsigned long data;
			bool irqsafe;

			timer = list_first_entry(head, struct timer_list,entry);
			fn = timer->function;
			data = timer->data;
			irqsafe = tbase_get_irqsafe(timer->base);

			timer_stats_account_timer(timer);

			base->running_timer = timer;
			detach_expired_timer(timer, base);

			if (irqsafe) {
				spin_unlock(&base->lock);
				call_timer_fn(timer, fn, data);
				spin_lock(&base->lock);
			} else {
				spin_unlock_irq(&base->lock);
				call_timer_fn(timer, fn, data);
				spin_lock_irq(&base->lock);
			}
		}
	}
	base->running_timer = NULL;
	spin_unlock_irq(&base->lock);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值