国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費(fèi)電子書(shū)等14項(xiàng)超值服

開(kāi)通VIP
Linux中斷處理之時(shí)鐘中斷
一:前言
時(shí)鐘是整個(gè)操作系統(tǒng)的脈搏,它為進(jìn)程的時(shí)間片調(diào)度,定時(shí)事件提供了依據(jù).另外,用戶(hù)空間的很多操作都依賴(lài)于時(shí)鐘,例如select.poll,make.操作系統(tǒng)管理的時(shí)間為分兩種,一種稱(chēng)為當(dāng)前時(shí)間,也即我們?nèi)粘I钏玫臅r(shí)間.這個(gè)時(shí)間一般保存在CMOS中.主板中有特定的芯片為其提供計(jì)時(shí)依據(jù).另外一種時(shí)間稱(chēng)為相對(duì)時(shí)間.例如系統(tǒng)運(yùn)行時(shí)間.顯然對(duì)計(jì)算機(jī)而然,相對(duì)時(shí)間比當(dāng)前時(shí)間更為重要.
二:與時(shí)鐘有關(guān)的硬件處理.
1):實(shí)時(shí)時(shí)鐘(RTC)
該時(shí)鐘獨(dú)立于CPU和其它芯片.即使PC斷電,該時(shí)鐘還是繼續(xù)運(yùn)行.該計(jì)時(shí)由一塊單獨(dú)的芯片處理,并把時(shí)鐘值存放CMOS.該時(shí)間可參在IRQ8上周期性的產(chǎn)生時(shí)間信號(hào).頻率在2Hz ~ 8192Hz之間.但在linux中,只是用RTC來(lái)獲取當(dāng)前時(shí)間.
2):時(shí)間戳計(jì)時(shí)器(TSC)
CPU附帶了一個(gè)64位的時(shí)間戳寄存器,當(dāng)時(shí)鐘信號(hào)到來(lái)的時(shí)候.該寄存器內(nèi)容自動(dòng)加1
3):可編程中斷定時(shí)器(PIC)
該設(shè)備可以周期性的發(fā)送一個(gè)時(shí)間中斷信號(hào).發(fā)送中斷信號(hào)的間隔可以對(duì)其進(jìn)行編程控制.在linux系統(tǒng)中,該中斷時(shí)間間隔由HZ表示.這個(gè)時(shí)間間隔也被稱(chēng)為一個(gè)節(jié)拍(tick).
4):CPU本地定時(shí)器
在處理器的本地APIC還提供了另外的一定定時(shí)設(shè)備.CPU本地定時(shí)器也可以單次或者周期性的產(chǎn)生中斷信號(hào).與上次描述的PIC相比.它有以下幾點(diǎn)的區(qū)別:
APIC本地計(jì)時(shí)器是32位.而PIC是16位.由此APIC本地計(jì)時(shí)器可以提供更低頻率的中斷信號(hào)
本地APIC只把中斷信號(hào)發(fā)送給本地CPU.而PIC發(fā)送的中斷信號(hào)任何CPU都可以處理
APIC定時(shí)器是基于總線(xiàn)時(shí)鐘信號(hào)的.而PIC有自己的內(nèi)部時(shí)鐘振蕩器
5):高精度計(jì)時(shí)器(HPET)
在linux2.6中增加了對(duì)HPET的支持.HPET是一種由微軟和intel聯(lián)合開(kāi)發(fā)的新型定時(shí)芯片.該設(shè)備有一組寄時(shí)器,每個(gè)寄時(shí)器對(duì)應(yīng)有自己的時(shí)鐘信號(hào),時(shí)鐘信號(hào)到來(lái)的時(shí)候就會(huì)自動(dòng)加1.
實(shí)際上,在intel多理器系統(tǒng)與單處理器系統(tǒng)還有所不同:
在單處理系統(tǒng)中.所有計(jì)時(shí)活動(dòng)過(guò)由PIC產(chǎn)生的時(shí)鐘中斷信號(hào)觸發(fā)的
在多處理系統(tǒng)中,所有普通活動(dòng)是由PIC產(chǎn)生的中斷觸發(fā).所有具體的CPU活動(dòng),都由本地APIC觸發(fā)的.
三:時(shí)鐘中斷相關(guān)代碼分析
time_init()是時(shí)鐘初始化函數(shù),他由asmlinkage void __init start_kernel()調(diào)用.具體代碼如下:
//時(shí)鐘中斷初始化
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_init;
return;
}
#endif
//從cmos 中取得實(shí)時(shí)時(shí)間
xtime.tv_sec = get_cmos_time();
//初始化wall_to_monotonic
wall_to_monotonic.tv_sec = -xtime.tv_sec;
xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ);
wall_to_monotonic.tv_nsec = -xtime.tv_nsec;
//選擇一個(gè)合適的定時(shí)器
cur_timer = select_timer();
printk(KERN_INFO "Using %s for high-res timesource\n",cur_timer->name);
//注冊(cè)時(shí)間中斷信號(hào)處理函數(shù)
time_init_hook();
}
該函數(shù)從cmos取得了當(dāng)前時(shí)間.并為調(diào)整時(shí)間精度選擇了合適的定時(shí)器
轉(zhuǎn)入time_init_hook():
void __init time_init_hook(void)
{
//注冊(cè)中斷處理函數(shù)
setup_irq(0, &irq0);
}
Irq0定義如下:
static struct irqaction irq0  = { timer_interrupt, SA_INTERRUPT, CPU_MASK_NONE, "timer", NULL, NULL};
對(duì)應(yīng)的中斷處理函數(shù)為:timer_interrupt():
irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
//因?yàn)樵摵瘮?shù)會(huì)修改xtime的值,為避免多處理器競(jìng)爭(zhēng).先加鎖
write_seqlock(&xtime_lock);
//記錄上一次時(shí)間中斷的精確時(shí)間.做調(diào)準(zhǔn)時(shí)鐘用
cur_timer->mark_offset();
do_timer_interrupt(irq, NULL, regs);
//解鎖
write_sequnlock(&xtime_lock);
return IRQ_HANDLED;
}
核心處理函數(shù)為 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);
}
#endif
do_timer_interrupt_hook(regs);
//如果要進(jìn)行時(shí)間同步,那就隔一段時(shí)間把當(dāng)前時(shí)間寫(xiě)回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_sec;
else
last_rtc_update = xtime.tv_sec - 600;
} else if (set_rtc_mmss(xtime.tv_sec) == 0)
last_rtc_update = xtime.tv_sec;
else
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 */
}
#endif
}
我們忽略選擇編譯部份,轉(zhuǎn)到do_timer_interrupt_hook()
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
//更新內(nèi)核代碼監(jiān)管器。在每次時(shí)鐘中斷的時(shí)候。取得每一次中斷前的esp,進(jìn)而可以得到運(yùn)行的函//數(shù)地址。這樣就可以統(tǒng)計(jì)運(yùn)行時(shí)間最長(zhǎng)的函內(nèi)核函數(shù)區(qū)域。以便于內(nèi)核管理者優(yōu)化
profile_tick(CPU_PROFILING, regs);
#else
if (!using_apic_timer)
smp_local_timer_interrupt(regs);
#endif
}
這里有幾個(gè)重要的操作.先看do_timer():
void do_timer(struct pt_regs *regs)
{
// 更新jiffies計(jì)數(shù).jiffies_64與jiffies在鏈接的時(shí)候,實(shí)際是指向同一個(gè)區(qū)域
jiffies_64++;
#ifndef CONFIG_SMP
/* SMP process accounting uses the local APIC timer */
//更新當(dāng)前運(yùn)行進(jìn)程的與時(shí)鐘相關(guān)的信息
update_process_times(user_mode(regs));
#endif
//更新當(dāng)前時(shí)間.xtime的更新
update_times();
}
Update_process_times()代碼如下:
void update_process_times(int user_tick)
{
struct task_struct *p = current;
int cpu = smp_processor_id(), system = user_tick ^ 1;
update_one_process(p, user_tick, system, cpu);
//激活時(shí)間軟中斷
run_local_timers();
//減少時(shí)間片。這個(gè)函數(shù)涉及到的東西過(guò)多,等到進(jìn)程調(diào)度的時(shí)候再來(lái)分析。請(qǐng)關(guān)注本站更新*^_^*
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);
//檢查進(jìn)程的定時(shí)器
do_it_virt(p, user);
do_it_prof(p);
}   
在這里簡(jiǎn)單介紹一下do_it_virt()與do_it_prof():
這兩個(gè)函數(shù)主要檢查用戶(hù)空間的進(jìn)程定時(shí)器是否到期.在進(jìn)程的內(nèi)存描述符有相關(guān)的字段.如下:
struct task_struct{
unsigned long it_real_value, it_prof_value, it_virt_value;
unsigned long it_real_incr, it_prof_incr, it_virt_incr;
struct timer_list real_timer;
}
(1)真實(shí)間隔定時(shí)器(ITIMER_REAL):這種間隔定時(shí)器在啟動(dòng)后,不管進(jìn)程是否運(yùn)行,每個(gè)時(shí)鐘滴答都將其間隔計(jì)數(shù)器減1。當(dāng)減到0值時(shí),內(nèi)核向進(jìn)程發(fā)送SIGALRM信號(hào)。結(jié)構(gòu)類(lèi)型task_struct中的成員it_real_incr則表示真實(shí)間隔定時(shí)器的間隔計(jì)數(shù)器的初始值,而成員it_real_value則表示真實(shí)間隔定時(shí)器的間隔計(jì)數(shù)器的當(dāng)前值。由于這種間隔定時(shí)器本質(zhì)上與上一節(jié)的內(nèi)核定時(shí)器時(shí)一樣的,因此Linux實(shí)際上是通過(guò)real_timer這個(gè)內(nèi)嵌在task_struct結(jié)構(gòu)中的內(nèi)核動(dòng)態(tài)定時(shí)器來(lái)實(shí)現(xiàn)真實(shí)間隔定時(shí)器ITIMER_REAL的。
(2)虛擬間隔定時(shí)器ITIMER_VIRT:也稱(chēng)為進(jìn)程的用戶(hù)態(tài)間隔定時(shí)器。結(jié)構(gòu)類(lèi)型task_struct中成員it_virt_incr和it_virt_value分別表示虛擬間隔定時(shí)器的間隔計(jì)數(shù)器的初始值和當(dāng)前值,二者均以時(shí)鐘滴答次數(shù)位計(jì)數(shù)單位。當(dāng)虛擬間隔定時(shí)器啟動(dòng)后,只有當(dāng)進(jìn)程在用戶(hù)態(tài)下運(yùn)行時(shí),一次時(shí)鐘滴答才能使間隔計(jì)數(shù)器當(dāng)前值it_virt_value減1。當(dāng)減到0值時(shí),內(nèi)核向進(jìn)程發(fā)送SIGVTALRM信號(hào)(虛擬鬧鐘信號(hào)),并將it_virt_value重置為初值it_virt_incr。具體請(qǐng)見(jiàn)7.4.3節(jié)中的do_it_virt()函數(shù)的實(shí)現(xiàn)。
(3)PROF間隔定時(shí)器ITIMER_PROF:進(jìn)程的task_struct結(jié)構(gòu)中的it_prof_value和it_prof_incr成員分別表示PROF間隔定時(shí)器的間隔計(jì)數(shù)器的當(dāng)前值和初始值(均以時(shí)鐘滴答為單位)。當(dāng)一個(gè)進(jìn)程的PROF間隔定時(shí)器啟動(dòng)后,則只要該進(jìn)程處于運(yùn)行中,而不管是在用戶(hù)態(tài)或核心態(tài)下執(zhí)行,每個(gè)時(shí)鐘滴答都使間隔計(jì)數(shù)器it_prof_value值減1。當(dāng)減到0值時(shí),內(nèi)核向進(jìn)程發(fā)送SIGPROF信號(hào),并將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)
{
unsigned long psecs;
//p->utime:在用戶(hù)空間所花的時(shí)間
psecs = (p->utime += user);
//p->stime:在系統(tǒng)空間所花的時(shí)間
psecs += (p->stime += system);
//如果運(yùn)行的時(shí)間片到達(dá)
if (psecs / HZ >= p->rlim[RLIMIT_CPU].rlim_cur) {
/* Send SIGXCPU every second.. */
//每秒發(fā)送一個(gè)SIGXCPU
if (!(psecs % HZ))
send_sig(SIGXCPU, p, 1);
/* and SIGKILL when we go over max.. */
//發(fā)送SIGKILL
if (psecs / HZ >= p->rlim[RLIMIT_CPU].rlim_max)
send_sig(SIGKILL, p, 1);
}
}
該函數(shù)檢查當(dāng)前進(jìn)程的時(shí)間片是否到達(dá),如果到達(dá)就給當(dāng)前進(jìn)程發(fā)送SIGKILL和SIGXCPU
do_it_virt()/do_it_prof()檢查過(guò)程的定時(shí)器是否到期.如果到期就給進(jìn)程發(fā)送相應(yīng)的信號(hào):
static inline void do_it_virt(struct task_struct * p, unsigned long ticks)
{
unsigned long it_virt = p->it_virt_value;
if (it_virt) {
it_virt -= ticks;
if (!it_virt) {
it_virt = p->it_virt_incr;
//發(fā)送SIGVTALRM
send_sig(SIGVTALRM, p, 1);
}
p->it_virt_value = it_virt;
}
}
返回到update_process_times()的其它函數(shù):
run_local_timers()
void run_local_timers(void)
{
raise_softirq(TIMER_SOFTIRQ);
}
激活時(shí)間軟中斷.這個(gè)函數(shù)我們?cè)贗RQ中斷中已經(jīng)分析過(guò)了,不再贅述
我們?cè)赿o_timer()還漏掉了一個(gè)函數(shù):
static inline void update_times(void)
{
unsigned long ticks;
//wall_jiffies:上一次更新的值
ticks = jiffies - wall_jiffies;
if (ticks) {
wall_jiffies += ticks;
//更新xtime
update_wall_time(ticks);
}
//統(tǒng)計(jì)TASK_RUNNING TASK_UNINTERRUPTIBLE進(jìn)程數(shù)量
calc_load(ticks);
}
四:定時(shí)器
在模塊的編寫(xiě)過(guò)程中,我們經(jīng)常使用定時(shí)器來(lái)等待一段時(shí)間之后再來(lái)執(zhí)行某一個(gè)操作。為方便分析,寫(xiě)了下列一段測(cè)試程序:
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/timer.h>
MODULE_LICENSE("GPL");
void test_timerfuc(unsigned long x)
{
printk("Eric xiao test ......\n");
}
//聲明一個(gè)定個(gè)器
struct timer_list test_timer = TIMER_INITIALIZER(test_timerfuc, 0, 0);
int kernel_test_init()
{
printk("test_init\n");
//修改定時(shí)器到期時(shí)間。為3個(gè)HZ。一個(gè)HZ產(chǎn)生一個(gè)時(shí)鐘中斷
mod_timer(&test_timer,jiffies+3*HZ);
//把定時(shí)器加入時(shí)鐘軟中斷處理鏈表
add_timer(&test_timer);
}
int kernel_test_exit()
{
printk("test_exit\n");
return 0;
}
module_init(kernel_test_init);
module_exit(kernel_test_exit);
上面的例子程序比較簡(jiǎn)單,我們從這個(gè)例子開(kāi)始研究linux下的定時(shí)器實(shí)現(xiàn)。
TIMER_INITIALIZER():
1):TIMER_INITIALIZER()用來(lái)聲明一個(gè)定時(shí)器,它的定義如下:
#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 {
//用來(lái)形成鏈表
struct list_head entry;
//定始器到達(dá)時(shí)間
unsigned long expires;
spinlock_t lock;
unsigned long magic;
//定時(shí)器時(shí)間到達(dá)后,所要運(yùn)行的函數(shù)
void (*function)(unsigned long);
//定時(shí)器函數(shù)對(duì)應(yīng)的參數(shù)
unsigned long data;
//掛載這個(gè)定時(shí)器的tvec_t_base_s.這個(gè)結(jié)構(gòu)我們等會(huì)會(huì)看到
struct tvec_t_base_s *base; 
};
從上面的過(guò)程中我們可以看到TIMER_INITIALIZER()只是根據(jù)傳入的參數(shù)初始化了struct timer_list結(jié)構(gòu).并把magic 成員初始化成TIMER_MAGIC
2): mod_timer():修改定時(shí)器的到時(shí)時(shí)間
int mod_timer(struct timer_list *timer, unsigned long expires)
{
//如果該定時(shí)器沒(méi)有定義fuction
BUG_ON(!timer->function);
//判斷timer的magic是否為T(mén)IMER_MAGIC.如果不是,則將其修正為T(mén)IMER_MAGIC
check_timer(timer);
//如果要調(diào)整的時(shí)間就是定時(shí)器的定時(shí)時(shí)間而且已經(jīng)被激活,則直接返回
if (timer->expires == expires && timer_pending(timer))
return 1;
//調(diào)用_mod_timer().呆會(huì)再給出分析
return __mod_timer(timer, expires);
}
3): add_timer()用來(lái)將定時(shí)器掛載到定時(shí)軟中斷隊(duì)列,激活該定時(shí)器
static inline void add_timer(struct timer_list * timer)
{
__mod_timer(timer, timer->expires);
}
可以看到mod_timer與add_timer 最后都會(huì)調(diào)用__mod_timer().為了分析這個(gè)函數(shù),我們先來(lái)了解一下定時(shí)系統(tǒng)相關(guān)的數(shù)據(jù)結(jié)構(gòu).
tvec_bases: per cpu變量,它的定義如下:
static DEFINE_PER_CPU(tvec_base_t, tvec_bases) = { SPIN_LOCK_UNLOCKED };
由此可以看到tves_bases的數(shù)型數(shù)據(jù)為teves_base_t.數(shù)據(jù)結(jié)構(gòu)的定義如下:
typedef struct tvec_t_base_s tvec_base_t;
struct tvec_t_base_s的定義:
struct tvec_t_base_s {
spinlock_t lock;
//上一次運(yùn)行計(jì)時(shí)器的jiffies 值
unsigned long timer_jiffies;
struct timer_list *running_timer;
//tv1 tv2 tv3 tv4 tv5是五個(gè)鏈表數(shù)組
tvec_root_t tv1;
tvec_t tv2;
tvec_t tv3;
tvec_t tv4;
tvec_t tv5;
} ____cacheline_aligned_in_smp;
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];
} tvec_t;
typedef struct tvec_root_s {
struct list_head vec[TVR_SIZE];
} tvec_root_t;
系統(tǒng)規(guī)定定時(shí)器最大超時(shí)時(shí)間間隔為0xFFFFFFFF.即為一個(gè)32位數(shù).即使在64位系統(tǒng)上.如果超過(guò)此值也會(huì)將其強(qiáng)制設(shè)這oxFFFFFFFF(這在后面的代碼分析中可以看到).內(nèi)核最關(guān)心的就是間隔在0~255個(gè)HZ之間的定時(shí)器.次重要的是間隔在255~1<<(8+6)之間的定時(shí)器.第三重要的是間隔在1<<(8+6) ~ 1<<(8+6+6)之間的定器.依次往下推.也就是把32位的定時(shí)間隔為份了五個(gè)部份.1個(gè)8位.4個(gè)6位.所以?xún)?nèi)核定義了五個(gè)鏈表數(shù)組.第一個(gè)鏈表數(shù)組大小為8位大小,也即上面定義的 #define TVR_SIZE (1 << TVR_BITS).其它的四個(gè)數(shù)組大小為6位大小.即上面定義的#define TVN_SIZE (1 << TVN_BITS)
在加入定時(shí)器的時(shí)候,按照時(shí)間間隔把定時(shí)器加入到相應(yīng)的數(shù)組即可.了解這點(diǎn)之后,就可以來(lái)看__mod_timer()的代碼了:
//修改timer或者新增一個(gè)timer都會(huì)調(diào)用此接口
int __mod_timer(struct timer_list *timer, unsigned long expires)
{
tvec_base_t *old_base, *new_base;
unsigned long flags;
int ret = 0;
//入口參數(shù)檢測(cè)
BUG_ON(!timer->function);
check_timer(timer);
spin_lock_irqsave(&timer->lock, flags);
//取得當(dāng)前CPU對(duì)應(yīng)的tvec_bases
new_base = &__get_cpu_var(tvec_bases);
repeat:
//該定時(shí)器所在的tvec_bases.對(duì)于新增的timer.它的base字段為NULL
old_base = timer->base;
/*
* Prevent deadlocks via ordering by old_base < new_base.
*/
//在把timer從當(dāng)前tvec_bases摘下來(lái)之前,要充分考慮好競(jìng)爭(zhēng)的情況
if (old_base && (new_base != old_base)) {
//按次序獲得鎖
if (old_base < new_base) {
spin_lock(&new_base->lock);
spin_lock(&old_base->lock);
} else {
spin_lock(&old_base->lock);
spin_lock(&new_base->lock);
}
/*
* The timer base might have been cancelled while we were
* trying to take the lock(s):
*/
//如果timer->base != old_base.那就是說(shuō)在Lock的時(shí)候.其它CPU更改它的值
//那就解鎖.重新判斷
if (timer->base != old_base) {
spin_unlock(&new_base->lock);
spin_unlock(&old_base->lock);
goto repeat;
}
} else {
//old_base == NULl 或者是 new_base==old_base的情況
//獲得鎖
spin_lock(&new_base->lock);
//同理,在Lock的時(shí)候timer會(huì)生了改變
if (timer->base != old_base) {
spin_unlock(&new_base->lock);
goto repeat;
}
}
/*
* Delete the previous timeout (if there was any), and install
* the new one:
*/
//將其從其它的tvec_bases上刪除.注意運(yùn)行到這里的話(huà),說(shuō)話(huà)已經(jīng)被Lock了
if (old_base) {
list_del(&timer->entry);
ret = 1;
}
//修改它的定時(shí)器到達(dá)時(shí)間
timer->expires = expires;
//將其添加到new_base中
internal_add_timer(new_base, timer);
//修改base字段
timer->base = new_base;
//操作完了,解鎖
if (old_base && (new_base != old_base))
spin_unlock(&old_base->lock);
spin_unlock(&new_base->lock);
spin_unlock_irqrestore(&timer->lock, flags);
return ret;
}
internal_add_timer()的代碼如下:
static void internal_add_timer(tvec_base_t *base, struct timer_list *timer)
{
//定時(shí)器到達(dá)的時(shí)間
unsigned long expires = timer->expires;
//計(jì)算時(shí)間間間隔
unsigned long idx = expires - base->timer_jiffies;
struct list_head *vec;
//根據(jù)時(shí)間間隔,將timer放入相應(yīng)數(shù)組的相應(yīng)位置
if (idx < TVR_SIZE) {
int i = expires & TVR_MASK;
vec = base->tv1.vec + i;
} else if (idx < 1 << (TVR_BITS + TVN_BITS)) {
int i = (expires >> TVR_BITS) & TVN_MASK;
vec = base->tv2.vec + i;
} else if (idx < 1 << (TVR_BITS + 2 * TVN_BITS)) {
int i = (expires >> (TVR_BITS + TVN_BITS)) & TVN_MASK;
vec = base->tv3.vec + i;
} else if (idx < 1 << (TVR_BITS + 3 * TVN_BITS)) {
int i = (expires >> (TVR_BITS + 2 * TVN_BITS)) & TVN_MASK;
vec = base->tv4.vec + i;
} 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);
} else {
int i;
/* If the timeout is larger than 0xffffffff on 64-bit
* architectures then we use the maximum timeout:
*/
//時(shí)間間隔超長(zhǎng),將其設(shè)為oxFFFFFFFF
if (idx > 0xffffffffUL) {
idx = 0xffffffffUL;
expires = idx + base->timer_jiffies;
}
i = (expires >> (TVR_BITS + 3 * TVN_BITS)) & TVN_MASK;
vec = base->tv5.vec + i;
}
/*
* Timers are FIFO:
*/
//加入到鏈表末尾
list_add_tail(&timer->entry, vec);
}
計(jì)算時(shí)間間隔即可知道要加入到哪一個(gè)數(shù)組.哪又怎么計(jì)算加入到該數(shù)組的那一項(xiàng)呢
對(duì)于間隔時(shí)間在0~255的定時(shí)器: 它的計(jì)算方式是將定時(shí)器到達(dá)時(shí)間的低八位與低八位為1的數(shù)相與而成
對(duì)于第1個(gè)六位,它是先將到達(dá)時(shí)間右移8位.然后與低六位全為1的數(shù)相與而成
對(duì)于第2個(gè)六位, 它是先將到達(dá)時(shí)間右移8+6位.然后與低六位全為1的數(shù)相與而成
依次為下推…
在后面結(jié)合超時(shí)時(shí)間到達(dá)的情況再來(lái)分析相關(guān)部份
4):定時(shí)器更新
每過(guò)一個(gè)HZ,就會(huì)檢查當(dāng)前是否有定時(shí)器的定時(shí)器時(shí)間到達(dá).如果有,運(yùn)行它所注冊(cè)的函數(shù),再將其刪除.為了分析這一過(guò)程,我們先從定時(shí)器系統(tǒng)的初始化看起.
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);
//注冊(cè)TIMER_SOFTIRQ軟中斷
open_softirq(TIMER_SOFTIRQ, run_timer_softirq, NULL);
}
timer_cpu_notify()àinit_timers_cpu():
代碼如下:
static void /* __devinit */ init_timers_cpu(int cpu)
{
int j;
tvec_base_t *base;
//初始化各個(gè)數(shù)組中的鏈表 
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);
//將最近到達(dá)時(shí)間設(shè)為當(dāng)前jiffies
base->timer_jiffies = jiffies;
}
我們?cè)谇懊娣治鲞^(guò),每當(dāng)時(shí)鐘當(dāng)斷函數(shù)到來(lái)的時(shí)候,就會(huì)打開(kāi)定時(shí)器的軟中斷.運(yùn)行其軟中斷函數(shù).run_timer_softirq()
代碼如下:
static void run_timer_softirq(struct softirq_action *h)
{
//取得當(dāng)于CPU的tvec_base_t結(jié)構(gòu)
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 *timer;
unsigned long flags;
spin_lock_irqsave(&base->lock, flags);
//因?yàn)镃PU可能關(guān)閉中斷,引起時(shí)鐘中斷信號(hào)丟失.可能jiffies要大base->timer_jiffies 好幾個(gè)
//HZ
while (time_after_eq(jiffies, base->timer_jiffies)) {
//定義并初始化一個(gè)鏈表
struct list_head work_list = LIST_HEAD_INIT(work_list);
struct list_head *head = &work_list;
int index = base->timer_jiffies & TVR_MASK;
/*
* Cascade timers:
*/
//當(dāng)index == 0時(shí),說(shuō)明已經(jīng)循環(huán)了一個(gè)周期
//則將tv2填充tv1.如果tv2為空,則用tv3填充tv2.依次類(lèi)推......
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_jiffies;
//將base->tv1.vec項(xiàng)移至work_list.并將base->tv1.vec置空
list_splice_init(base->tv1.vec + index, &work_list);
repeat:
//work_List中的定時(shí)器是已經(jīng)到時(shí)的定時(shí)器
if (!list_empty(head)) {
void (*fn)(unsigned long);
unsigned long data;
//遍歷鏈表中的每一項(xiàng).運(yùn)行它所對(duì)應(yīng)的函數(shù),并將定時(shí)器從鏈表上脫落
timer = list_entry(head->next,struct timer_list,entry);
fn = timer->function;
data = timer->data;
list_del(&timer->entry);
set_running_timer(base, timer);
smp_wmb();
timer->base = NULL;
spin_unlock_irqrestore(&base->lock, flags);
fn(data);
spin_lock_irq(&base->lock);
goto repeat;
}
}
set_running_timer(base, NULL);
spin_unlock_irqrestore(&base->lock, flags);
}
如果base->timer_jiffies低八位為零.說(shuō)明它向第九位有進(jìn)位.所以把第九位到十五位對(duì)應(yīng)的定時(shí)器搬到前八位對(duì)應(yīng)的數(shù)組.如果第九位到十五位為空的話(huà).就到它的上個(gè)六位去搬數(shù)據(jù).上面的代碼也說(shuō)明.要經(jīng)過(guò)1<<8個(gè)HZ才會(huì)更新全部數(shù)組中的定時(shí)器.這樣做的效率是很高的.
分析下里面的兩個(gè)重要的子函數(shù):
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, *curr;
//取數(shù)組中序號(hào)對(duì)應(yīng)的鏈表
head = tv->vec + index;
curr = head->next;
/*
* We are removing _all_ timers from the list, so we don't  have to
* detach them individually, just clear the list afterwards.
*/
//遍歷這個(gè)鏈表,將定時(shí)器重新插入到base中
while (curr != head) {
struct timer_list *tmp;
tmp = list_entry(curr, struct timer_list, entry);
BUG_ON(tmp->base != base);
curr = curr->next;
internal_add_timer(base, tmp);
}
//將鏈表設(shè)為初始化狀態(tài)
INIT_LIST_HEAD(head);
return index;
}
//將list中的數(shù)據(jù)放入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中的數(shù)據(jù)放入head
static inline void __list_splice(struct list_head *list,
struct list_head *head)
{
//list的第一個(gè)元素
struct list_head *first = list->next;
//list的最后一個(gè)元素
struct list_head *last = list->prev;
//head的第一個(gè)元素
struct list_head *at = head->next;
將first對(duì)應(yīng)的鏈表鏈接至head
first->prev = head;
head->next = first;
//將head 原有的數(shù)據(jù)加入到鏈表末尾
last->next = at;
at->prev = last;
}
5):del_timer()刪除定時(shí)器
//刪除一個(gè)timer
int del_timer(struct timer_list *timer)
{
unsigned long flags;
tvec_base_t *base;
check_timer(timer);
repeat:
base = timer->base;
//該定時(shí)器沒(méi)有被激活
if (!base)
return 0;
//加鎖
spin_lock_irqsave(&base->lock, flags);
//如果在加鎖的過(guò)程中,有其它操作改變了timer
if (base != timer->base) {
spin_unlock_irqrestore(&base->lock, flags);
goto repeat;
}
//將timer從鏈表中刪除
list_del(&timer->entry);
timer->base = NULL;
spin_unlock_irqrestore(&base->lock, flags);
return 1;
}
6): del_timer_sync()有競(jìng)爭(zhēng)情況下的定時(shí)器刪除
在SMP系統(tǒng)中,可能要?jiǎng)h除的定時(shí)器正在某一個(gè)CPU上運(yùn)行.為了防止這種在情況.在刪除定時(shí)器的時(shí)候,應(yīng)該優(yōu)先使用del_timer_synsc().它會(huì)一直等待所有CPU上的定時(shí)器執(zhí)行完成.
int del_timer_sync(struct timer_list *timer)
{
tvec_base_t *base;
int i, ret = 0;
check_timer(timer);
del_again:
//刪除些定時(shí)器
ret += del_timer(timer);
//遍歷CPU
for_each_online_cpu(i) {
base = &per_cpu(tvec_bases, i);
//如果此CPU正在運(yùn)行這個(gè)timer
if (base->running_timer == timer) {
//一直等待,直到這個(gè)CPU執(zhí)行完
while (base->running_timer == timer) {
cpu_relax();
preempt_check_resched();
}
break;
}
}
smp_rmb();
//如果這個(gè)timer又被調(diào)用.再刪除
if (timer_pending(timer))
goto del_again;
return ret;
}
定時(shí)器部份到這里就介紹完了.為了管理定時(shí)器.內(nèi)核用了一個(gè)很巧妙的數(shù)據(jù)結(jié)構(gòu).值得好好的體會(huì).
五:小結(jié)
2.6內(nèi)核在時(shí)鐘管理子系統(tǒng)的修改比較大.因?yàn)樵?.6完全摒棄掉了下半部機(jī)制.2.4中下半部處理的大部份都放在了中斷處理程序里,只有定時(shí)器控制被移到了時(shí)鐘軟中斷.另外時(shí)鐘中斷初始化涉及到了很多硬件的操作.需要查閱相關(guān)資料才能完全理解.




本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶(hù)發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
Linux內(nèi)核系統(tǒng)定時(shí)器TIMER實(shí)現(xiàn)過(guò)程分析
struct--timer
定時(shí)器
Linux時(shí)間子系統(tǒng)之五:低分辨率定時(shí)器的原理和實(shí)現(xiàn)
linux內(nèi)核定時(shí)器
Linux內(nèi)核開(kāi)發(fā)之中斷與時(shí)鐘
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服