时钟机制的,使用通知链技术来执行。
通知链技术,时钟源建立一个链表,然后待发生的事件将自己要执行的操作注册到链表上,简单吧,呵呵
通知链有好多种:
- 原子通知链( Atomic notifier chains ):通知链元素的回调函数(当事件发生时要执行的函数)只能在中断上下文中运行,不允许阻塞
- 可阻塞通知链( Blocking notifier chains ):通知链元素的回调函数在进程上下文中运行,允许阻塞
- 原始通知链( Raw notifier chains ):对通知链元素的回调函数没有任何限制,所有锁和保护机制都由调用者维护
- SRCU 通知链( SRCU notifier chains ):可阻塞通知链的一种变体
所以对应了四种通知链头结构:
- struct atomic_notifier_head :原子通知链的链头
- struct blocking_notifier_head :可阻塞通知链的链头
- struct raw_notifier_head :原始通知链的链头
- struct srcu_notifier_head : SRCU 通知链的链头
通知链元素的类型:- struct notifier_block :通知链中的元素,记录了当发出通知时,应该执行的操作(即回调函数)
先贴tick_init //向通知链中注册回调函数
kernel/time/tick-common.c
/**
* tick_init - initialize the tick control
*
* Register the notifier with the clockevents framework
*/
void __init tick_init(void)
{
clockevents_register_notifier(&tick_notifier); //向通知链中注册元素
}kernel/time/clockevents.c
static RAW_NOTIFIER_HEAD(clockevents_chain); //此链表维护了将要执行的事件操作
int clockevents_register_notifier(struct notifier_block *nb)
{
unsigned long flags;
int ret;spin_lock_irqsave(&clockevents_lock, flags);
ret = raw_notifier_chain_register(&clockevents_chain, nb);
spin_unlock_irqrestore(&clockevents_lock, flags);return ret;
}
static struct notifier_block tick_notifier = { //回调函数
.notifier_call = tick_notify,
};
/*
* Notification about clock event devices
*/
static int tick_notify(struct notifier_block *nb, unsigned long reason,
void *dev)
{
switch (reason) {case CLOCK_EVT_NOTIFY_ADD:
return tick_check_new_device(dev);case CLOCK_EVT_NOTIFY_BROADCAST_ON:
case CLOCK_EVT_NOTIFY_BROADCAST_OFF:
case CLOCK_EVT_NOTIFY_BROADCAST_FORCE:
tick_broadcast_on_off(reason, dev);
breakcase CLOCK_EVT_NOTIFY_BROADCAST_ENTER:
case CLOCK_EVT_NOTIFY_BROADCAST_EXIT:
tick_broadcast_oneshot_control(reason);
break;case CLOCK_EVT_NOTIFY_CPU_DYING:
tick_handover_do_timer(dev);
break;case CLOCK_EVT_NOTIFY_CPU_DEAD:
tick_shutdown_broadcast_oneshot(dev);
tick_shutdown_broadcast(dev);
tick_shutdown(dev);
break;case CLOCK_EVT_NOTIFY_SUSPEND:
tick_suspend();
tick_suspend_broadcast();
break;case CLOCK_EVT_NOTIFY_RESUME:
tick_resume();
break;default:
break;
}return NOTIFY_OK;
}该贴init_timers了
kernel/time/timer.cvoid __init init_timers(void)
{
int err = timer_cpu_notify(&timers_nb, (unsigned long)CPU_UP_PREPARE,
(void *)(long)smp_processor_id()); //首次调用初始化本CPU上的boot_tvec_baseinit_timer_stats();
BUG_ON(err == NOTIFY_BAD);
register_cpu_notifier(&timers_nb); //向CPU通知链注册回调函数定义在kernel/cpu.c中static __cpuinitdata RAW_NOTIFIER_HEAD(cpu_chain);
open_softirq(TIMER_SOFTIRQ, run_timer_softirq); //初始化软中断函数
}static struct notifier_block __cpuinitdata timers_nb = { //回调函数
.notifier_call = timer_cpu_notify,
};static int __cpuinit timer_cpu_notify(struct notifier_block *self, //回调函数,首次执行初始化本CPU的boot_tvec_base
unsigned long action, void *hcpu)
{
long cpu = (long)hcpu;
switch(action) {
case CPU_UP_PREPARE:
case CPU_UP_PREPARE_FROZEN:
if (init_timers_cpu(cpu) < 0)
return NOTIFY_BAD;
break;
#ifdef CONFIG_HOTPLUG_CPU
case CPU_DEAD:
case CPU_DEAD_FROZEN:
migrate_timers(cpu);
break;
#endif
default:
break;
}
return NOTIFY_OK;
}
static int __cpuinit init_timers_cpu(int cpu) //初始化本CPU上的boot_tvec_base
{
int j;
struct tvec_base *base;
static char __cpuinitdata tvec_base_done[NR_CPUS];if (!tvec_base_done[cpu]) {
static char boot_done;if (boot_done) {
/*
* The APs use this path later in boot
*/
base = kmalloc_node(sizeof(*base),
GFP_KERNEL | __GFP_ZERO,
cpu_to_node(cpu));
if (!base)
return -ENOMEM;/* Make sure that tvec_base is 2 byte aligned */
if (tbase_get_deferrable(base)) {
WARN_ON(1);kfree(base);
return -ENOMEM;
}
per_cpu(tvec_bases, cpu) = base;
} else {
/*
* This is for the boot CPU - we use compile-time
* static initialisation because per-cpu memory isn't
* ready yet and because the memory allocators are not
* initialised either.
*/
boot_done = 1;
base = &boot_tvec_bases;
}
tvec_base_done[cpu] = 1;
} else {
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);base->timer_jiffies = jiffies;
return 0;
}
void open_softirq(int nr, void (*action)(struct softirq_action *)) //初始化软中断函数
{
softirq_vec[nr].action = action;
}
这里有必要贴一下struct tvec_base了/kernel/timer.c
struct tvec_base {
spinlock_t lock;
struct timer_list *running_timer;
unsigned long timer_jiffies;
struct tvec_root tv1;
struct tvec tv2;
struct tvec tv3;
struct tvec tv4;
struct tvec tv5;
} ____cacheline_aligned;struct tvec {
struct list_head vec[TVN_SIZE];
};
- struct atomic_notifier_head :原子通知链的链头