自幹作業系統 - 初探 Timer 原理
在 自幹作業系統 - Simple OS 的過程中,我的學習方法是快速的先取得成就感,透過與 AI 協作過程,快速刻出能動的、完整的,只用了三週就完成實作 (03/12 - 03/27),但是非常古早味的作業系統,說白話:先能動再說,好聽的就是 麻雀雖小,五臟俱全。但往下探索,很容易暴露一個現實:知其然,不知所以然,有些東西就是沒有透測的理解。但也是這份好奇心停留在心裡,我開始往下了解與深究背後的原理以及知識,同時也連結到以前的經驗與知識 (或者資訊)。
Day12 - Timer/PIT (Programmable Interval Timer) 是我開始深究的題目,底下是我好奇的幾個點:
整理一下探索過程學習到的資料,以及自問自答相關資料。
自幹作業系統 系列:
註:內容僅是自學的一些筆記,如果有發現資訊不正確,或者描述錯誤,請不令給予指教,感謝。
Timer in SimpleOS
計算機構成 Timer (計時器) 整個工作的角色有以下:
CPU: 中央處理單元,負責計算資料PIT: Programmable Interval Timer (可程式化間隔計時器),負責產生固定的頻率為1,193,180 Hz,通常是 INTEL 8253/8254 晶片,背後的物理原理是透過 石英振盪器 (Crystal Oscillator) 輸出固定的頻率PIC: Programmable Interrupt Control (可程式化中斷控制器),當外部硬體 (PIT、鍵盤、滑鼠、網卡) 發出中斷訊號時,負責執行仲裁角色,通常是 INTEL 8259 晶片OS: 作業系統,介於硬體與應用層之間的軟體。
註:PIT, PIC 這兩顆晶片在現代計算機裡,會被整合在主機板上的
南橋晶片組內,但邏輯與職責上還是分開的。
用一個例子來說明,這三個角色的運作:
- 主管 (CPU) 在負責對外溝通與協調,正在忙著開會中;而團隊成員 (其他硬體: 滑鼠、鍵盤) 有事要找他;秘書 (PIC) 則負責管理誰可以去敲老闆的門;行事曆 (PIT) 則是一個時間軸。
- 當整點時間到了 (PIT),秘書 (PIC) 會根據 來敲門的團隊成員 (滑鼠與鍵盤) 優先序,安排跟老闆 (CPU) 碰面。
- PIT 就是個固定計數器,一直在數數 (tick),一個單位時間的數值到了,就會跟 PIC 講,由 PIC 根據次序安排硬體跟 CPU 溝通。
SimpleOS 的 timer.c 有以下介面:
1 | void init_timer(uint32_t frequency); |
其中 init_timer() 負責出根據 PIT 的工作頻率與目標中斷次數 (frequency),計算出 tick 的最小單位,然後把 tick 最小單位的值寫到 PIT。而 timer_handler() 則是負責記錄每次中斷的 tick 累加。
整個 Timer 概念在 Simple OS 裡大概如下:

PIT - INTEL 8254
計數器晶片 INTEL 8254 負責產生的規律電子訊號,他的基礎頻率是 1.19318 MHz (1,193,180 Hz),頻率則透過 石英振蕩器生成 (Crystal Oscillator)。1,193,180 這個數字緣由是彩色電視 NTSC 的副載波頻率(14.31818 MHz)經過 12 分頻計算得來。在電子工程中,「分頻 (Frequency Division)」指的是將一個高頻率的訊號,轉換成較低頻率訊號的過程。簡單說「12 分頻」就是把原始頻率除以 12。
14.31818 MHz / 12 = 1,193,181 Hz
作業系統約定成俗的會使用 1,193,180 或 1,193,182 當常數
80 年代 (IBM PC 誕生初期) 工程師為了省錢,不想在主機板上插滿各種不同頻率的石英鐘,於是採用了「一魚多吃」策略:
- 主頻源:只買一個最便宜、大量生產的 14.31818 MHz 石英震盪器 (當時因為彩色電視普及,這種零件極度便宜)。
- 給顯卡用:這個頻率直接給彩色顯示卡使用,因為它剛好是 NTSC 電視訊號的 4 倍。
- 給 CPU 用:經過 3 分頻 (14.318 MHz / 3 = 4.77 MHz),這就是初代 IBM PC 處理器的時脈。
- 給計時器用:經過 12 分頻 (14.318 MHz / 12 = 1.19 MHz),這就是 8254 PIT 晶片拿到的工作頻率。
PIC - INTEL 8259
中斷控制器 (Programmable Interrupt Controller, PIC) 通常是 Intel 8259 這顆晶片,是介於所有硬體設備(如 PIT、鍵盤、滑鼠)與 CPU 之間的「通訊官」。PIC 的角色如同警衛,在 PIC 向 CPU 發出中斷訊號後,會自動擋住後續的所有硬體中斷訊號。如果 OS 不回覆 EOI (End of Interrupt) 指令給 PIC,PIC 會判定 CPU 仍在處理舊的中斷,導致 PIC 停止發送任何新的中斷訊號給 CPU,最終造成 OS 無法接收新的 Tick 或硬體事件,系統表現如同「當機」。
OS 透過發送 EOI (End of Interrupt) 指令告訴 PIC:
OS 已經處理完當前的中斷請求,PIC 可以發送下一個中斷訊號給 CPU 了。
完成告知之後,OS 就可以做所謂的 Context Switch,把 CPU 的使用權讓出去給其他的 Task,這個過程則是透過 Scheduler 執行。在 SimpleOS 的實作,是在 timer_handler() 裡面最後調用 schedule(),實踐 搶佔式多工 (Preemptive Multitasking),強行切換任務,達成「每個程式都在同時執行」的假象。
timer.c#init_timer() - 初始節拍器最小單位:Tick
tick (狀聲詞, 滴答聲) 用來記錄 作業系統 的 節拍數、虛擬時間,定義作業系統的節奏感,從開機就開始計算,每次的累加,都代表著作業系統時間的流逝。
實際的運作原理是利用 INTEL 8254 的基頻 - 1,193,180 Hz,我們想在每秒產生 100 次中斷 / 100 個 tick,在 timer.c#init_timer() 計算方式以及初始 PIT 流程如下:
1 | volatile uint32_t tick = 0; |
整個流程有三個步驟:
- 計算 divisor:表示震盪 N 次,就發出一次中斷。freq=100 標示 PIT 每經過 1193.18 個震盪週期,就是一個 tick (一拍),每秒則有 100 個 tick。
- 設定 PIT 模式控制:包含選擇通道、存取模式、選擇訊號產生的方式。
- 把 divisor 寫入 PIT:把 1193 這個數字跟 PIT 講,之後這個頻率就發出中斷訊號。
timer.c 的 tick 值是一個 原子操作 或受 中斷保護 的操作,確保所有依賴時間的 SysCall (如 sleep) 都有統一的參考。所以 tick 在 c 語言裡需要宣告 volatile,保證 tick 可見性 (Visibility)。如果沒有它,在多工或編譯器優化下,一個 while(tick < target) 的無窮迴圈可能會因為編譯器認為 tick 在迴圈內沒被修改,而將其優化為死循環,永遠不重新讀取記憶體中的新值。
題外話:編曲軟體 (Digial Audio Workstation, DAW) 中常用的 Piano Roll 畫面,為了要可以紀錄更自然、更真實的演奏時間,每個拍子的單位不是只有固定在節奏數,而是有個單位稱為
PPQ (Pulses Per Quarter Note),例如 PPQ=360,代表一拍可以再細分 360 個等分,透過這樣的方式可以紀錄演奏更細緻的表情,像是琶音這種技巧。現代的 DAW 像是 Logic Pro、FL Studio 預設為 960 PPQ。下圖由 Google 產生
另一個例子,是電影
頭文字 D裡面的對話,描述對時間體感越來越慢的時候,其實背後就是類似的概念,代表 tick 數越來越高了!「幾年前,他說看東西不清楚,我帶他去配眼鏡。誰知道驗過眼睛以後,一點事也沒有。……他對我說,最近不知道為什麼,開車的時候看東西越來越慢,那時候,我才知道,他越來越快了。」
timer.c#timer_handler() - 作業系統的 Game Loop
timer.c#timer_handler() 每一個週期會被 IRQ32 中斷 interrupts.S 呼叫一次,整個內容最重要的幾件事:
- 累加 Tick,也就是現在是第幾個 Clock
- 告訴 中斷控制器,這個 Tick 結束,請重新分配 Task
- 讓 OS 透過 Task Scheduler 執行 Context Switch,重新分配 CPU
整個 Timer 運作流程如下:

timer_handler() 是透過 PIC 固定週期就被調用一次,概念類似於 Game Loop 裡的 FPS。底下是我在 純手工遊戲開發 - Java 2D RPG Game 的 Game Loop 精簡過後的片段邏輯:
1 | public void run() { |
兩者都是有一個 while(true) {} 的 loop,然後固定調用 function - timer_handler() and update()但不同的是:
- Game Loop 會因為 Update 處理的效能 (通常是 2D/3D 圖形渲染),因而影響 FPS 的大小。
- Game Loop 裡
update()更新的每一個 Frame,前後彼此之間是有關聯的。如果計算錯誤、或者計算延遲,就會產生卡頓、掉幀的感覺。 - OS 不能因為 task 慢了,tick 計數就停下來,這在電腦科學中稱為
不可屏蔽性或搶佔 (Preemption)。Game Loop 中的update()是協作式的 (前一個跑完下一個才跑);OS 的 timer_handler 是侵入式的 (管你跑完沒,硬體訊號強行插隊)。
底下我整理了我自己的理解的差異:
| 特性 | Java 2D RPG Game - Game Loop | Simple OS - Timer |
|---|---|---|
| 迴圈本體 | while(running) { ... } |
isr32: -> call timer_handler |
| 頻率控制 | 軟體自行計算 delta 並 Sleep |
硬體晶片固定發送電氣訊號 |
| 執行保證 | 若卡住,下一影格就順延 | 若卡住,硬體訊號會強制插隊 (或遺失) |
| 主要任務 | 更新遊戲邏輯、渲染畫面 | 計時、統計 CPU 佔用、強制換人 (schedule) |
簡單說:
Game Loop:主動式。跑者自己看手錶,跑太快就 Sleep。OS Tick:被動式。硬體監工每隔一段時間「啪」地拍桌子,強制 CPU 停下工作,執行 OS 的家務事。
小結
在 Linux 面前,我的知識是匱乏的、無知的,我在看待 Linux Kernel 時是尊敬的,因為他是人類智慧與文明的精華,經過千錘百鍊的結果。開啟 Simple OS 自幹的初衷,就是覺得我自己知識匱乏的緊,即使我已經工作數十餘載,但常常還是覺得自己懂的太少,有時候會有 一曝十寒 之感。
洋蔥是學習是我的方法,一圈一圈的扒開,逐漸明朗,好玩又有趣!再配合很多既有的課程,像是 Linux 核心設計: Timer 及其管理機制、以前在研究 分散式系統 讀過的論文 Time, clocks, and the ordering of events in a distributed system – Leslie Lamport (1978),利用幫我 AI 探索,這個過程是有趣且豐富的!
但如果只是把這一切都交給 AI Agent,那最後我將得到一個能用的東西,但我依舊停在原點,呈現 淺薄思考 與 知識匱乏 的狀態。
未完
timer.c 延伸議題還有很多想寫,不過再寫下去篇幅太長了,相關題目先做個紀錄,之後慢慢來補。
- timer.c 概念,那麼 User Space 的
sleep()是怎麼被實作出來的?- 已經完成實作,不過內容篇福有點多,加上要講到
Process Control Block、Task Scheduler會有點複雜,需要放到獨立的一篇來講。
- 已經完成實作,不過內容篇福有點多,加上要講到
- 實務上曾經遇過跟時間有關的想法,包含
sleep(),ntp、k8s cpu=100m的意思 - 摘要現代 Linux Kernel 跟 timer / tick 相關的資訊
- 探索程式語言 Java 21 sleep() 的實作
- 以前在研究 分散式系統 讀過的論文 Time, clocks, and the ordering of events in a distributed system – Leslie Lamport (1978) 的一些想法
最後。。。還是要提一下 AI,字很多,但其實只有寫 30%。
知識飛輪的推與拉 - 知識複利
AI 的發展飛速,最近大部分的人都可以用 Agentic 方式快速寫出應用程式。有程式背景的人,在使用上的生產力更是 10x 倍速的成長。也出現 Andrej Karpathy 用 LLM-Wiki 做 知識管理 概念的方法,有人把想法快速用 Agentic 方法做出來 - 像是 Graphify。
我先不提軟體工程或 SRE 等議題。
AI 我定位是個高效能工具,創意協作工具,依舊是工具。使用的人才是 主要、主要的,AI 是 次要的。 再次強調,人是主要的,工具是次要的。
使用的人可以透過 AI 推進產品的開發、推進工作產能提升。同時也可以透過 AI 拉動自己,提升自己的本職學能、知識、啟發創意。
我在 自幹作業系統 這件事情上定位是 - Just for Fun & Learning。也就是目標是充實我腦袋裡的知識,在學習與實踐過程中,把片段的知識補齊、把這些學習的知識連結起來。注意我這句話:
把片段的知識補齊、把這些學習的知識連結起來
主詞是誰?主動的是誰?
答案是我自己。
- 我自己把知識放到腦袋裡
- 我自己把知識點連結起來
這個過程,我腦袋裡會建立屬於自己的知識連結,做深度思考、深度連結。也就是我們常說的:
資料 -> 資訊 -> 知識 -> 智慧
這個循環又稱為 DIKW 體系,我在以前學習音樂就很常在用這概念。
我在 聊聊分散式系統 最後提到人類大腦運作的原理,當時是用分散式系統在解讀。底下是簡單摘要:
人類大腦由 140 億個神經元組成,神經元又由細胞體、神經纖維組成,其中細胞體想像成計算機,神經纖維想像成網路。生命的演化,正是因為神經元不斷地增長,最後才演化出智慧生命體:人類。人類大腦的
灰質相當於計算機 (細胞體),白質相當於連結系統 (神經纖維)的網路。表示人腦約有 140 億台計算機 (灰質、細胞體) 同時在運作,計算機之間透過網路連結 (白質、神經纖維)。研究顯示註,人與人之間的區別,不在灰質,在於白質。例如鋼琴家的白質,遠比普通人發達的多。人與人之間腦袋裡的運算能力其實大同小異,但是相差很大的是腦袋裡的網路的連線方式不同。
其實我用來解讀知識管理也是通的。也就是知識管理的重點不在搜集或者整理,重點在於 連結,然後形成的體系。關鍵在於 連結,誰來做連結?要把知識放到腦袋裡的人,也就是你自已。
換言之,AI LLM 可以很快速的整理出所有資料,畫出一個漂亮的關聯拓樸,這表示什麼?使用知識的人,從頭到尾都沒有在自己的腦袋裡建立連結,換言之,你依舊停在原地。
我在 聊聊寫 Blog 的想法 最後也寫到,我每寫一篇文章的過程,就會把過去寫的東西串起來,這個過程的思路都是我自己連結的。經過幾次的複利效應之後,文章就會成為一個體系,最後就會形成我個人的知識,我稱為 複利知識。所以知識管理這個議題,只要有持續寫文章,持續重構、組織的人,看到用 LLM 整理文章,我的直覺反應就是:
他是他,你是你。
文章關聯快速建立起來,但你並沒有因此真的獲得知識。
回到段落標題,知識飛輪的推與拉,我引用的是 Amazon 的 飛輪效應 概念,利用 推與拉做知識管理,飛輪效應之下的知識管理帶來的 複利效應。
如果你有耐心看到這裡,應該不難發現,這段整個過程我都在串聯我過去寫的文章,以及想法。這個過程就是連結知識,創造 知識複利。
底下問題我先留白,一些很現實的問題,未來可以探討的:
- 大家 (80%) 都可以用 Agenitc 寫出應用,那彼此的差異是什麼?公司為什麼要用你?
- Vibe Coding 可以很快做出應用程式,不會寫程式的人很快做出一個類似 591 的租賃網站,他做得出來?你做得出來?
- 這個有機會可以說說十幾年前,我在電商服務時,承接一些 3rd-party 遺留下來的東西 …
- 用 AI 提升工作生產力,薪水有變多了?時間有變多了?生活更有餘裕了?
- 大家 (80%) 都可以用 LLM KM 創造出自己的第二大腦,所以這些人都變得有知識了?有智慧了?有創造力?
- 很多人都可以用 AI 快速寫出應用,然後呢?因此創造 Income,往 FIRE 走?或者滿街的人都往 FIRE 前進?
- 承上,稍微提一下
軟體工程,這些被快速創造的應用:- 個人用的:過了半年後還有在用?功能都滿足了?
- 企業:過了半年有多少應用還在記憶體裡運行的?多少人在用?都沒有 Bug?
- 上述應用的維護成本 –> 簡單帶入 SRE …
我不是反對 AI 的一些方法,我自己也用很兇 (燒很多錢)。而是要提醒我自己 (其實別人怎樣不關我的事),不要因為這些工具太強大,因而帶來 淺薄思考 與 知識幻覺,反而要善用他們來強大自己,讓生活更豐富更有想像,帶來更多探索的樂趣。但要認清的事實是:
- AI 建立的只是資料的拓樸,你在腦袋裡建立的資料網路才能形成「知識」。
- 看懶人包,學不到知識;寫懶人包,才真正擁有知識。
- 只有透過深度思考,才能真的學習。
幾個名詞,我自己亂掰的:
知識幻覺: 大概類似達克效應的概念。很叫 AI 做事,誤以為自己很厲害,其實一曝十寒。知識飛輪: 從飛輪效應和知識管理結合而來,形容建立腦袋知識體系的方法,就是推與拉複利知識: 跟知識飛輪類似的想法,集合知識管理與複利效應這兩個詞,重點在於知識是可以透過連結不斷堆疊,產生複利效應的。最好的方法就是寫 Blog,然後要連結他們。寫流水帳的沒用。
收筆。
延伸閱讀
自幹作業系統 系列
- Source Code
- Demo/錄影
- 專有名詞對照表
- 自幹作業系統 - Simple OS
- 自幹作業系統 - Networking Fundamentals
- 自幹作業系統 - 初探 Timer 原理
- 自幹作業系統 - Task and Scheduler
- 自幹作業系統 - Context Switch
站內文章
參考資料
- Linux 核心設計: Timer 及其管理機制
- Intel 8253 / 8254 - programmable interval timers (PITs)
- Intel 8259 - programmable interrupt controller (PIC)
- Time, clocks, and the ordering of events in a distributed system - Leslie Lamport (1978)




