- 论坛徽章:
- 0
|
周期产生的事件都是由系统定时器驱动的(系统定时器是一种可编程硬件芯片,以固定频率产生中断,即定时器中断。中断处理程序负责更新系统时间以及执行需要周期性运行的任务);
推迟执行程序是使用动态定时器。
系统定时器频率——节拍率(tick rate)、HZ(中有定义);大多数体系结构的节拍率可调
jiffies
节拍计数值会发生回绕到0的情况 => 在比较节拍计数时要注意回绕
#define time_after(unknown, known) ((long)(known) - (long)(unknown) = 0)
#define time_before_eq(unknown, known) ((long)(known) - (long)(unknown) >= 0)
用户空间和HZ
内核定义用户空间看到的HZ值 USER_HZ
将一个由HZ表示的节拍计数转换成USER_HZ表示的节拍计数:#define jiffies_to_clock_t(x) ((x)/(HZ/USER_HZ))
或者jiffies_64_to_clock_t()
硬时钟和定时器
两种计时时钟:
实时时钟RTC——用于持久存放系统时间。在PC中,RTC与CMOS集成在一起,靠主板上的微型电池提供电力保持系统计时。系统启动时,读取RTC存放在xtime中,从而初始化墙上时间。
系统定时器——提供一种周期性触发中断机制。0x86体系结构采用可编程中断时钟PIT
时钟中断处理程序
(1) 获得xtime_lock锁,保护jiffies_64和墙上时间xtime;
(2) 需要时应答或重设系统时钟;
(3) 周期性的使用墙上时间更新实时时钟;
(4) 调用系统结构无关的时钟例程:do_timer()。
void do_timer(struct pt_regs *regs)
{
jiffies_64++;
update_process_times(user_mode(regs));//user_mode()查询处理器寄存器regs状态,时钟中断发生在用户空间返回1,内核模式返回0
update_times();
}
void update_process_times(int user_tick)//根据中断产生位置,更新用户或系统时间(user_tick或者system)
{
struct task_struct *p = current;
int cpu = smp_processor_id();
int system = user_tick ^ 1;
update_one_process(p, user_tick, system, cpu);
run_local_timers(); //标记一个软中断去处理所有到期的定时器
scheduler_tick(user_tick, system);//减少当前进程的时间片计数值并在需要时设置need_resched标志
}
void update_times(void)//更新墙上时间
{
unsigned long ticks;
ticks = jiffies - wall_jiffies;
if (ticks) {
wall_jiffies += ticks;
update_wall_time(ticks);
}
last_time_offset = 0;
calc_load(ticks);
}
(5) 继续执行后面的工作,释放xtime_lock锁,然后退出。
以上工作每1/HZ秒发生一次
实际时间(墙上时间)
kernel/timer.h>
struct timespec xtime;
struct timespec {
time_t tv_sec; /* seconds 存放着字1970年7月1日(UTC)以来经过的时间*/
long tv_nsec; /* nanoseconds 记录自上一秒开始经过的纳秒数*/
};
读写xtime变量需要使用xtime_lock锁:
更新xtime:
write_seqlock(&xtime_lock);
/* update xtime ... */
write_sequnlock(&xtime_lock);
读取xtime:
do {
unsigned long lost;
seq = read_seqbegin(&xtime_lock);//循环期间有时钟中断处理程序更新xtime,返回无效序列号,继续等待
usec = timer->get_offset();
lost = jiffies - wall_jiffies;
if (lost)
usec += lost * (1000000 / HZ);
sec = xtime.tv_sec;
usec += (xtime.tv_nsec / 1000);
} while (read_seqretry(&xtime_lock, seq));
从用户空间获取墙上时间gettimeofday();
内核中获取墙上时间的系统调用sys_gettimeofday()。
定时器(也称为动态定时器或内核定时器)
使用定时器:
头文件:
定义:struct timer_list my_timer;
struct timer_list {
struct list_head entry; /* entry in linked list of timers */
unsigned long expires; /* expiration value, in jiffies 超时时间*/
spinlock_t lock; /* lock protecting this timer */
void (*function)(unsigned long); /* the timer handler function 处理函数*/
unsigned long data; /* lone argument to the handler */
struct tvec_t_base_s *base; /* internal timer field, do not touch */
};
初始化:init_timer(&my_timer);
处理函数:void my_timer_function(unsigned long data);
激活定时器:add_timer(&my_timer);
注意:内核会在当前节拍计数大于或等于指定超时时开始执行处理函数,即可能推迟到下一次时钟节拍才运行 => 不能用定时器实现任何硬实时任务。
更改定时器超时时间:mod_timer(&my_timer, jiffies + new_delay); /* new expiration */
若定时器还未被激活,mod_timer函数可以激活之;调用时定时器未被激活,返回0,否则返回1。
停止定时器:del_timer(&my_timer);
若停止时定时器未被激活,返回0,否则返回1。已经超时的定时器不用调用此函数,它们会自动删除。
删除定时器需要等待可能在其他处理器上运行的定时器处理程序都退出:del_timer_sync(&my_timer);(不能用于中断上下文)
定时器竞争条件:
定时器与当前执行代码是异步的,有可能发生竞争。
实现定时器:
void run_local_timers(void)
{
raise_softirq(TIMER_SOFTIRQ);//触发定时器软中断
}
延迟执行
(1) 定时器
(2) 下半部机制
(3) 忙等待——最简单直接
unsigned long delay = jiffies + 10; /* ten ticks */
while (time_before(jiffies, delay))
;
在代码等待时,允许内核重新调度其他任务:
unsigned long delay = jiffies + 5*HZ;
while (time_before(jiffies, delay))
cond_resched();
cond_resched()只能用于进程上下文中。
(4) 短延时
void udelay(unsigned long usecs) 微秒级
void mdelay(unsigned long msecs) 毫秒级
BogoMIPS记录处理器在给定时间内忙循环执行的次数,简单来说就是记录处理器在空闲时速度有多快。值存放在loops_per_jiffy中,从/proc/cpuinfo中读取。
(5) schedule_timeout()
使需要延迟执行的任务睡眠,直到延时时间耗尽,唤醒任务重新放入运行队列。但不能保证睡眠时间正好等于延迟时间。
/* set task's state to interruptible sleep */
set_current_state(TASK_INTERRUPTIBLE);
/* take a nap and wake up in "s" seconds */
schedule_timeout(s * HZ);
schedule_timeout使调度程序必须处于进程上下文中。
使用的是内核定时器,实现代码如下:
signed long schedule_timeout(signed long timeout)
{
timer_t timer;
unsigned long expire;
switch (timeout)
{
case MAX_SCHEDULE_TIMEOUT:
schedule();
goto out;
default:
if (timeout 0)
{
printk(KERN_ERR "schedule_timeout: wrong timeout "
"value %lx from %p\n", timeout,
__builtin_return_address(0));
current->state = TASK_RUNNING;
goto out;
}
}
expire = timeout + jiffies;
init_timer(&timer);
timer.expires = expire;
timer.data = (unsigned long) current;
timer.function = process_timeout;
add_timer(&timer);
schedule();
del_timer_sync(&timer);
timeout = expire - jiffies;
out:
return timeout 0 ? 0 : timeout;
}
void process_timeout(unsigned long data)
{
wake_up_process((task_t *) data);
}
(6) 设置超时时间,在等待队列上睡眠
本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u2/85048/showart_1900214.html |
|