摘要:
本文主要從內核實現的角度分析了Linux 2.4.0內核的時鐘中斷、內核對時間 的表示等。本文是為那些想要了解Linux I/O子系統的讀者和Linux驅動程序開發 人員而寫的。
關鍵詞:Linux、時鐘、定時器
申明:這份文檔是按照自由軟件開放源代碼的精神發布的,任何人可以免費 獲得、使用和重新發布,但是你沒有限制別人重新發布你發布內容的權利。發布 本文的目的是希望它能對讀者有用,但沒有任何擔保,甚至沒有適合特定目的的 隱含的擔保。更詳細的情況請參閱GNU通用公共許可證(GPL),以及GNU自由文檔 協議(GFDL)。
你應該已經和文檔一起收到一份GNU通用公共許可證(GPL)的副本。如果還沒 有,寫信給:The Free Software Foundation, Inc., 675 Mass Ave, Cambridge,MA02139, USA 歡迎各位指出文檔中的錯誤與疑問。
前言
時間在一個操作系統內核中占據著重要的地位,它是驅動一個OS內核運行的 “起博器”。一般說來,內核主要需要兩種類型的時間:
1. 在內核運行期間持續記錄當前的時間與日期,以便內核對某些對象和事件 作時間標記(timestamp,也稱為“時間戳”),或供用戶通過時間syscall進行 檢索。
2. 維持一個固定周期的定時器,以提醒內核或用戶一段時間已經過去了。
PC機中的時間是有三種時鐘硬件提供的,而這些時鐘硬件又都基於固定頻率 的晶體振蕩器來提供時鐘方波信號輸入。這三種時鐘硬件是:(1)實時時鐘 (Real Time Clock,RTC);(2)可編程間隔定時器(Programmable Interval Timer,PIT);(3)時間戳計數器(Time Stamp Counter,TSC)。
時鐘硬件
1、實時時鐘RTC
自從IBM PC AT起,所有的PC機就都包含了一個叫做實時時鐘(RTC)的時鐘 芯片,以便在PC機斷電後仍然能夠繼續保持時間。顯然,RTC是通過主板上的電 池來供電的,而不是通過PC機電源來供電的,因此當PC機關掉電源後,RTC仍然 會繼續工作。通常,CMOS RAM和RTC被集成到一塊芯片上,因此RTC也稱作“CMOS Timer”。最常見的RTC芯片是MC146818(Motorola)和DS12887(maxim), DS12887完全兼容於MC146818,並有一定的擴展。本節內容主要基於MC146818這 一標准的RTC芯片。具體內容可以參考MC146818的Datasheet。
1.1 RTC寄存器
MC146818 RTC芯片一共有64個寄存器。它們的芯片內部地址編號為0x00~ 0x3F(不是I/O端口地址),這些寄存器一共可以分為三組:
(1)時鐘與日歷寄存器組:共有10個(0x00~0x09),表示時間、日歷的具 體信息。在PC機中,這些寄存器中的值都是以BCD格式來存儲的(比如23dec= 0x23BCD)。
(2)狀態和控制寄存器組:共有4個(0x0A~0x0D),控制RTC芯片的工作方 式,並表示當前的狀態。
(3)CMOS配置數據:通用的CMOS RAM,它們與時間無關,因此我們不關心它 。
時鐘與日歷寄存器組的詳細解釋如下:
Address Function
00 Current second for RTC
01 Alarm second
02 Current minute
03 Alarm minute
04 Current hour
05 Alarm hour
06 Current day of week(01=Sunday)
07 Current date of month
08 Current month
09 Current year(final two digits,eg:93)
狀態寄存器A(地址 0x0A)的格式如下:
其中:
(1)bit[7]——UIP標志(Update in Progress),為1表示RTC正在更新 日歷寄存器組中的值,此時日歷寄存器組是不可訪問的(此時訪問它們將得到一 個無意義的漸變值)。
(2)bit[6:4]——這三位是“除法器控制位”(divider-control bits ),用來定義RTC的操作頻率。各種可能的值如下:
Divider bits Time-base frequency Divider Reset Operation Mode
DV2 DV1 DV0
0 0 0 4.194304 MHZ NO YES
0 0 1 1.048576 MHZ NO YES
0 1 0 32.769 KHZ NO YES
1 1 0/1 任何 YES NO
PC機通常將Divider bits設置成“010”。
(3)bit[3:0]——速率選擇位(Rate Selection bits),用於周期性或 方波信號輸出。
RS bits 4.194304或1.048578 MHZ 32.768 KHZ
RS3 RS2 RS1 RS0 周期性中斷 方波 周期性中斷 方波
0 0 0 0 None None None None
0 0 0 1 30.517μs 32.768 KHZ 3.90625ms 256 HZ
0 0 1 0 61.035μs 16.384 KHZ
0 0 1 1 122.070μs 8.192KHZ
0 1 0 0 244.141μs 4.096KHZ
0 1 0 1 488.281μs 2.048KHZ
0 1 1 0 976.562μs 1.024KHZ
0 1 1 1 1.953125ms 512HZ
1 0 0 0 3.90625ms 256HZ
1 0 0 1 7.8125ms 128HZ
1 0 1 0 15.625ms 64HZ
1 0 1 1 31.25ms 32HZ
1 1 0 0 62.5ms 16HZ
1 1 0 1 125ms 8HZ
1 1 1 0 250ms 4HZ
1 1 1 1 500ms 2HZ
PC機BIOS對其默認的設置值是“0110”。
狀態寄存器B的格式如下所示:
各位的含義如下:
(1)bit[7]——SET標志。為1表示RTC的所有更新過程都將終止,用戶程 序隨後馬上對日歷寄存器組中的值進行初始化設置。為0表示將允許更新過程繼 續。
(2)bit[6]——PIE標志,周期性中斷使能標志。
(3)bit[5]——AIE標志,告警中斷使能標志。
(4)bit[4]——UIE標志,更新結束中斷使能標志。
(5)bit[3]——SQWE標志,方波信號使能標志。
(6)bit[2]——DM標志,用來控制日歷寄存器組的數據模式,0=BCD,1 =BINARY。BIOS總是將它設置為0。
(7)bit[1]——24/12標志,用來控制hour寄存器,0表示12小時制,1表 示24小時制。PC機BIOS總是將它設置為1。
(8)bit[0]——DSE標志。BIOS總是將它設置為0。
狀態寄存器C的格式如下:
(1)bit[7]——IRQF標志,中斷請求標志,當該位為1時,說明寄存器B中 斷請求發生。
(2)bit[6]——PF標志,周期性中斷標志,為1表示發生周期性中斷請求 。
(3)bit[5]——AF標志,告警中斷標志,為1表示發生告警中斷請求。
(4)bit[4]——UF標志,更新結束中斷標志,為1表示發生更新結束中斷 請求。
狀態寄存器D的格式如下:
(1)bit[7]——VRT標志(Valid RAM and Time),為1表示OK,為0表示 RTC已經掉電。
(2)bit[6:0]——總是為0,未定義。
1.2 通過I/O端口訪問RTC
在PC機中可以通過I/O端口0x70和0x71來讀寫RTC芯片中的寄存器。其中,端 口0x70是RTC的寄存器地址索引端口,0x71是數據端口。
讀RTC芯片寄存器的步驟是:
mov al, addr
out 70h, al ; Select reg_addr in RTC chip
jmp $+2 ; a slight delay to settle thing
in al, 71h ;
寫RTC寄存器的步驟如下:
mov al, addr
out 70h, al ; Select reg_addr in RTC chip
jmp $+2 ; a slight delay to settle thing
mov al, value
out 71h, al
1.3 可編程間隔定時器PIT
每個PC機中都有一個PIT,以通過IRQ0產生周期性的時鐘中斷信號。當前使用 最普遍的是Intel 8254 PIT芯片,它的I/O端口地址是0x40~0x43。
Intel 8254 PIT有3個計時通道,每個通道都有其不同的用途:
(1) 通道0用來負責更新系統時鐘。每當一個時鐘滴答過去時,它就會通過 IRQ0向系統產生一次時鐘中斷。
(2) 通道1通常用於控制DMAC對RAM的刷新。
(3) 通道2被連接到PC機的揚聲器,以產生方波信號。
每個通道都有一個向下減小的計數器,8254 PIT的輸入時鐘信號的頻率是 1193181HZ,也即一秒鐘輸入1193181個clock-cycle。每輸入一個clock-cycle其 時間通道的計數器就向下減1,一直減到0值。因此對於通道0而言,當他的計數 器減到0時,PIT就向系統產生一次時鐘中斷,表示一個時鐘滴答已經過去了。當 各通道的計數器減到0時,我們就說該通道處於“Terminal count”狀態。
通道計數器的最大值是10000h,所對應的時鐘中斷頻率是1193181/(65536 )=18.2HZ,也就是說,此時一秒鐘之內將產生18.2次時鐘中斷。
2、 PIT的I/O端口
在i386平台上,8254芯片的各寄存器的I/O端口地址如下:
Port Description
40h Channel 0 counter(read/write)
41h Channel 1 counter(read/write)
42h Channel 2 counter(read/write)
43h PIT control word(write only)
其中,由於通道0、1、2的計數器是一個16位寄存器,而相應的端口卻都是8 位的,因此讀寫通道計數器必須進行進行兩次I/O端口讀寫操作,分別對應於計 數器的高字節和低字節,至於是先讀寫高字節再讀寫低字節,還是先讀寫低字節 再讀寫高字節,則由PIT的控制寄存器來決定。8254 PIT的控制寄存器的格式如 下:
(1)bit[7:6]——Select Counter,選擇對那個計數器進行操作。“00 ”表示選擇Counter 0,“01”表示選擇Counter 1,“10”表示選擇Counter 2 ,“11”表示Read-Back Command(僅對於8254,對於8253無效)。
(2)bit[5:4]——Read/Write/Latch格式位。“00”表示鎖存(Latch) 當前計數器的值;“01”只讀寫計數器的高字節(MSB);“10”只讀寫計數器 的低字節(LSB);“11”表示先讀寫計數器的LSB,再讀寫MSB。
(3)bit[3:1]——Mode bits,控制各通道的工作模式。“000”對應 Mode 0;“001”對應Mode 1;“010”對應Mode 2;“011”對應Mode 3;“100 ”對應Mode 4;“101”對應Mode 5。
(4)bit[0]——控制計數器的存儲模式。0表示以二進制格式存儲,1表示 計數器中的值以BCD格式存儲。
2.1 PIT通道的工作模式
PIT各通道可以工作在下列6種模式下:
1. Mode 0:當通道處於“Terminal count”狀態時產生中斷信號。
2. Mode 1:Hardware retriggerable one-shot。
3. Mode 2:Rate Generator。這種模式典型地被用來產生實時時鐘中斷。此 時通道的信號輸出管腳OUT初始時被設置為高電平,並以此持續到計數器的值減 到1。然後在接下來的這個clock-cycle期間,OUT管腳將變為低電平,直到計數 器的值減到0。當計數器的值被自動地重新加載後,OUT管腳又變成高電平,然後 重復上述過程。通道0通常工作在這個模式下。
4. Mode 3:方波信號發生器。
5. Mode 4:Software triggered strobe。
6. Mode 5:Hardware triggered strobe。
2.2 鎖存計數器(Latch Counter)
當控制寄存器中的bit[5:4]設置成0時,將把當前通道的計數器值鎖存。 此時通過I/O端口可以讀到一個穩定的計數器值,因為計數器表面上已經停止向 下計數(PIT芯片內部並沒有停止向下計數)。NOTE!一旦發出了鎖存命令,就 要馬上讀計數器的值。
2.3 時間戳記數器TSC
從Pentium開始,所有的Intel 80x86 CPU就都又包含一個64位的時間戳記數 器(TSC)的寄存器。該寄存器實際上是一個不斷增加的計數器,它在CPU的每個 時鐘信號到來時加1(也即每一個clock-cycle輸入CPU時,該計數器的值就加1) 。
匯編指令rdtsc可以用於讀取TSC的值。利用CPU的TSC,操作系統通常可以得 到更為精准的時間度量。假如clock-cycle的頻率是400MHZ,那麼TSC就將每2.5 納秒增加一次。