求一个nextnexusmods账号2018共享,就用一次

&nbsp>&nbsp
&nbsp>&nbsp
linux教程 &nbsp>&nbsp
Linux中断处理之时钟中断--X86
摘要:转自:http://tech.ddvip.com/.html一:前言时钟是整个操作系统的脉搏,它为进程的时间片调度,定时事件提供了依据.另外,用户空间的很多操作都依赖于时钟,例如select.poll,make.操作系统管理的时间为分两种,一种称为当前时间,也即我们日常生活所用的时间.这个时间一般保存在CMOS中.主板中有特定的芯片为其提供计时依据.另外一种时间称为相对时间.例如系统运行时间.显然对计算机而然,相对时间比当前时间更
转自:http://tech.ddvip.com/.html
时钟是整个操作系统的脉搏,它为进程的时间片调度,定时事件提供了依据.另外,用户空间的很多操作都依赖于时钟,例如select.poll,make.操作系统管理的时间为分两种,一种称为当前时间,也即我们日常生活所用的时间.这个时间一般保存在CMOS中.主板中有特定的芯片为其提供计时依据.另外一种时间称为相对时间.例如系统运行时间.显然对计算机而然,相对时间比当前时间更为重要.
二:与时钟有关的硬件处理.
1):实时时钟(RTC)
该时钟独立于CPU和其它芯片.即使PC断电,该时钟还是继续运行.该计时由一块单独的芯片处理,并把时钟值存放CMOS.该时间可参在IRQ8上周期性的产生时间信号.频率在2Hz ~ 8192Hz之间.但在linux中,只是用RTC来获取当前时间.
2):时间戳计时器(TSC)
CPU附带了一个64位的时间戳寄存器,当时钟信号到来的时候.该寄存器内容自动加1,ARM中没有
3):可编程中断定时器(PIC)
该设备可以周期性的发送一个时间中断信号.发送中断信号的间隔可以对其进行编程控制.在linux系统中,该中断时间间隔由HZ表示.这个时间间隔也被称为一个节拍(tick).
4):CPU本地定时器
在处理器的本地APIC还提供了另外的一定定时设备.CPU本地定时器也可以单次或者周期性的产生中断信号.与上次描述的PIC相比.它有以下几点的区别:
APIC本地计时器是32位.而PIC是16位.由此APIC本地计时器可以提供更低频率的中断信号
本地APIC只把中断信号发送给本地CPU.而PIC发送的中断信号任何CPU都可以处理
APIC定时器是基于总线时钟信号的.而PIC有自己的内部时钟振荡器
5):高精度计时器(HPET)
在linux2.6中增加了对HPET的支持.HPET是一种由微软和intel联合开发的新型定时芯片.该设备有一组寄时器,每个寄时器对应有自己的时钟信号,时钟信号到来的时候就会自动加1.
实际上,在intel多理器系统与单处理器系统还有所不同:
在单处理系统中.所有计时活动过由PIC产生的时钟中断信号触发的
在多处理系统中,所有普通活动是由PIC产生的中断触发.所有具体的CPU活动,都由本地APIC触发的.
三:时钟中断相关代码分析
time_init()是时钟初始化函数,他由asmlinkage void __init start_kernel()调用.具体代码如下: //时钟中断初始化
void __init time_init(void)
//如果定义了HPET
#ifdef CONFIG_HPET_TIMER
if (is_hpet_capable()) {
* HPET initialization needs to do memory-mapped io. So, let
* us do a late initialization after mem_init().
late_time_init = hpet_time_
//从cmos 中取得实时时间
xtime.tv_sec = get_cmos_time();
//初始化wall_to_monotonic
wall_to_monotonic.tv_sec = -xtime.tv_
xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ);
wall_to_monotonic.tv_nsec = -xtime.tv_
//选择一个合适的定时器
cur_timer = select_timer();
printk(KERN_INFO &Using %s for high-res timesourcen&,cur_timer-&name);
//注册时间中断信号处理函数
time_init_hook();
}该函数从cmos取得了当前时间.并为调整时间精度选择了合适的定时器
转入time_init_hook():
void __init time_init_hook(void)
//注册中断处理函数
setup_irq(0, &;irq0);
}Irq0定义如下:
static struct irqaction irq0 = { timer_interrupt, SA_INTERRUPT, CPU_MASK_NONE, &timer&, NULL, NULL};
对应的中断处理函数为:timer_interrupt():
irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
//因为该函数会修改xtime的值,为避免多处理器竞争.先加锁
write_seqlock(&;xtime_lock);
//记录上一次时间中断的精确时间.做调准时钟用
cur_timer-&mark_offset();
do_timer_interrupt(irq, NULL, regs);
write_sequnlock(&;xtime_lock);
return IRQ_HANDLED;
核心处理函数为 do_timer_interrupt():
static inline void do_timer_interrupt(int irq, void *dev_id,
struct pt_regs *regs)
#ifdef CONFIG_X86_IO_APIC
if (timer_ack) {
spin_lock(&;i8259A_lock);
outb(0x0c, PIC_MASTER_OCW3);
/* Ack the IRQ; AEOI will end it automatically. */
inb(PIC_MASTER_POLL);
spin_unlock(&;i8259A_lock);
do_timer_interrupt_hook(regs);
//如果要进行时间同步,那就隔一段时间把当前时间写回coms
if ((time_status &; STA_UNSYNC) == 0 &;&;
xtime.tv_sec & last_rtc_update + 660 &;&;
(xtime.tv_nsec / 1000)
&= USEC_AFTER - ((unsigned) TICK_SIZE) / 2 &;&;
(xtime.tv_nsec / 1000)
&= USEC_BEFORE + ((unsigned) TICK_SIZE) / 2) {
/* horrible...FIXME */
if (efi_enabled) {
if (efi_set_rtc_mmss(xtime.tv_sec) == 0)
last_rtc_update = xtime.tv_
last_rtc_update = xtime.tv_sec - 600;
} else if (set_rtc_mmss(xtime.tv_sec) == 0)
last_rtc_update = xtime.tv_
last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */
#ifdef CONFIG_MCA
if( MCA_bus ) {
/* The PS/2 uses level-triggered interrupts. You can't
turn them off, nor would you want to (any attempt to
enable edge-triggered interrupts usually gets intercepted by a
special hardware circuit). Hence we have to acknowledge
the timer interrupt. Through some incredibly stupid
design idea, the reset for IRQ 0 is done by setting the
high bit of the PPI port B (0x61). Note that some PS/2s,
notably the 55SX, work fine if this is removed. */
irq = inb_p( 0x61 ); /* read the current state */
outb_p( irq|0x80, 0x61 ); /* reset the IRQ */
static inline void do_timer_interrupt_hook(struct pt_regs *regs)
do_timer(regs);
* In the SMP case we use the local APIC timer interrupt to do the
* profiling, except when we simulate SMP mode on a uniprocessor
* system, in that case we have to call the local interrupt handler.
#ifndef CONFIG_X86_LOCAL_APIC
//更新内核代码监管器。在每次时钟中断的时候。取得每一次中断前的esp,进而可以得到运行的函//数地址。这样就可以统计运行时间最长的函内核函数区域。以便于内核管理者优化
profile_tick(CPU_PROFILING, regs);
if (!using_apic_timer)
smp_local_timer_interrupt(regs);
}这里有几个重要的操作.先看do_timer():
void do_timer(struct pt_regs *regs)
// 更新jiffies计数.jiffies_64与jiffies在链接的时候,实际是指向同一个区域
jiffies_64++;
#ifndef CONFIG_SMP
/* SMP process accounting uses the local APIC timer */
//更新当前运行进程的与时钟相关的信息
update_process_times(user_mode(regs));
//更新当前时间.xtime的更新
update_times();
}Update_process_times()代码如下:
void update_process_times(int user_tick)
struct task_struct *p =
int cpu = smp_processor_id(), system = user_tick ^ 1;
update_one_process(p, user_tick, system, cpu);
//激活时间软中断
run_local_timers();
//减少时间片。这个函数涉及到的东西过多,等到进程调度的时候再来分析。请关注本站更新*^_^*
scheduler_tick(user_tick, system);
}先看update_one_process():
static void update_one_process(struct task_struct *p, unsigned long user,
unsigned long system, int cpu)
do_process_times(p, user, system);
//检查进程的定时器
do_it_virt(p, user);
do_it_prof(p);
} 在这里简单介绍一下do_it_virt()与do_it_prof():这两个函数主要
检查用户空间的进程定时器是否到期.在进程的内存描述符有相关的字段.如下: struct task_struct{
unsigned long it_real_value, it_prof_value,it_virt_
unsigned long it_real_incr, it_prof_incr, it_virt_
struct timer_list real_
}(1)真实间隔定时器(ITIMER_REAL):这种间隔定时器在启动后,不管进程是否运行,每个时钟滴答都将其间隔计数器减1。当减到0值时,内核向进程发送SIGALRM信号。结构类型task_struct中的成员it_real_incr则表示真实间隔定时器的间隔计数器的初始值,而成员it_real_value则表示真实间隔定时器的间隔计数器的当前值。由于这种间隔定时器本质上与上一节的内核定时器时一样的,因此Linux实际上是通过real_timer这个内嵌在task_struct结构中的内核动态定时器来实现真实间隔定时器ITIMER_REAL的。
(2)虚拟间隔定时器ITIMER_VIRT:也称为进程的用户态间隔定时器。结构类型task_struct中成员it_virt_incr和it_virt_value分别表示虚拟间隔定时器的间隔计数器的初始值和当前值,二者均以时钟滴答次数位计数单位。当虚拟间隔定时器启动后,只有当进程在用户态下运行时,一次时钟滴答才能使间隔计数器当前值it_virt_value减1。当减到0值时,内核向进程发送SIGVTALRM信号(虚拟闹钟信号),并将it_virt_value重置为初值it_virt_incr。具体请见7.4.3节中的do_it_virt()函数的实现。
(3)PROF间隔定时器ITIMER_PROF:进程的task_struct结构中的it_prof_value和it_prof_incr成员分别表示PROF间隔定时器的间隔计数器的当前值和初始值(均以时钟滴答为单位)。当一个进程的PROF间隔定时器启动后,则只要该进程处于运行中,而不管是在用户态或核心态下执行,每个时钟滴答都使间隔计数器it_prof_value值减1。当减到0值时,内核向进程发送SIGPROF信号,并将it_prof_value重置为初值it_prof_incr.
do_process_times():
static inline void do_process_times(struct task_struct *p,
unsigned long user, unsigned long system)
//p-&utime:在用户空间所花的时间
psecs = (p-&utime += user);
//p-&stime:在系统空间所花的时间
psecs += (p-&stime += system);
//如果运行的时间片到达
if (psecs / HZ &= p-&rlim[RLIMIT_CPU].rlim_cur) {
/* Send SIGXCPU every second.. */
//每秒发送一个SIGXCPU
if (!(psecs % HZ))
send_sig(SIGXCPU, p, 1);
/* and SIGKILL when we go over max.. */
//发送SIGKILL
if (psecs / HZ &= p-&rlim[RLIMIT_CPU].rlim_max)
send_sig(SIGKILL, p, 1);
该函数检查当前进程的时间片是否到达,如果到达就给当前进程发送SIGKILL和SIGXCPU
do_it_virt()/do_it_prof()检查过程的定时器是否到期.如果到期就给进程发送相应的信号:
static inline void do_it_virt(struct task_struct * p, unsigned long ticks)
unsigned long it_virt = p-&it_virt_
if (it_virt) {
it_virt -=
if (!it_virt) {
it_virt = p-&it_virt_
//发送SIGVTALRM
send_sig(SIGVTALRM, p, 1);
p-&it_virt_value = it_
}返回到update_process_times()的其它函数:
run_local_timers()
void run_local_timers(void)
raise_softirq(TIMER_SOFTIRQ);
激活时间软中断.这个函数我们在IRQ中断中已经分析过了,不再赘述
我们在do_timer()还漏掉了一个函数:
static inline void update_times(void)
//wall_jiffies:上一次更新的值
ticks = jiffies - wall_
if (ticks) {
wall_jiffies +=
//更新xtime
update_wall_time(ticks);
//统计TASK_RUNNING TASK_UNINTERRUPTIBLE进程数量
calc_load(ticks);
在模块的编写过程中,我们经常使用定时器来等待一段时间之后再来执行某一个操作。为方便分析,写了下列一段测试程序: #include
MODULE_LICENSE(&GPL&);
void test_timerfuc(unsigned long x)
printk(&Eric xiao test ......n&);
//声明一个定个器
struct timer_list test_timer = TIMER_INITIALIZER(test_timerfuc, 0, 0);
int kernel_test_init()
printk(&test_initn&);
//修改定时器到期时间。为3个HZ。一个HZ产生一个时钟中断
mod_timer(&;test_timer,jiffies+3*HZ);
//把定时器加入时钟软中断处理链表
add_timer(&;test_timer);
int kernel_test_exit()
printk(&test_exitn&);
module_init(kernel_test_init);
module_exit(kernel_test_exit);
上面的例子程序比较简单,我们从这个例子开始研究linux下的定时器实现
TIMER_INITIALIZER():
1):TIMER_INITIALIZER()用来声明一个定时器,它的定义如下:
#define TIMER_INITIALIZER(_function, _expires, _data) {
.function = (_function),
.expires = (_expires),
.data = (_data),
.base = NULL,
.magic = TIMER_MAGIC,
.lock = SPIN_LOCK_UNLOCKED, }
Struct timer_list定义如下:
struct timer_list {
//用来形成链表
struct list_
//定始器到达时间
//定时器时间到达后,所要运行的函数
void (*function)(unsigned long);
//定时器函数对应的参数
//挂载这个定时器的tvec_t_base_s.这个结构我们等会会看到
struct tvec_t_base_s *
从上面的过程中我们可以看到TIMER_INITIALIZER()只是根据传入的参数初始化了struct timer_list结构.并把magic 成员初始化成TIMER_MAGIC
2): mod_timer():修改定时器的到时时间
int mod_timer(struct timer_list *timer, unsigned long expires)
//如果该定时器没有定义fuction
BUG_ON(!timer-&function);
//判断timer的magic是否为TIMER_MAGIC.如果不是,则将其修正为TIMER_MAGIC
check_timer(timer);
//如果要调整的时间就是定时器的定时时间而且已经被激活,则直接返回
if (timer-&expires == expires &;&; timer_pending(timer))
//调用_mod_timer().呆会再给出分析
return __mod_timer(timer, expires);
3): add_timer()用来将定时器挂载到定时软中断队列,激活该定时器
static inline void add_timer(struct timer_list * timer)
__mod_timer(timer, timer-&expires);
可以看到mod_timer与add_timer 最后都会调用__mod_timer().为了分析这个函数,我们先来了解一下定时系统相关的数据结构.
tvec_bases: per cpu变量,它的定义如下:
static DEFINE_PER_CPU(tvec_base_t, tvec_bases) = { SPIN_LOCK_UNLOCKED };
由此可以看到tves_bases的数型数据为teves_base_t.数据结构的定义如下:
typedef struct tvec_t_base_s tvec_base_t;
struct tvec_t_base_s的定义:
struct tvec_t_base_s {
//上一次运行计时器的jiffies 值
unsigned long timer_
struct timer_list *running_
//tv1 tv2 tv3 tv4 tv5是五个链表数组
tvec_root_t tv1;
tvec_t tv2;
tvec_t tv3;
tvec_t tv4;
tvec_t tv5;
} ____cacheline_aligned_in_
Tves_root_t与tvec_t的定义如下:
#define TVN_BITS 6
#define TVR_BITS 8
#define TVN_SIZE (1 && TVN_BITS)
#define TVR_SIZE (1 && TVR_BITS)
#define TVN_MASK (TVN_SIZE - 1)
#define TVR_MASK (TVR_SIZE - 1)
typedef struct tvec_s {
struct list_head vec[TVN_SIZE];
typedef struct tvec_root_s {
struct list_head vec[TVR_SIZE];
} tvec_root_t;
系统规定定时器最大超时时间间隔为0xFFFFFFFF.即为一个32位数.即使在64位系统上.如果超过此值也会将其强制设这oxFFFFFFFF(这在后面的代码分析中可以看到).内核最关心的就是间隔在0~255个HZ之间的定时器.次重要的是间隔在255~1&&(8+6)之间的定时器.第三重要的是间隔在1&&(8+6) ~ 1&&(8+6+6)之间的定器.依次往下推.也就是把32位的定时间隔为份了五个部份.1个8位.4个6位.所以内核定义了五个链表数组.第一个链表数组大小为8位大小,也即上面定义的 #define
TVR_SIZE (1 && TVR_BITS).其它的四个数组大小为6位大小.即上面定义的#define TVN_SIZE (1 && TVN_BITS)
在加入定时器的时候,按照时间间隔把定时器加入到相应的数组即可.了解这点之后,就可以来看__mod_timer()的代码了:
//修改timer或者新增一个timer都会调用此接口
int __mod_timer(struct timer_list *timer, unsigned long expires)
tvec_base_t *old_base, *new_
int ret = 0;
//入口参数检测
BUG_ON(!timer-&function);
check_timer(timer);
spin_lock_irqsave(&;timer-&lock, flags);
//取得当前CPU对应的tvec_bases
new_base = &;__get_cpu_var(tvec_bases);
//该定时器所在的tvec_bases.对于新增的timer.它的base字段为NULL
old_base = timer-&
* Prevent deadlocks via ordering by old_base & new_base.
//在把timer从当前tvec_bases摘下来之前,要充分考虑好竞争的情况
if (old_base &;&; (new_base != old_base)) {
//按次序获得锁
if (old_base & new_base) {
spin_lock(≠w_base-&lock);
spin_lock(&;old_base-&lock);
spin_lock(&;old_base-&lock);
spin_lock(≠w_base-&lock);
* The timer base might have been cancelled while we were
* trying to take the lock(s):
//如果timer-&base != old_base.那就是说在Lock的时候.其它CPU更改它的值
//那就解锁.重新判断
if (timer-&base != old_base) {
spin_unlock(≠w_base-&lock);
spin_unlock(&;old_base-&lock);
//old_base == NULl 或者是 new_base==old_base的情况
spin_lock(≠w_base-&lock);
//同理,在Lock的时候timer会生了改变
if (timer-&base != old_base) {
spin_unlock(≠w_base-&lock);
* Delete the previous timeout (if there was any), and install
* the new one:
//将其从其它的tvec_bases上删除.注意运行到这里的话,说话已经被Lock了
if (old_base) {
list_del(&;timer-&entry);
//修改它的定时器到达时间
timer-&expires =
//将其添加到new_base中
internal_add_timer(new_base, timer);
//修改base字段
timer-base = new_
//操作完了,解锁
if (old_base &;&; (new_base != old_base))
spin_unlock(&;old_base-&lock);
spin_unlock(≠w_base-&lock);
spin_unlock_irqrestore(&;timer-&lock, flags);
}internal_add_timer()的代码如下:
static void internal_add_timer(tvec_base_t *base, struct timer_list *timer)
//定时器到达的时间
unsigned long expires = timer-&
//计算时间间间隔
unsigned long idx = expires - base-&timer_
struct list_head *
//根据时间间隔,将timer放入相应数组的相应位置
if (idx & TVR_SIZE) {
int i = expires &; TVR_MASK;
vec = base-&tv1.vec +
} else if (idx & 1 && (TVR_BITS + TVN_BITS)) {
int i = (expires && TVR_BITS) &; TVN_MASK;
vec = base-&tv2.vec +
} else if (idx & 1 && (TVR_BITS + 2 * TVN_BITS)) {
int i = (expires && (TVR_BITS + TVN_BITS)) &; TVN_MASK;
vec = base-&tv3.vec +
} else if (idx & 1 && (TVR_BITS + 3 * TVN_BITS)) {
int i = (expires && (TVR_BITS + 2 * TVN_BITS)) &; TVN_MASK;
vec = base-&tv4.vec +
} else if ((signed long) idx & 0) {
* Can happen if you add a timer with expires == jiffies,
* or you set a timer to go off in the past
//如果间隔小于0
vec = base-&tv1.vec + (base-&timer_jiffies &; TVR_MASK);
/* If the timeout is larger than 0xffffffff on 64-bit
* architectures then we use the maximum timeout:
//时间间隔超长,将其设为oxFFFFFFFF
if (idx & 0xffffffffUL) {
idx = 0xffffffffUL;
expires = idx + base-&timer_
i = (expires && (TVR_BITS + 3 * TVN_BITS)) &; TVN_MASK;
vec = base-&tv5.vec +
* Timers are FIFO:
//加入到链表末尾
list_add_tail(&;timer-&entry, vec);
} 计算时间间隔即可知道要加入到哪一个数组.哪又怎么计算加入到该数组的那一项呢?
对于间隔时间在0~255的定时器: 它的计算方式是将定时器到达时间的低八位与低八位为1的数相与而成
对于第1个六位,它是先将到达时间右移8位.然后与低六位全为1的数相与而成
对于第2个六位, 它是先将到达时间右移8+6位.然后与低六位全为1的数相与而成
依次为下推…
在后面结合超时时间到达的情况再来分析相关部份
4):定时器更新
每过一个HZ,就会检查当前是否有定时器的定时器时间到达.如果有,运行它所注册的函数,再将其删除.为了分析这一过程,我们先从定时器系统的初始化看起.
asmlinkage void __init start_kernel(void)
init_timers();
}Init_timers()的定义如下:
void __init init_timers(void)
timer_cpu_notify(&;timers_nb, (unsigned long)CPU_UP_PREPARE,
(void *)(long)smp_processor_id());
register_cpu_notifier(&;timers_nb);
//注册TIMER_SOFTIRQ软中断
open_softirq(TIMER_SOFTIRQ, run_timer_softirq, NULL);
timer_cpu_notify()&init_timers_cpu():
static void /* __devinit */ init_timers_cpu(int cpu)
tvec_base_t *
//初始化各个数组中的链表
base = &;per_cpu(tvec_bases, cpu);
spin_lock_init(&;base-&lock);
for (j = 0; j & TVN_SIZE; j++) {
INIT_LIST_HEAD(base-&tv5.vec + j);
INIT_LIST_HEAD(base-&tv4.vec + j);
INIT_LIST_HEAD(base-&tv3.vec + j);
INIT_LIST_HEAD(base-&tv2.vec + j);
for (j = 0; j & TVR_SIZE; j++)
INIT_LIST_HEAD(base-&tv1.vec + j);
//将最近到达时间设为当前jiffies
base-&timer_jiffies =
我们在前面分析过,每当时钟当断函数到来的时候,就会打开定时器的软中断.运行其软中断函数.run_timer_softirq()
static void run_timer_softirq(struct softirq_action *h)
//取得当于CPU的tvec_base_t结构
tvec_base_t *base = &;__get_cpu_var(tvec_bases);
//如果jiffies & base-&timer_jiffies
if (time_after_eq(jiffies, base-&timer_jiffies))
__run_timers(base);
}__run_timers()代码如下:
static inline void __run_timers(tvec_base_t *base)
struct timer_list *
spin_lock_irqsave(&;base-&lock, flags);
//因为CPU可能关闭中断,引起时钟中断信号丢失.可能jiffies要大base-&timer_jiffies 好几个
while (time_after_eq(jiffies, base-&timer_jiffies)) {
//定义并初始化一个链表
struct list_head work_list = LIST_HEAD_INIT(work_list);
struct list_head *head = &;work_
int index = base-&timer_jiffies &; TVR_MASK;
* Cascade timers:
//当index == 0时,说明已经循环了一个周期
//则将tv2填充tv1.如果tv2为空,则用tv3填充tv2.依次类推......
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
++base-&timer_
//将base-&tv1.vec项移至work_list.并将base-&tv1.vec置空
list_splice_init(base-&tv1.vec + index, &;work_list);
//work_List中的定时器是已经到时的定时器
if (!list_empty(head)) {
void (*fn)(unsigned long);
//遍历链表中的每一项.运行它所对应的函数,并将定时器从链表上脱落
timer = list_entry(head-&next,struct timer_list,entry);
fn = timer-&
data = timer-&
list_del(&;timer-&entry);
set_running_timer(base, timer);
smp_wmb();
timer-&base = NULL;
spin_unlock_irqrestore(&;base-&lock, flags);
spin_lock_irq(&;base-&lock);
set_running_timer(base, NULL);
spin_unlock_irqrestore(&;base-&lock, flags);
}如果base-&timer_jiffies低八位为零.说明它向第九位有进位.所以把第九位到十五位对应的定时器搬到前八位对应的数组.如果第九位到十五位为空的话.就到它的上个六位去搬数据.上面的代码也说明.要经过1&&8个HZ才会更新全部数组中的定时器.这样做的效率是很高的.
分析下里面的两个重要的子函数:
static int cascade(tvec_base_t *base, tvec_t *tv, int index)
/* cascade all the timers from tv up one level */
struct list_head *head, *
//取数组中序号对应的链表
head = tv-&vec +
curr = head-&
* We are removing _all_ timers from the list, so we don't have to
* detach them individually, just clear the list afterwards.
//遍历这个链表,将定时器重新插入到base中
while (curr != head) {
struct timer_list *
tmp = list_entry(curr, struct timer_list, entry);
BUG_ON(tmp-&base != base);
curr = curr-&
internal_add_timer(base, tmp);
//将链表设为初始化状态
INIT_LIST_HEAD(head);
//将list中的数据放入head中,并将list置为空
static inline void list_splice_init(struct list_head *list,
struct list_head *head)
if (!list_empty(list)) {
__list_splice(list, head);
INIT_LIST_HEAD(list);
//将list中的数据放入head
static inline void __list_splice(struct list_head *list,struct list_head *head)
//list的第一个元素
struct list_head *first = list-&
//list的最后一个元素
struct list_head *last = list-&
//head的第一个元素
struct list_head *at = head-&
将first对应的链表链接至head
first-&prev =
head-&next =
//将head 原有的数据加入到链表末尾
last-&next =
at-&prev =
5):del_timer()删除定时器
//删除一个timer
int del_timer(struct timer_list *timer)
tvec_base_t *
check_timer(timer);
base = timer-&
//该定时器没有被激活
if (!base)
spin_lock_irqsave(&;base-&lock, flags);
//如果在加锁的过程中,有其它操作改变了timer
if (base != timer-&base) {
spin_unlock_irqrestore(&;base-&lock, flags);
//将timer从链表中删除
list_del(&;timer-&entry);
timer-&base = NULL;
spin_unlock_irqrestore(&;base-&lock, flags);
6): del_timer_sync()有竞争情况下的定时器删除
在SMP系统中,可能要删除的定时器正在某一个CPU上运行.为了防止这种在情况.在删除定时器的时候,应该优先使用del_timer_synsc().它会一直等待所有CPU上的定时器执行完成.
int del_timer_sync(struct timer_list *timer)
tvec_base_t *
int i, ret = 0;
check_timer(timer);
del_again:
//删除些定时器
ret += del_timer(timer);
for_each_online_cpu(i) {
base = &;per_cpu(tvec_bases, i);
//如果此CPU正在运行这个timer
if (base-&running_timer == timer) {
//一直等待,直到这个CPU执行完
while (base-&running_timer == timer) {
cpu_relax();
preempt_check_resched();
smp_rmb();
//如果这个timer又被调用.再删除
if (timer_pending(timer))
定时器部份到这里就介绍完了.为了管理定时器.内核用了一个很巧妙的数据结构.值得好好的体会.
2.6内核在时钟管理子系统的修改比较大.因为在2.6完全摒弃掉了下半部机制.2.4中下半部处理的大部份都放在了中断处理程序里,只有定时器控制被移到了时钟软中断.另外时钟中断初始化涉及到了很多硬件的操作.需要查阅相关资料才能完全理解.
以上是的内容,更多
的内容,请您使用右上方搜索功能获取相关信息。
若你要投稿、删除文章请联系邮箱:zixun-group@service.aliyun.com,工作人员会在五个工作日内给你回复。
云服务器 ECS
可弹性伸缩、安全稳定、简单易用
&40.8元/月起
预测未发生的攻击
&24元/月起
为您提供0门槛上云实践机会
你可能还喜欢
你可能感兴趣
阿里云教程中心为您免费提供
Linux中断处理之时钟中断--X86相关信息,包括
的信息,所有Linux中断处理之时钟中断--X86相关内容均不代表阿里云的意见!投稿删除文章请联系邮箱:zixun-group@service.aliyun.com,工作人员会在五个工作日内答复
售前咨询热线
服务与支持
账号与支持
关注阿里云
International}

我要回帖

更多关于 nexusmods账号共享 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信