時鐘中斷與進程調(diào)度課程設計報告_第1頁
時鐘中斷與進程調(diào)度課程設計報告_第2頁
時鐘中斷與進程調(diào)度課程設計報告_第3頁
時鐘中斷與進程調(diào)度課程設計報告_第4頁
時鐘中斷與進程調(diào)度課程設計報告_第5頁
已閱讀5頁,還剩32頁未讀 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領

文檔簡介

系統(tǒng)軟件課程計時鐘中斷與進調(diào)度

一、報告摘要進程調(diào)度是操作系統(tǒng)十分重要的一個部分操作系統(tǒng)的設計過程中程度和時鐘中斷形成了密不可分的關系。系統(tǒng)時鐘時的實現(xiàn)和進程調(diào)度,共同合作,實現(xiàn)了操作系統(tǒng)的有關功能。二、關鍵詞操作系統(tǒng)、系統(tǒng)時鐘、時鐘中斷、定時器、任務隊列、進程調(diào)度三、引言進程調(diào)度是CPU處理多進程并發(fā)執(zhí)行所必須的步驟,而和進程度緊密相關的是時間中斷涉到系統(tǒng)時鐘的初化和定時器的相關知識行過程中分方式,每人負責一部分內(nèi)容在每人閱代碼的過程中存在相互調(diào)用的關系則在一起共同完成代碼閱讀。分工如下:系統(tǒng)時鐘()許秀定時器(timer.c)胡進程調(diào)度()彭華四、任務分配與代分析4.1系4.2.1內(nèi)核機制原理分析時間在一個操作系統(tǒng)內(nèi)核中占據(jù)著重要的地位動一個S內(nèi)運行博器一般說來,內(nèi)核主要需要兩種類型的時間:在內(nèi)核運行期間持續(xù)記錄當前的時間與日期,以便內(nèi)核對些對象和事件作時間標記(timestamp,稱為“時間戳戶通過時間進檢索。維一個固定周期的定時器,以提醒內(nèi)核或用戶一段時間已經(jīng)過去了。機中的時間是有三種時鐘硬件提供的,而這些時鐘硬件又都基于固定率的晶體振蕩器來提供時鐘方波信號輸入三時鐘硬件是時可編程間隔定時器(Timer,間戳計數(shù)器Stamp,TSC時硬件.實時鐘自從IBMPC起,所有的PC機就都包含了一個叫做實時時鐘)時鐘芯片,以便在PC機斷電后仍然能夠繼續(xù)保持時間。顯然是過主板上的電池來供電的,而不是通過機源來供的當PC機關掉電源后仍然會繼續(xù)工作CMOSRAM和被成到一塊芯片上,因此RTC也作CMOS常見的RTC芯是

Motorola)和(完兼容于,有一定的擴展節(jié)內(nèi)容主要基于MC146818這標準的芯片體容可以參考MC146818的Datasheet。.可程間隔定時器每個PC機都有一個PIT,通過IRQ0產(chǎn)周期性的時鐘中斷信號。當前使用最普遍的是PIT芯,它的I/O端地址是。Intel8254PIT有計時通道,每個通道都有其不同的用途:(1通道0用負責更新系統(tǒng)時鐘。每當個時鐘滴答過去時,它就會通過IRQ0向統(tǒng)產(chǎn)生一次時鐘中斷。(2通道常用于控制對的新。(3通道連接到PC機的揚聲器,以產(chǎn)生方波信號。每個通道都有一個向下減小的計數(shù)器PIT的輸入時鐘信號的頻率是即一秒鐘輸入個入一個其間道的計數(shù)器就向下減,一直減到值。因此對于通道而,當他的計數(shù)器減到PIT就系統(tǒng)產(chǎn)生一次時鐘中斷,表示一個時鐘滴答已經(jīng)過去了。當各通道的計數(shù)器減到時我們就說該通道處于“count”狀態(tài)。通道計數(shù)器的最大值是,所對應的時鐘中斷頻率是1193181/65536)=18.2HZ也就是說,此時一秒鐘之內(nèi)將產(chǎn)生18.2次鐘中斷.時戳記數(shù)器從開,所有的80x86CPU就又包含一個位時間戳記數(shù)器)的寄存器。該寄存器實際上是一個不斷增加的計數(shù)器,它在CPU的個時鐘信號來時加1(也即每一個clock-cycle輸入CPU時,該計數(shù)器的值就加1匯編指令可用于讀取的值。利用,操作系統(tǒng)通??梢缘玫礁鼮榫珳实臅r間度量。假如的率是400MHZ,那么TSC就每2.5納增加一次。不同的操作系統(tǒng)和時的關系是不同的RTC和OS時之間的關系通常也被稱作操作系統(tǒng)的時鐘運作機制。一般來說,是OS時的時間基準,操系統(tǒng)通過讀取RTC來始化時鐘,此后二者保持同步運行,共同維持著系統(tǒng)時間。linux時機制Linux中的時鐘運作制如圖所示時鐘和間要通過BIOS的接,是因為傳統(tǒng)機固化有對RTC進有關操作的函數(shù)1AH中斷服務程序,通常操作系統(tǒng)也直接利用這些函數(shù)對進操作,例如從中出有關數(shù)據(jù)對時鐘初始化、對RTC進更新等等。實際上,不通過BIOS而直接對RTC的有關端口進行操作也是可以的。Linux中內(nèi)核初始化完成后就完全拋棄了BIOS中程序。

我們可以看到,處于最底層,提供最原始的時鐘數(shù)據(jù)時建立在RTC之,初始化完成后將完全由操作系統(tǒng)控制,和RTC脫關系。操作系統(tǒng)通過時鐘提供給應用程序所有和時間有關的服務為時完全是一個軟件問題其所能表達的時間由操作系統(tǒng)的設計者決定時鐘定義為整型還是長整型或者大的超乎想象都是設計者的事。Linux時基準以上我們了解了RTC(實時時鐘、硬件時鐘和OS時(系統(tǒng)時鐘、軟時鐘我們具體描述OS時鐘。我們知道時是由可編程定/數(shù)器產(chǎn)生的輸出脈沖觸發(fā)中斷而產(chǎn)生的輸出脈沖的周期叫做一個“時鐘滴答書上也把它叫做“時標中時間是以時鐘滴答為單位的,每一次時鐘滴答,系統(tǒng)時間就會加1。操作系統(tǒng)根據(jù)當前時鐘滴答的目就可以得到以秒或毫秒等為單位的其他時間格式。不同的操作系統(tǒng)采用不同的“時間基準“時間基準”的目的是為了簡化計算,這樣計算機中的時間只要表示為從這個時間基準開始的時鐘滴答數(shù)就可以了基準是由操作系統(tǒng)的設計者規(guī)定的。例如的間基準是1980年11日Unix和Minux的時間基準是年1月日上午12點,Linux的間基準是年1月日晨點。Linux的間系統(tǒng)通過上面的時鐘運作機制,我們知道了時鐘在中重要地位。OS時鐘記錄的時間也就是通常所說的系統(tǒng)時間統(tǒng)時間是“時鐘滴答”為單位的,而時鐘中斷的頻率決定了一個時鐘滴答的長短,例如每秒有100次鐘中斷,那么一個時鐘滴答的就是毫秒(記為10ms應地,系統(tǒng)時間就會每增1不同的操作系統(tǒng)對時鐘滴答的定義是不同的,例如DOS的鐘滴答滴答為,Minix時鐘滴答為等。時鐘中斷的產(chǎn)生

前面我們看到Linux的OS鐘的物理產(chǎn)生原因是可編程定/計數(shù)器產(chǎn)生的輸出脈沖,這個脈沖送入,就可以引發(fā)一個中斷請求信號,我們就把它叫做時中斷。“時鐘中斷是特別重要的一個斷因為整個操作系統(tǒng)的活動都受到它的激勵統(tǒng)利用時鐘中斷維持系統(tǒng)時間、促使環(huán)境的切換,以保證所有進程共享CPU利用時鐘中斷進行記帳、監(jiān)督系統(tǒng)工作以及確定未來的調(diào)度優(yōu)先級等工作??梢哉f鐘中斷”是整個操作系統(tǒng)的脈搏。時鐘中斷的物理產(chǎn)生如圖所示:操作系統(tǒng)對可編程定/計數(shù)器進行有關初始化,然后定/數(shù)器就對輸入脈沖進行計數(shù)(分頻生的三個輸出脈各用途,很多接口書都介紹這個問題,我們只看上的輸出脈沖,這個脈沖信號接到中斷控制器259A_1管腳,觸發(fā)一個周期性的中斷我們就把個中斷叫做時鐘中斷鐘中斷的周期也是脈沖信號的周期,我們叫做“滴答”或“時標本上說,時鐘中斷只是一個周期性的信號,完全是硬件行為,該信號觸發(fā)去行一個中斷服務程序4.2.2數(shù)據(jù)結(jié)構(gòu)與變量說明1、結(jié)構(gòu)體

Xtime在頭文件中structtimeval{tv_sec;/*seconds*/suseconds_ttv_usec;/**/};其中,成員tv_sec表當前時間距UNIX時基準的秒數(shù)值,成員表示一秒之內(nèi)的微秒值,且1000000>tv_usec>=0。Linux內(nèi)核通過結(jié)構(gòu)類型的局變量來維持當前時間,該變量定義在文中,如下所示:/*The*/volatilestructtimevalxtime__attribute__((aligned

struct時鐘中斷結(jié)構(gòu)體

2、宏定義:

HZ節(jié)拍率時鐘滴答的頻率HZ即秒間內(nèi)所生的時鐘滴答次數(shù)。在#中置2.5版前,時鐘中斷頻率為一次時鐘滴答的具體時間間隔應該是/HZ=10ms。

TICK_SIZEtick宏來為變的引用別名Linux用局變量tick表示時鐘滴答的時間間隔長度tick=+HZ2/其中被除數(shù)表達式中的HZ/作用就是用來將tick值上圓整成一個整型數(shù)。

CONFIG_X86_TSC表是否存在TSC寄器。CLOCK_TICK_RATE內(nèi)鐘周期個數(shù)定義要寫到通0的計數(shù)器中的值示PIT將隔多少個時鐘周期產(chǎn)生一時鐘中斷鎖:

xtime_lock相時間鎖,保證進程對xtime改的互斥性rtc_lock鎖,保證進程對操的互斥,初始未鎖i8253_lock計數(shù)器鎖初始未鎖i8259A_lock中鎖rtc_lock實時鐘RTC,用于保證操作RTC互3、主要變量delay_at_last_interruptlast_tsc_lowfast_gettimeoffset_quotientwall_jiffies

系統(tǒng)時鐘頻率中斷服務執(zhí)行延遲delay_at_last_interrupt:于從產(chǎn)生時鐘中斷的那個時刻到內(nèi)核時鐘斷服務函數(shù)timer_interrupt真在執(zhí)行的那個時刻之間是有一段延遲間隔的,因此,Linux內(nèi)用量delay_at_last_interrupt來表示這一段時間延遲間.表示中斷服務timer_interrupt真在CPU上行時刻的寄存器值的低32位(LSBTSC計值代表多長的時間隔表示自內(nèi)核上一次啟動以來的時鐘滴答次數(shù)一次時鐘滴答,內(nèi)核的時鐘中斷處理函數(shù)timer_interrupt()都要將該全局變量jiffies加。保存內(nèi)核上一次更新xtime時

last_rtc_update4.2.3函數(shù)組織流程及功能do_slow_gettimeoffsetdo_settimeofdayset_rtc_mmssdo_timer_interrupttimer_interruptcalibrate_tsctime_init

表示當前時間距UNIX時基準-0100:的對秒數(shù)值表示內(nèi)核最近一次成功地對RTC進更新的時間(單位是秒數(shù)時間中斷申請表示內(nèi)核是否使用CPU存器通過TSC寄器計算函數(shù)被執(zhí)行的時刻到上一次時鐘中斷發(fā)生時的時間間隔值在無TSC的況計算函數(shù)被執(zhí)行的時刻到上一次時鐘中斷發(fā)生時的時間間隔值完成實際的當前時間檢索工作,確地修正xtime的把的時間設定為系統(tǒng)時間向RTC中寫當前時間與日期,該函用來更新RTC中的時間中斷服務程序通用例程,執(zhí)行真正的中斷時鐘服務時鐘中斷程序內(nèi)核在啟動時從RTC中取動時的時間與日期僅僅在內(nèi)核啟動時被調(diào)用一次校準,獲取更新的的系統(tǒng)時鐘初始化4.2.3程序段功能描述與代碼注釋/**linux/arch/i386/kernel/time.c**Copyright(C)1992,**timehandling*RTCatbootup,etc..*Modra*fixedset_rtc_mmss,for>=mktime*Kuhn*fixed500msatcallDS12887*precisionCMOSupdate*Molnar*fixedindo_[slow|fast]_gettimeoffset()*1997-09-10UpdatedNTPcodeaccordingtomemorandumJan'96*"AforTimekeeping"Mills

**Morerobustimplemented*withAPM,Cyrix6x86MXandC6),*monotonicgettimeofday()withfast_get_timeoffset(),*drift-proofprecisionTSCcalibrationboot*(C.Ananian<cananian@>,Andrew*<andrebalsa@>,Philip<philip@>;*portedfromJumbo-9by**Fixedindo_gettimeofday1jiffy*becausewasnotaccountinglost_ticks.*(C)1998AndreaArcangeli*FixedaxtimeSMP(weneedthextime_lockspinlockto*serializeaccessesto*/<linux/errno.h><linux/param.h>節(jié)拍率值在這里設置2.5版前,時鐘中斷頻率為100Hz時鐘滴答的頻率HZ即時間內(nèi)所生的時鐘滴答次數(shù)。類似地,這個值也是由通0的數(shù)器初值決定的(反過來,確定了時鐘滴答的頻率值后也就可以確定PIT通0計數(shù)器初值內(nèi)核用宏HZ來示時鐘答的頻率且在不同的平臺上HZ有不同的定值。根據(jù)HZ的也可以知道一次時鐘滴答的具體時間間隔應該/10ms。<linux/string.h><linux/interrupt.h><linux/time.h><linux/delay.h><linux/init.h><asm/smp.h><asm/msr.h><asm/delay.h><asm/mpspec.h><asm/processor.h>

<linux/timex.h><linux/config.h><asm/fixmap.h><asm/cobalt.h>/**forx86_do_profile()*/<linux/irq.h>/*linux系統(tǒng)中,起作用的是軟件時鐘,即系時鐘*/unsignedlongcpu_khz;/*aswetheTSC*//*Numberofusecsthatthelastinterruptdelayed*/intdelay_at_last_interrupt;中斷服務執(zhí)行延遲:于從產(chǎn)生時鐘中斷的那個刻到內(nèi)核時鐘中斷服務函數(shù)真在CPU上行的那個時刻之間有一段延遲間隔,Linux內(nèi)用變量來示這一段時間延遲間unsignedlonglast_tsc_low;/*32bitsTimeCounter*/它表示中斷服務timer_interrupt正在上行時刻的寄器值的低32LSB顯然,通過delay_at_last_interrupt、和刻x處寄存器值,我們就可以完全確定時刻x上一次時鐘中斷產(chǎn)生時刻的時間間隔偏移offset_usec的值。/*Cachedconvertcountsmicroseconds.**Equalto2^32*(1/(clocks*Initializedintime_init.*/unsignedlong與可編程定時器相比TSC寄器可以獲得更精確的時間度量在可以使用TSC之前,它必須精確地確定個計值到底代表多長的時間間隔,也即到底要過多長時間間隔寄器會加1。Linux內(nèi)用全局變量來示這個值這個變量的值是通過下述公式來計算的:fast_gettimeoffset_quotient=(每微秒內(nèi)的時鐘周期個)定義在arch/i386/kernel/time.c文件中的數(shù)calibrate_tsc()就是根據(jù)上述公式來計算fast_gettimeoffset_quotient的值的。顯然這個計算過程必須在內(nèi)核啟動時完成,因此,函數(shù)只初始化函數(shù)所調(diào)用。externrwlock_t//相對時間,保證進程對xtime改的互斥性externlongwall_jiffies;表系統(tǒng)自啟動以來的時鐘滴答數(shù)目。

由于Half機在執(zhí)行時間具有某些不確定性,因此在timer_bh()函(更新執(zhí)行定時器得到真正執(zhí)行之前間可能又會有幾次時鐘中斷發(fā)生這樣就會造成時鐘滴答的丟失現(xiàn)象。為了處理這種情況Linux內(nèi)使用了個輔助全局變量wall_jiffies,表示上一次更新時值而timer_bh()數(shù)真正執(zhí)行時的值wall_jiffies的就是在真正執(zhí)行之前所發(fā)生的時鐘中斷次數(shù)。鎖保證進程對操的互斥,初始未鎖========================================================計算該函數(shù)執(zhí)行的時刻到上一次時鐘中斷發(fā)生時的時間間隔值inlinelongdo_fast_gettimeoffset(void){longeax,edx;/*ReadTimeStampCounter*/rdtsc(eax,edx);先調(diào)用函讀當前時刻TSC寄存器的值,并將其高32位存在局變量中,低位保存在局部變量eax中/*..topreviousjiffyis*/-=/*tsc_lowdelta*/讓局部變量eax=Δ=-;也即計算當前時刻的TSC值上一次時鐘中斷服務函數(shù)timer_interrupt()行時的TSC值間的差。/**Time=*****(usecs_per_jiffy/**Usingmulldivlsavesupto31*inthecritical*/顯然,從上一次到當前時刻的時間間隔就是(Δ*此用一條mul指來計算這個乘法表達式的值。%2"(edx)(fast_gettimeoffset_quotient),"0"(eax));/*adjustedoffsetin*/+edx;

返回值+(*fast_gettimeoffset_quotient)就是從上一次時鐘中斷發(fā)生時到當前時刻之間的時間偏移間隔值。}============end=========================================TICK_SIZEtick宏來為變的引用別名Linux用局變量tick來示鐘滴答的時間間隔長度tick變的單位是微妙μ于在不同平臺上宏HZ的值會有所不同,因此方程式tick=1000000÷HZ的結(jié)果可能會是個小數(shù),因此將其進行四舍五入成一個整數(shù),所以Linux將tick定(1000000+HZ2/其中被除數(shù)表達式中的HZ的作用就是用來將tick值上圓整成一整型數(shù)。i8253_lock計數(shù)器鎖,初始未鎖(1通道0用負責更新系統(tǒng)時鐘。每當個時鐘滴答過去時,它就會通過IRQ0向統(tǒng)產(chǎn)生一次時鐘中斷。(2通道常用于控制對的新。(3通道連接到PC機的揚聲器,以產(chǎn)生方波信號。externspinlock_t

中斷鎖CONFIG_X86_TSC宏CONFIG_X86_TSC表示是否存在TSC寄存器。/*withinterrupts*ItbySteveMcCanne'sfor**However,thepc-audiodriverdivisorthat*itgetsmoreoften-itloadsinto*ratherthanhas*do_gettimeoffset()itstopsWhatis*intervalthattimergetscalled*isms,9.9767To*wouldrequireatimingsomeone*couldusetheIknowthatinterruptfrequencies*fromto2Hz.IfIhadI'dsomehowfix*itsothatstartup,thetimercodein*usingeithertheorthetimer.*basedwhetherwasotherdevicearound*trampleontheI'dsetuptheRTCinterruptHz,*andthendosometoversionofthat*advancedclockbys.thatover*second,thendoalltheoldIfwascorrect*do_gettimeoffsetcouldjusttherenoorder

*dividerthataccessed.**Ideally,youwouldbeusetheforspeakerdriver,*itappearsthatthedriverreally*everyso.**needs(1993-08-28)**Ifyouarereallythatinterested,shouldbe*tocols.time.ntp!*/=========================do_slow_gettimeoffset==================================unsignedlong{intcount;int/*forfirstcallafterboot*/LATCH為數(shù)初值unsignedlongjiffies_p=0;/**cachevolatiletemporarily;weoff.*/unsignedlongjiffies_t;/*withirqlocallydisabled*/spin_lock(&i8253_lock);/*timerunderflowright*/outb_p(0x00,0x43);/*latchtheASAP*/把制字寫到控端口,用于鎖存count/*read*/計數(shù)器的值寫入/**Weguaranteedofa_p*postfixinaccess.hack*/jiffies_t=countinb_p(0x40)<</*code...resetlatchif>1*/if(count>{outb_p(0x34,0x43);將制字寫入控制端口

outb_p(LATCH&outb(LATCH>>8,count-}/**avoidinginconsistencies(theyarebutthey*therearetwoofthatbehere:*1.thetimercounterunderflows*2.hardwareproblemthetimer,givingcontinuoustime,*thecounterdoes"jumps"upwardssystems,*(seec't95/10forNeptunbug.)*//*youcanundefineifdon'tchipset*/BUGGY_NEPTUN_TIMERif(jiffies_t==jiffies_p{if(countcount_p{/*the*/inti;/**trickyI/O**/i=inb(0x20);spin_unlock(&i8259A_lock);/*timerbeingIRQ0*/if(i&{/**Weinterrupts*well,that'swhycallthemlost,:)*[hmm,Alphawe...sortof]*/count}{#ifdef

/**fortheNeptunknowthatthe'latch'*highandvalue*counteratomically.haveto*thecounter*...funny,:)*/counthardwareproblem?\n");}}}=jiffies_t;count;count((LATCH-1)-*TICK_SIZE;count+LATCH/2)/}do_slow_gettimeoffset==============================unsignedlong=do_slow_gettimeoffset;============精確修正時間=============================完成實際的當前時間檢索工作。由于gettimeofday()統(tǒng)調(diào)用要求時間精度要達到微秒級,因此do_gettimeofday()數(shù)不能簡單地返回xtime中值可而必須確地確定自從時鐘驅(qū)動的上一次更新xtime的個時刻到函的當前執(zhí)行時刻之間的具體時間間隔長以便精確地修正xtime的/**versiongettimeofdaymicrosecondresolution*andthanprecisiononx86machines*/

voiddo_gettimeofday(struct*tv){unsignedlongunsignedlongsec;read_lock_irqsave(&xtime_lock,flags);=do_gettimeoffset();調(diào)用函數(shù)計從上一次時鐘中斷發(fā)生到執(zhí)行函的當前時刻之間的時間間隔offset_usec{unsignedlonglost=-wall_jiffies;if+=lost*/1000000/HZ-----tick時滴答時間間隔長度}通過wall_jiffies和計丟失的時鐘間隔lost_usec值。sec+=xtime.tv_usec;當前實際時間(墻上時間)定義在文件中structtimespec數(shù)結(jié)構(gòu)定義在文<linux/time.h>,形式如下:structtimespec{time_ttv_sec;/*秒*/以秒為單位,存放著自1970年以來經(jīng)過的時間/*納秒*/記錄了自上一秒開始經(jīng)過的納數(shù)}read_unlock_irqrestore(&xtime_lock,flags);然后令sec=xtime.tv_secusec=xtime.tv_usec+lost_usec+offset_usec顯然sec表系統(tǒng)當前時間在秒數(shù)量級上的值,而usec表系統(tǒng)當前時間在微秒量級上的值。>={-=1000000;sec++;}用一個while{}循環(huán)來判斷否已經(jīng)溢出而超過1000000us1秒果溢出將減去并應地將增1直到不溢出為止。=tv->tv_usec=usec;最后,用和usec分更新參指針所指向的timeval構(gòu)變量。至此,整個查詢過程結(jié)束。}====================end========================================================

統(tǒng)

===do_settimeofday====

==========================把tv的間設定為系統(tǒng)時間void*tv){write_lock_irq(&xtime_lock);/**Weneed"xtime"correctly.However,the*valueinthisisvalueof*walltime.Discoverwhatcorrectiongettimeofday()wouldhave*thenit!*/tv->tv_usec-=調(diào)用do_gettimeoffset()函計算上一次時鐘中斷發(fā)生時刻到當前時刻之間的時間間隔值。tv->tv_usec-=wall_jiffies)*/HZ);通過wall_jiffies與計二者之間的時間間隔lost_usec從tv->tv_usec中減去fixed_usec即=(+<0){tv->tv_usec+=}用一個while{}循根據(jù)tv->tv_usec是小于0調(diào)整結(jié)變量小,則將加106us并相應地將tv->tv_sec。直到tv->tv_usec不于止。*tv;用修正后的時間tv來新內(nèi)核全局時間變量xtimetime_adjust=0;/*stopactiveadjtime()*/STA_UNSYNC;time_maxerror=NTP_PHASE_LIMIT;NTP_PHASE_LIMIT;write_unlock_irq(&xtime_lock);最后,重置其它時間狀態(tài)變量。}do_settimeofday====================================================更新RTC時間==========================/**ordertheclockprecisely,set_rtc_mmsshasto*msaftersecondnowtimestarted,because*nowtimewrittenintoregistersofclock,itwill*preciselymslater.CheckMotorola*MC146818AorDS12887for**Thisdoeshouroverflowit

===

*setsyou'llthatreboot!*/intset_rtc_mmss(unsignedlongnowtime)向RTC中寫當前時間與日期該函數(shù)用來更新RTC中的時間,它僅有一個參數(shù)nowtime,是以秒數(shù)表示的當前時間{int=intunsignedcharsave_freq_select;/*withirqlocallydisabled*/spin_lock(&rtc_lock);首先對自旋鎖進加鎖在文中的全局自旋鎖rtc_lock用來串行化所有CPU對RTC的作。save_control=CMOS_READ(RTC_CONTROL);/*telltheit'sbeingset*/接下來RTC制寄存器中設置標位便通知RTC軟程序隨馬上將要更新它的時間與日期。為1表示RTC的所有更新過程都將終止,用戶程隨后馬上對日歷寄存器組中的值進行初始化設置。為表示將允許更新過程繼續(xù)。為此先把RTC_CONTROL寄存的前讀到變量save_control中,然再把值(save_control)寫到寄存器RTC_CONTROL中寄存器的各位用于使能/禁止的各種特性此控制寄存器(稱控制寄存器Linux用別名RTC_CONTROL來示控制寄存器save_freq_select/*stopandprescaler*/RTC_FREQ_SELECT);然后通寄器中bit4重啟RTC芯內(nèi)部的除法器為,類似地先把RTC_FREQ_SELECT寄器的當前值讀到變量中再把值(|)寫到寄存器中cmos_minutes=if&RTC_DM_BINARY)||RTC_ALWAYS_BCD)接著將寄器的當前值讀到變量cmos_minutes根需要將它從格式轉(zhuǎn)化為二進制格式。/**onlyminutesand*withhour*messingwithunknownbutyour*notbeoffmoreminutes*/real_seconds=nowtime%=/從nowtime參中得到當前時間秒數(shù)和分鐘數(shù)。分別保存到real_seconds和

變量。注意,這里對于半小時區(qū)的情況要修正分鐘數(shù)real_minutes的。if(((abs(real_minutes-cmos_minutes)15)/30)&1)+=30;/*forhalfhourtimezone*/if-<30){if&RTC_DM_BINARY)||RTC_ALWAYS_BCD){BIN_TO_BCD(real_minutes);}CMOS_WRITE(real_seconds,RTC_SECONDS);CMOS_WRITE(real_minutes,RTC_MINUTES);}{printk(KERN_WARNING%dtoreal_minutes);retval-1;}然后在real_minutes與RTC_MINUTES存器的原值二相差不超過分鐘的情況下和real_minutes所示的時間值寫到RTC的寄存器分鐘寄存器中。當然,在回寫之前要記得把二進制轉(zhuǎn)換為BCD格。/*Thefollowinghavereleasedinorder,*DS12887clonewithintegrated*batteryandquartz)willnottheoscillatorwillnot*updateprecisely500Youwon'tfindthismentionedin*Semiconductorwhodata*anyway--Markus*/CMOS_WRITE(save_control,RTC_CONTROL);CMOS_WRITE(save_freq_select,RTC_FREQ_SELECT);spin_unlock(&rtc_lock);}最后,恢復RTC_CONTROL寄器和寄器原來的值。這者的先后次序是:先恢復RTC_CONTROL寄器,再恢復RTC_FREQ_SELECT寄存器然后在解除自旋鎖rtc_lock后可以返回了。最后,需要說明的一點是,函盡可能在靠近一秒時間間隔的中間位置(也即處左右被調(diào)用。此外,Linux核對每一次成功的更新RTC時都留下時間軌跡一個系統(tǒng)全局變量來示內(nèi)核最近一次成功地對RTC進更新的時間(單位是秒數(shù)變定義在arch/i386/kernel/time.c件中:/*theclockgot*/longlast_rtc_update;每一次成功地調(diào)用函數(shù)后,內(nèi)核都會馬上將last_rtc_update更為當前時間

set_rtc_mmss===============================/*theclockgot*/longlast_rtc_update;表示內(nèi)核最近一次成功地對進更新的時間(單位是秒數(shù)每一次成功地調(diào)用函數(shù)后,內(nèi)核都會馬上將last_rtc_update更為當前時間inttimer_ack;

時間中斷申請===============中斷服務程序通用歷程do_timer_interrupt======================中斷服務程序通用例程,執(zhí)行真正的中斷時鐘服務/**t標oupthereal-timeclock,*wellascallthe"do_timer()"clocktick*/inlinevoidirq,voidpt_regs{#ifdefif{/**I/OareusedwetimerIRQ*manuallyIRRbit*willalsodeassertNMIlinesfortheifrun*onansystem.*/0x20);/*AckIRQ;AEOIwillendit*/inb(0x20);spin_unlock(&i8259A_lock);}若有時鐘中斷請求,響應后,鎖上,往控制端寫控制字,然后解鎖。#ifdef/*Clear*/co_cpu_write(CO_CPU_STAT,co_cpu_read(CO_CPU_STAT)&~CO_STAT_TIMEINTR);修改時間中斷標志位調(diào)用do_timer()時鐘函數(shù)。該函數(shù)的核心是完成三個任務:(1將表示自系統(tǒng)啟動以來的時鐘滴答計數(shù)變量jiffies加。(2調(diào)用函更新當前進程的時間統(tǒng)計信息。注意,該函數(shù)的參數(shù)

原型intuser_tick次時鐘中即時鐘滴答生時正于用戶態(tài)下執(zhí)行,則user_tick參應該為;否則如果本次時鐘中斷發(fā)生時正處于核心態(tài)下執(zhí)行時,則user_tick數(shù)應改為。所以這里我們以宏來作為函數(shù)的調(diào)用參數(shù)。該宏定義在頭件中,它根據(jù)regs指針所指向的核心堆棧寄存器結(jié)構(gòu)來判斷進中斷服務之前是處于用戶態(tài)下還是處于核心態(tài)下。調(diào)用mark_bh()函數(shù)激活時鐘中斷的Half向TIMER_BH和TQUEUE_BH(注意,TQUEUE_BH僅任務隊列tq_timer不空的情況下才會被激活TQUEUE_BH的用是用來運行tq_timer這任務隊列中的任。因此函數(shù)僅僅在tq_timer任隊列不為空的情況才激活量。函數(shù)的實現(xiàn)非常簡單,它只是簡單地調(diào)用run_task_queue()函數(shù)來運行任務隊列tq_timer。TIMER_BH這BottomHalf量是Linux內(nèi)時鐘中斷驅(qū)動的一個重要輔助部分核在每一次對時鐘中斷的服務快要結(jié)束時,都會無條件地激活一個TIMER_BH向,以使得內(nèi)核在稍后一段延遲后執(zhí)行相應的BH數(shù)——timer_bh()/**caseweAPICtimer*simulatemode*inthathavetocallhandler.*/ifsmp_local_timer_interrupt(regs);/**IfweexternallyLinuxupdate*CMOSclockaccordinglybe*asastomsbeforenew*/判斷是否需要更新CMOS時(即RTC中的時間Linux僅在下列三個條件同時成立時才更新時:①系統(tǒng)全局時間狀態(tài)變量中有設置STA_UNSYNC標,也即說明Linux有一個外部同步時鐘。實際上全局時間狀態(tài)變量僅一種情況下會被清除STA_SYNC志,那就是執(zhí)行adjtimex()系統(tǒng)調(diào)用時(這個syscall與有關由RTC存UpdateCycle最在一秒時間間隔的中間位置500ms右調(diào)用函數(shù)來更新時。因此Linux規(guī)僅當全局變量的微秒數(shù)在500000±tick/2微秒范圍范圍之內(nèi)時,才調(diào)用函。如果述條件均成立,那就調(diào)用將當前時間更回寫到RTC中如果上面是的set_rtc_mmss()數(shù)返回值則表明更新成功。于是就將“最近一RTC新時間”變量last_rtc_update更為當前時間xtime.tv_sec。果返回非值說明更新失敗于就讓=(當于便在在秒之后再次對RTC進更新。

if((time_status&STA_UNSYNC)==&&>last_rtc_update660&&>=tick)/2&&<=+tick)/2){if==0)last_rtc_update=xtime.tv_sec;last_rtc_update=xtime.tv_sec-/*itagain60*/}#ifdefCONFIG_MCAif(MCA_bus){/*Theuseslevel-triggeredwantattemptenableedge-triggeredinterruptsusuallyinterceptedaspecialhardwarecircuit).havetothetimerThroughincrediblydesigntheresetforIRQdonebysettingthebitofportB(0x61).NotePS/2s,notably55SX,workfineifisremoved.*/irq=0x61);/*readthecurrent*/);/*IRQ*/}}do_timer_interrupt============================intuse_tsc;全局變量來表示內(nèi)核否使用的TSC寄器use_tsc表使用TSC=0表不使用TSC。該變量的值是在time_init()始化函數(shù)中被初始化的。從開,所有的80x86CPU就又包含一個位時間戳記數(shù)器)的寄存器。該寄存器實際上是一個不斷增加的計數(shù)器,它在CPU的個時鐘信號來時加1(也即每一個clock-cycle輸入CPU時,該計數(shù)器的值就加1匯編指令可用于讀取的值。利用,操作系統(tǒng)通??梢缘玫礁鼮榫珳实臅r間度量。假如的率是400MHZ,那么TSC就每2.5納增加一次。======================時鐘中斷程序==timer_interrupt===========================中斷服務描述符一旦被鉤掛到IRQ0的中斷服務隊列中去后Linux內(nèi)就可以通過irq0->handler數(shù)指針所指向的timer_interrupt()數(shù)對時鐘中斷請求進行真正的服務是向前面所說的那樣只是讓CPU空跑”一趟。此時Linux內(nèi)可以說是真的“跳動”起來了。

通常只有第一項(即)是最為迫切的,因此必須在時鐘中斷服務例程中完成。而其余的幾個要求可以稍緩因可以放在時鐘中斷的Half中執(zhí)這Linux內(nèi)核就是timer_interrupt()函數(shù)的執(zhí)行時間盡可能的短,因為它是在CPU關斷的條件下執(zhí)行的。/**sameas_also_*TimeStampvaluetimeofthat*estimatedaymore*/voidtimer_interrupt(intirq,*dev_id,pt_regs{intcount;/**inirqWehaveirqslocally*disableddon'tknowifthetimer_bhrunningother*WeneedavoidtoSMPwithit.NOTE:don'tneed*irqversionofsaidwehaveirq*locally-arca*/write_lock(&xtime_lock);由于函數(shù)執(zhí)行期間要訪問全局時間變量,因此一開就對自旋鎖xtime_lock行加鎖。if(use_tsc){如果內(nèi)核使用CPU的TSC存器變非0么過寄存器來計算從時間中斷的產(chǎn)生到timer_interrupt)函數(shù)真正在上行這之的時間延遲:/**Itthesetwoalmost*sametime.dotheRDTSCstuffsince*faster.avoidinconsistencies,needinterrupts*disabledlocally.*//**Interruptsarejustdisabledsinceirq*flag-arca*//*readPentiumcounter*/調(diào)用宏將64位TSC寄器值中的低32位)到量last_tsc_low,以供函計算時偏差之用一步的實質(zhì)就是將CPU寄器的值更新到內(nèi)核對TSC的存變量中

spin_lock(&i8253_lock);outb_p(0x00,0x43);/*latchcountASAP*/首先自旋鎖i8253_lock進加鎖旋i8253_lock的用就是用來串行化對PIT的讀寫訪問。其次,向254的控制寄存器(端口)中寫入值,以便對通道的計數(shù)器進行鎖存。count/**/count通過端口將道的計數(shù)器的當前值讀到局部變量中并鎖i8253_lock。count((LATCH-1)-*TICK_SIZE;delay_at_last_interrupt=(count+LATCH/2)/}顯然,從時間中斷的產(chǎn)生到函真正執(zhí)行這時間內(nèi),以一共流逝了)-count個時鐘周期,因此這個延時長度可以用如下公式計算:=(LATCH-1)-)÷)﹡TICK_SIZE顯然,上述公式的結(jié)果是個小數(shù),應對其進行四舍五入,為此Linux用述表達式來計算delay_at_last_interrupt變量的值:))*+LATCH/2)/上述被除數(shù)表達式中的LATCH/2就用來將結(jié)果上圓整成整數(shù)的。在計算出時間延遲后,最后調(diào)用函數(shù)do_timer_interrupt()行真正的時鐘服務。write_unlock(&xtime_lock);}timer_interrupt===============================================從RTC中讀取時間==============================在從RTC中讀取時間,由于RTC存Cycle,因此軟件發(fā)出讀操作的時機是很重要的。對此函數(shù)通過UIP標位來解決這個問題/*notstatic:byAPM*/unsignedlongget_cmos_time(void)內(nèi)核在啟動時從RTC中讀取啟動時的時間與日期該函數(shù)只被內(nèi)核的初始化例程和內(nèi)核的APM模塊所調(diào)用。內(nèi)核在啟動時從RTC中讀取啟動時的時間與日僅僅在內(nèi)核啟動時被調(diào)用一次{unsignedintmon,day,hour,min,inti;spin_lock(&rtc_lock);

自旋鎖將鎖,進入讀取時間臨界區(qū)/*TheLinuxinterpretationofclockregistercontents:*WhenUpdate-In-Progressflag0,the*registersshowsecondwhichpreciselystarted.

*hopeinterpretsame*/當Update-In-ProgressUIP標從1變0時寄存器顯示剛剛開始的秒數(shù)。UIP為1表示正更新日歷寄存器組中的值,此時日歷寄存器組是不可訪問的(此時訪問它們將得到一個無意義的漸變值/*readonfallingedgeofupdateflag*/控制寄存器A中UIP標志位用來表示MC146818否正處于更新周期中UIP從為1的個時刻,就表示MC146818將稍后馬上就開更新周期。在UIP從變到的那個時刻與真開始Update的那個時刻之間時有一段時間間隔的,通常是。也就是說,在UIP從0變1的244us之后,時間與日期寄存器組中的值才會正開始改變,而在這之間的間內(nèi),它們的值并不會真正改變。for(i=0;i<1000000;i++)/*mayupto1*/if&break;第一個for循不停地讀取頻選寄存器中的UIP標位,并且只要讀到的值為1就馬上退出這個循環(huán)。for(i=0;i<1000000;i++)/*must2.228*/if(!(CMOS_READ(RTC_FREQ_SELECT)&RTC_UIP))break;第二個for循同樣不停地讀取UIP志位他只要一讀到UIP值為就馬上退出這個for循。這兩個for循的目的就是要在軟件邏輯上同步RTC的,然第二個for環(huán)最大可能需要從第二個for循退出后Cycle已結(jié)束此我們就已經(jīng)把當前時間邏輯定準在RTC的當前一時間間隔內(nèi)。也就是說,這時我們就可以開始RTC寄器中讀取當前時間值。但是要注意,讀操作應該保證在244us內(nèi)完成(準確地說,讀操作要在的下一個更新周期開始之前完成的制是過分偏執(zhí)的,get_cmos_time()函數(shù)接下來通過宏中次讀取秒分鐘小時期月和年分。{/*Isn'tthisoverkill*/secCMOS_READ(RTC_SECONDS);min=hour=CMOS_READ(RTC_DAY_OF_MONTH);=CMOS_READ(RTC_MONTH);CMOS_READ(RTC_YEAR);}while(sec!=這里的do{}while循就是用來確保上述個操作必須在下一個Cycle開始之前完成。if&RTC_DM_BINARY)||RTC_ALWAYS_BCD){

BCD_TO_BIN(sec);BCD_TO_BIN(min);BCD_TO_BIN(day);}接下來判定時間的數(shù)據(jù)格式,PC機中一般總是使用格式的時間,因此需要通過BCD_TO_BIN()宏把格轉(zhuǎn)換為二進制格式。spin_unlock(&rtc_lock);對rtc_lock進解鎖if((year+=+=mktime(year,mon,min,}接下來對年分進行修正,以將年份轉(zhuǎn)換為19XX的格式,如果是1以的年份,則將其加上100。最后調(diào)用mktime()數(shù)將當前時間與日期轉(zhuǎn)換為相對于--01的秒數(shù)值,并將其作為函數(shù)返回值返回。函數(shù)定義在頭文件中,它用根據(jù)算法將以如1980-12-235959式示的時間轉(zhuǎn)換為相對于--01::00這個時基準以來的相對秒數(shù)。=======================end====get_cmos_time============================structirqaction={"timer",NULL};的始化/*------theTSC-------*2^32*/perfor*Toomucharithmetictoinfor*sakeweoverheadspeaker*outputlooplowavoidreadingtheCTC*ofawkward8-bitmechanismof82C54**/(5*LATCH)宏LATCHLinux用LATCH來義要寫到通0的數(shù)器中的值它示將沒隔多少個時鐘周期產(chǎn)生一次時鐘中斷。顯然LATCH應由下列公式計算:LATCH秒內(nèi)的時鐘周期個秒之內(nèi)的時鐘中斷次數(shù)CLOCK_TICK_RATE÷(HZ)類似地上公式的結(jié)果可能會是個小數(shù)該對其進行四舍五入所以Linux將

定義為(include/linux/timex.h/*LATCHisinintervaltimerandsetup.*/LATCHHZ/2)/HZ)/*Fordivider*/類似地,被除數(shù)表達式中的HZ2也用來將LATCH向圓整成一個整數(shù)。CALIBRATE_TIME(5*===============================calibrate_tsc====================================unsignedlong__init//校準,獲取更新的fast_gettimeoffset_quotient的{/*Gatehigh,disable*/outb((inb(0x61)&|0x01,//激活通道,關閉揚聲器/**Nowlet'scareofCTC2**Gatehigh,programCTCfor*(interruptterminalcountmode),binary**LATCHcount,(LSBandMSB)begincountdown.*//*binary,LSB/MSB,Ch2*///設置模式字,二進制,方式,先第八位再高八位,通道outb(CALIBRATE_LATCH&/*LSBof*/outb(CALIBRATE_LATCH>>/*count*///開始校準{unsignedlongstartlow,starthigh;unsignedlongendlow,unsignedlongcount{count++;}while&==rdtsc(endlow,endhigh);last_tsc_low/*Error:ECTCNEVERSET*/if(count<=1)

//寫入低位//寫入高位

/*withlong*/%2,%0\n\t""sbbl%3,%1""g"(starthigh),"0"(endlow),/*Error:ECPUTOOFAST*/if(endhigh)/*Error:*/if(endlow<=CALIBRATE_TIME)%2":"r"(endlow),"0""1"(CALIBRATE_TIME));}/**CTCwegotahitonveryfirst*quotientwouldn'tfitin*32*/0;}==============================時

===time_init=============================void__init{externintx86_udelay_tsc;==調(diào)用函數(shù)從RTC中得到系統(tǒng)啟動時的時間與日期,它返回的是當前時間相

對于-0100::00這UNIX時基的秒數(shù)值。因此這個秒數(shù)值就被保存在系統(tǒng)全局變量的tv_sec成中。而的另一個成員則初始化為0。/**IfweAPMenabledtheCPUisvariable*stopsclockorclocktosave*byupto1jiffyfrom*'realbutnothingwillbreak.*mostcaseCPU"woken"froma*statethetimerinterrupt0error.In*rarecasesadriverwouldrequesta*themaximumerroris<jiffy.timestampsare*stillperfectly*counterwillberesetifAPM*disk;breakthewe're*arch/i386/kernel/apm.c.*//**FirstlywehaveCPUforchipswith*potentiallyTSC.Atthispoint*ident/bugsrunthishookasit*turnoffTSCflag.**doesntyethandleSMPmachineswhereonly*CPU'shaveThatsnevernobody*moanedifyouhavetheonlyinworldyoufixit!*/通過dodgy_tsc()數(shù)檢測是存在時間戳記數(shù)器if(cpu_has_tsc){宏cpu_has_tsc可確定當前系統(tǒng)的是配置有TSC寄存器。unsignedlongtsc_quotient=if{fast_gettimeoffset_quotient==如果存在,那么內(nèi)核就可以用來得更為精確的時間。為了能夠用T來修正內(nèi)核時間。這里必須作一些初始化工作:①調(diào)用來確定的一次計數(shù)真正代表多長的時間間位為個鐘周期的真正時間間隔長度calibrate_tsc()函數(shù)所返回的值保存在全局變量fast_gettimeoffset_quotient中變量被用來快速地計算時間偏差;同時還將另一個全局變量use_tsc置為1表示內(nèi)核可以使用。/**WecouldbemoreselectivehereIsuspect*enableforthenextchips*/

=接下來將系統(tǒng)全局變量置為表可以通過來現(xiàn)微妙級的精確延時。該變量定義在arch/i386/lib/delay.c文中。do_gettimeoffsetdo_gettimeoffset=do_fast_gettimeoffset;將函數(shù)指針do_gettimeoffset強性地指向函數(shù)與之對應的是函而內(nèi)在計算時間偏差時可以用T這快的方法來進行。/*reportCPUclockin*formulais*/*1/=*clock/second.about100*/{unsignedlongeax=0,edx=1000;%2":"r""0"(eax),"1"(edx));printk("Detected%lu.%03luMHzprocessor.\n",cpu_khz/1000,1000);}}}#ifdefprintk("StartingTimer/*countdownvalue*/co_cpu_write(CO_CPU_TIMEVCO_TIME_HZ/HZ);/**/|/*interrupt*/co_cpu_read(CO_CPU_CTRL)&~CO_CTRL_TIMEMASK);/*cpuentrytohandlerCobaltAPICtoIDT)*/setup_irq(CO_IRQ_TIMER,時中斷安裝程序功能:將中斷程序連入相應的中斷請求隊列,以等待時

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論