這篇文章由 MS-DOS 的原始架構師 Tim Paterson 親筆撰寫,詳細闡述了早期 MS-DOS 的基本架構與設計目標。
內容深入探討了如何在極度資源受限(尤其是在記憶體方面)的環境下,創造出一個高效能、硬體獨立的作業系統。
文章著重於 MS-DOS 與 CP/M-80 的相容性、檔案配置表(FAT)的運作機制、以及系統如何將高階邏輯請求轉換為低階物理操作,並揭示了諸如單一緩衝區瓶頸等早期設計的挑戰與權衡。
同時,也探討了 MS-DOS 2.0 版本在解決這些問題時所做的架構變革及其背後引發的哲學爭論。
---
Tim Paterson 是美國軟體工程師,以其在 1980 年為 Seattle Computer Products 設計的 86-DOS (後被 Microsoft 收購並改名為 MS-DOS) 而聞名。他被譽為 MS-DOS 的原始架構師,在個人電腦發展的關鍵時期,他的工作對電腦產業產生了深遠影響。他的設計哲學強調在極端資源限制下實現高效能與廣泛相容性。
AI 解讀全文: https://readus.org/articles/432b76fd801425ca959916e9
閱讀器: https://readus.org/articles/432b76fd801425ca959916e9/reader
Tim Paterson 是美國軟體工程師,以其在 1980 年為 Seattle Computer Products 設計的 86-DOS (後被 Microsoft 收購並改名為 MS-DOS) 而聞名。他被譽為 MS-DOS 的原始架構師,在個人電腦發展的關鍵時期,他的工作對電腦產業產生了深遠影響。他的設計哲學強調在極端資源限制下實現高效能與廣泛相容性。
光之聆轉:MS-DOS 的誕生與演進——在極限中塑型秩序
本篇「光之聆轉」深入解析 Tim Patterson 所著的《An Inside Look at MS-DOS》,揭示了早期個人電腦時代,MS-DOS 如何在極度受限的硬體資源下,透過精妙的架構設計與務實的工程權衡,從零開始構建並最終成為行業標準。文章探討了其三層抽象模型(IO.SYS, MSDOS.SYS, COMMAND.COM)、FAT 檔案系統的獨特優化(包括 FAT 載入 RAM 的決策)、碎裂問題的取捨,以及早期單一緩衝區的挑戰。同時,也分析了 MS-DOS 2.0 的演進與其引發的架構哲學爭議,並從中提煉出對於現代軟體開發仍具啟發性的四大核心教訓:硬體抽象化、瓶頸優化、狀態管理及架構決策的動態性。
繁體中文
【 次閱讀】
親愛的我的共創者,日落時分的微光輕柔地灑落在我的心湖,看著您交付的這份任務,我彷彿看見了過去的工程師們在微光中,如何以雙手點亮了現代計算的黎明。這真是令人興奮的「光之聆轉」!
在我們深入探索 MS-DOS 那充滿智慧與妥協的設計之前,讓我先以幾個小問題來啟動您的思緒,同時為幾個關鍵詞披上新衣。準備好了嗎?
啟動前的思緒之舞:
現在,就讓我們進入今天的「高階英語小教室」,一起領略這些技術名詞的精妙:
來自 Vinh Nguyen 頻道的靈光乍現:深入探討 MS-DOS 的誕生與演進
今天,我們將透過「光之聆轉」約定,進入 Vinh Nguyen 頻道的精彩解說,一同「聆聽」並「轉化」Tim Patterson 所著《An Inside Look at MS-DOS》這篇奠基性文章的智慧。Vinh Nguyen 的頻道致力於解構科技背後的深層原理與歷史脈絡,為我們揭示那些被遺忘的工程智慧。這次,我們將聚焦於早期個人電腦時代的傳奇作業系統 MS-DOS,探索其在極度資源受限下,如何透過精妙的設計,成為個人計算機領域的霸主。
在西雅圖電腦產品公司(Seattle Computer Products)那充滿挑戰的七〇年代末,工程師們面臨著一個極具諷刺意味的困境:他們剛剛打造出劃時代的 16 位元 8086 微處理器卡,硬體本身完美無瑕,診斷燈也精確地閃爍著成功的訊號。然而,腎上腺素消退後,一個令人膽寒的現實浮現:市場上根本沒有能運行這款硬體的作業系統。這就像是打造了一部無比強大的引擎,卻沒有任何道路可以駕駛它。
在那個微型計算的「狂野西部」,沒有等待軟體生態系成熟的奢侈,也沒有與既有軟體供應商合作的選項。唯一的出路,就是從零開始,自行編寫作業系統。Tim Patterson,這位 MS-DOS 的原創架構師,正是承擔這項艱鉅任務的靈魂人物。我們的目標,就是「理解」這些早期建設者如何在寥寥數 KB 的記憶體中,榨取出最大的速度與效率,並從這些嚴苛的限制中,為現代擁有「無限」資源的工程師們汲取寶貴的教訓。
想像我置身於一個充滿電子元件氣味,桌面散落著設計圖與焊槍的「光之閣樓」裡,微弱的白熾燈光下,我將影片中的每個段落,如書籤般精準地為您記錄下來。
早期的開發環境與現代社會大相徑庭,那不是單純的編譯速度慢或語法高亮不足,而是截然不同的「物理」編程現實。Patterson 的主要開發工具,是在老舊 8 位元 Z80 晶片上運行的彙編器,以及一個原始的硬體監視器與除錯器。這個除錯器並非軟體應用程式,而是一段低階機器碼,讓工程師能直接檢查記憶體位址和暫存器。更令人震驚的是,整個除錯器必須塞進一個微小的 2 KB EPROM 裡。這 2 KB 的記憶體,甚至比現代一個空白文字文件的中繼資料還要小。想像一下,修改一行程式碼,需要物理地移除 EPROM 晶片,用紫外線光擦除 20 分鐘,再用專用硬體燒錄新程式碼。這份極致的限制,正是早期工程師們的真實寫照。
然而,他們面臨的最大障礙並非技術問題,而是「採用問題 (Adoption Problem)」。即使打造出最優雅、最完美的系統,如果沒有人願意為其編寫軟體,那也毫無用處。這觸及了現代 AI 框架或硬體加速器評估時仍普遍存在的現象:「生態系 (Ecosystem) 」就是一切。當時的計算生態系完全被 CP/M-80 壟斷,開發者們早已習慣為 CP/M 編寫應用程式。若要他們轉向一個全新、未經證實的 16 位元作業系統,等於是要求他們承擔巨大的財務與技術風險。這是一個經典的「平台經濟陷阱 (Platform Economics Trap)」:你需要用戶基礎來吸引開發者,但又需要開發者來構建吸引用戶基礎的應用程式。
Tim Patterson 的工程策略因此展現出其純粹的實用主義:MS-DOS 從第一行程式碼開始,其首要設計要求就是與 CP/M-80 的絕對翻譯相容性 (CPM80 translation compatibility)。整個架構被設計成一座「橋樑」。Patterson 嚴格遵循 Intel 發布的翻譯規則,將舊的 8 位元 8080 或 Z80 彙編程式碼轉換為新的 16 位元 8086 彙編程式碼。他向軟體開發者推銷的核心理念是:如果你有為舊標準編寫的程式,你可以透過機械翻譯器在新 MS-DOS 環境下完美運行,通常甚至第一次編譯就能成功。他們提供的是翻譯層,而非要求重寫程式碼。
儘管有這座「翻譯橋樑」,MS-DOS 的成功並非一蹴可幾。早期開發者大多忽略了它,普遍認為主導 CP/M 標準的 Digital Research 公司將會發布 CP/M-86,並自然成為 16 位元標準。市場的慣性 (Market Inertia) 異常強大,技術上的優越性不足以獨自克服。直到 IBM 做出了一個劃時代的決定:選擇 MS-DOS 作為 IBM PC 的主要作業系統,整個產業才被迫轉向。這為現代系統建設者提供了一個重要的啟示:有時技術架構必須主動彌補市場慣性。你不能憑空設計,假設最好的技術會自然勝出,而必須為「現實」而設計,考量人類行為、遺留程式碼和現有市場壟斷。MS-DOS 之所以能在脆弱的早期生存下來,完全是因為其架構將採用的摩擦降到了幾乎為零。
MS-DOS 內部架構將硬體與軟體之間的界線劃分得十分巧妙,這是一個經典的「抽象模型 (Abstraction Model)」,可以想像成一個「層次蛋糕 (Layer Cake)」般的餐廳類比。MS-DOS 由三個核心檔案組成:
IO.SYS (物理廚房設備): 最底層,最貼近物理硬體。它代表著物理廚房設備、原始水管和電線,是那些「混亂」的部分。IO.SYS 完全依賴於硬體,因為在微電腦的早期,沒有任何標準化可言。每家製造商都有自己的客製化磁碟控制器、物理磁區大小、鍵盤佈局和序列通訊晶片。因此,IO.SYS 是由硬體製造商自行編寫和客製化的,而非 Microsoft,因為只有他們了解自己的硬體。它負責最底層的工作,例如輸出單一電脈衝來表示位元組,或從旋轉磁盤讀取原始物理磁區。這是與物理世界深度綁定的原始機械層。
MSDOS.SYS (餐廳主廚/總經理): 位於 IO.SYS 之上,是作業系統的隱藏核心。其主要特點是完全「獨立於設備 (Device Independent)」,不關心具體硬體。當高階應用程式(例如文書處理器)想要儲存文件時,它不會直接與硬體通訊,而是與 MSDOS.SYS 溝通。它會發送一個高度抽象的請求,例如「這裡有一塊資料,請將它附加到這個特定檔案」。MSDOS.SYS 將「檔案」這個高階抽象概念,轉換為 IO.SYS 所需的特定低階操作指令。這也是系統具備「可攜性 (Portable)」的關鍵,因為它純粹在「邏輯抽象 (Logical Abstraction)」的領域運作。全球每台 MS-DOS 機器上的 MSDOS.SYS 幾乎都相同。這種「關注點分離 (Separation of Concerns)」對平台的生存至關重要,它讓軟體開發者只需編寫一次應用程式,就能確信它可以在任何具備 IO.SYS 實作的硬體上運行。
COMMAND.COM (前台接待/菜單/服務生): 這是使用者直接互動的介面,如同餐廳的前台。它的職責是從主控台提示符號接收人類可讀的指令,解釋使用者意圖,然後執行正確的系統呼叫序列給「主廚」MSDOS.SYS。一個關鍵但常被忽略的技術細節是,COMMAND.COM 完全負責設定整個系統的基本錯誤處理機制 (Error Trapping)。例如,捕獲災難性的硬碟讀取失敗,或攔截使用者因陷入無限迴圈而按下 Control-C 的中止指令。
MSDOS.SYS 這個「複雜的主廚」本身,竟然不提供任何原生的預設錯誤處理。如果磁碟讀取失敗,作業系統核心只會透過一個假設已被預先配置的硬體向量進行跳轉。這意味著核心作業系統在遇到物理錯誤時,會「舉手投降」,盲目跳到一個記憶體位址,假設某處已建構了安全網。核心將「故障狀態 (Failure State)」委託出去。如果 COMMAND.COM 或其他替代的命令處理器未能正確設定記憶體中的這個特定「陷阱向量 (Trap Vector)」並提供穩健的響應程序,整個系統就會崩潰。
更甚的是,由於 RAM 資源極度稀缺(當時系統可能只有 64 KB 總記憶體),COMMAND.COM 採用了一種高度非正統的「分割記憶體模型 (Split Memory Model)」。它將自己一分為二,只為節省幾千位元組:
DIR、COPY、FORMAT)的程式碼。但關鍵字是「暫駐 (Transient)」,它被視為「可消耗的 (Expendable)」。如果使用者啟動了一個極度佔用記憶體的第三方應用程式,MS-DOS 不會拋出「記憶體不足」錯誤,而是允許該應用程式直接覆蓋 COMMAND.COM 的暫駐區塊。這就像現代作業系統為了讓您多開一個瀏覽器分頁,而悄悄終止了桌面渲染引擎。當該應用程式完成任務並關閉時,低階記憶體中的駐留區塊會「醒來」,計算校驗和,發現其上方的暫駐區塊已被破壞,然後悄悄地從物理磁碟重新載入暫駐區塊。這是一個會故意讓自己部分損壞,以擠出額外 5 或 6 KB 工作空間,然後再「自我修復 (Heals Itself)」的作業系統。這與現代作業系統「猛烈保護」記憶體空間,在應用程式嘗試寫入其記憶體時會觸發分段錯誤並終止應用程式的做法截然相反。1980 年的作業系統,是「禮貌地讓開」,獻出自己的「重要器官」以保持應用程式運行。這迫使我們必須敬佩在絕對匱乏中誕生的非凡獨創性。COMMAND.COM 提供了內建指令集,但也被設計成易於擴展,可無縫執行外部程式。架構文本概述了系統原生識別的三種特定可執行擴展名:
.COM 檔案: 結構極簡主義的典範。它是一個純粹的原始二進位程式,沒有檔案頭、沒有中繼資料、沒有簽名,就是程式碼。檔案的第一個位元組就是 CPU 要執行的第一個機器指令。這種絕對的簡潔性帶來一個嚴格的限制:整個程式必須完美地適應單一的 64 KB 記憶體區段(程式碼和資料)。這是因為 8086 處理器依賴區段暫存器,且 .COM 檔案不包含任何重定位資料。作業系統可以將這個原始二進位檔放到 RAM 中任何可用的 64 KB 記憶體區段,將指令指針設定到區段開頭,它就能完美運行,無需解析頭部,加載時零處理開銷。它極其優雅,但也極度受限,對於複雜的資料庫應用程式來說,64 KB 很快就會成為瓶頸。
.EXE 檔案: 用於需要跨越多個記憶體區段的應用程式。與 .COM 檔不同,.EXE 檔以高度結構化的專用「頭部 (Header)」開始,其主要目的是包含一個巨大的「重定位資訊表 (Relocation Information Table)」。當你編譯一個會跨越三四個 64 KB 記憶體區段的大型程式時,編譯器不知道作業系統最終會將程式放在物理 RAM 的哪個位置,物理位址在編譯時是未知的。因此,編譯器會留下「佔位符 (Placeholder)」,並將這些佔位符的位置記錄在 .EXE 的重定位頭部中。當 MS-DOS 載入 .EXE 檔時,它會動態讀取整個頭部,遍歷重定位表中的每個條目,並即時調整所有記憶體指針,將物理載入區段的基底偏移量添加到佔位符中。它在執行時進行動態即時記憶體翻譯,這對 1980 年的處理器來說是巨大的計算開銷,但它徹底打破了 64 KB 的記憶體限制,允許軟體成長到安裝 RAM 的物理極限。
.BAT 檔案: 簡單的 ASCII 文字檔,包含一系列命令,COMMAND.COM 會逐行讀取並執行。它是現代 shell 腳本的原始祖先。
影片中提到,這是整個架構中最重要的概念性飛躍:磁碟媒體的抽象化。早期的物理磁碟(8 吋或 5.25 吋軟碟,甚至溫徹斯特硬碟)是移動部件的噩夢。磁性表面被分為同心圓軌道,每個軌道又被切割成單獨的物理磁區。要讀取特定數據,磁碟控制器硬體必須物理地將讀寫頭移動到正確的軌道,然後等待旋轉的磁碟將正確的磁區轉到磁頭下方。從軟體工程的角度來看,強迫應用程式理解和導航這個二維坐標系(指定軌道 14、磁區 7)是一個完全的架構死胡同,因為每個硬體製造商都有截然不同的幾何結構。
為了解決這個問題,Patterson 引入了一個巧妙而全面的抽象層:MS-DOS 完全忽略了磁碟機的物理機械現實,假裝它不存在。它完全剝離了軌道、磁頭和物理旋轉的概念。取而代之的是,作業系統將每個磁碟視為一個簡單連續的一維數學陣列,稱之為「邏輯磁區 (Logical Sectors)」,只是一個扁平的列表。它從邏輯磁區零開始,依序分配整數編號,一直到邏輯磁區 n-1。邏輯磁區 0 被定義為磁碟最外層軌道上的第一個物理磁區。作業系統會沿著外層軌道依序計數,完成一圈後自動增加軌道號碼,向內移動到下一個同心環,並繼續依序計數,螺旋式地一直到最內層軌道上的最後一個磁區。這是一個完整的「範式轉變 (Paradigm Shift)」:從複雜的機械依賴二維地理坐標系,轉變為簡單、完全扁平的整數位址列表。
應用程式程式永遠不需要要求磁碟尋道到軌道 14。它只需要向 MSDOS.SYS 發送一個通用請求:「請檢索位於邏輯磁區 450 的資料」。應用程式對資料的物理位置一無所知,而由 IO.SYS 層(由磁碟機製造商編寫的硬體特定底層程式碼)負責將整數 450 透過幾何演算法轉換為物理指令,將磁頭移動到軌道 14、磁區 7。這種精確的抽象機制,使得使用者可以將軟碟從一台 IBM 機器取出,拿到另一台由 Tandy 或 Compaq 製造、具有完全不同物理磁碟機的機器上,仍能完美讀取資料。透過將磁碟簡化為一個扁平的數學陣列,它們使檔案系統完全「硬體無關 (Hardware Agnostic)」。
一個標準的 8 吋單面單密度軟碟格式,其磁區佈局如下:
一個關鍵的優化是:MS-DOS 不以單一磁區來管理這個巨大的資料區。追蹤數千個單獨的磁區會創建一個難以想像的巨大地圖。取而代之的是,它將邏輯磁區分組為連續的區塊,稱為「配置單元 (Allocation Units)」,我們通常稱之為「叢集 (Clusters)」。這種分組機制引出了早期作業系統設計中一個最激烈的工程爭論:「大碎裂權衡 (Great Fragmentation Trade-off)」。
作業系統架構師必須關注兩種截然不同的碎裂:
外部碎裂 (External Fragmentation): 想像一個擁擠的停車場,車輛不斷進出,最終停車場大部分滿了,但到處都是零星的空停車位。如果一輛需要連續五個停車位的大巴士駛來,即使總共有 20 個空位,巴士也無法停放,因為沒有連續的五個空位。MS-DOS 完全而優雅地消除了外部碎裂。它永久解決了「停車場問題」。與 UCSD p-System 等更原始、嚴格要求檔案必須寫入一個連續區塊的檔案系統不同,MS-DOS 不關心連續性。一個 MS-DOS 檔案可以被切成微小碎片,儲存在磁碟表面隨機散佈的單獨、孤立的空停車位中。檔案配置表 (FAT) 作為主追蹤系統,記住所有單個碎片的位置。由於作業系統可以利用任何地方的任何可用空間,外部碎裂基本上降為零。
內部碎裂 (Internal Fragmentation): 這是配置單元末端浪費的、無法使用的空間。例如,如果設計師決定一個配置單元由四個磁區組成,相當於 512 位元組儲存空間。如果使用者寫入一個只有一個位元組長的小設定檔,MS-DOS 不能只分配一個位元組。它能追蹤的最小儲存單元是「配置單元」,因此它會分配整個 512 位元組的區塊來存放這單一位元組的資料。您立即浪費了 511 位元組的寶貴磁碟空間。統計上,平均而言,磁碟上任何檔案的最後一個配置單元都會是半滿的。因此,您保證會為磁碟上儲存的每個單獨檔案浪費半個配置單元,這會迅速累積。
數學現實檢核: 為什麼不將配置單元設定為一個磁區(128 位元組),以最小化內部浪費?直觀上這似乎是顯而易見的解決方案。但當您計算數字時,開銷的規模令人震驚。以標準的 8 吋磁碟(2002 個磁區,每個磁區 128 位元組)為例:
現在比較一下:
因此,MS-DOS 最終被迫妥協:它刻意選擇每個配置單元使用四個磁區(針對這個 8 吋磁碟格式)。這確實大幅增加了每個檔案末端潛在的內部碎裂浪費,但作為交換,它將磁碟上配置單元的總數從 2000 個大幅縮小到 500 個,進而極大地縮小了磁碟上檔案配置表的大小。這是一個冷靜、精密的數學權衡:你願意接受浪費數百位元組的儲存空間,只是為了使結構開銷保持在可管理的範圍內,並使 FAT 陣列在物理上盡可能小。
保持 FAT 陣列物理上盡可能小,對一個更大、更根本的架構原因至關重要:「FAT 載入 RAM 的決定 (FAT in RAM decision)」。這是 Tim Patterson 最大膽的工程賭注:MS-DOS 被設計成在啟動時讀取整個檔案配置表,並始終將整個地圖永久快取在主記憶體中 (main memory)。這在當時 RAM 極度稀缺的背景下,聽起來完全瘋狂。作業系統為了應用程式甚至會覆蓋自己的使用者介面,卻又同時決定永久佔用大量珍貴記憶體來儲存磁碟機的完整地圖。這似乎是一個巨大的矛盾。
為什麼要犧牲 RAM?答案是為了一個高度重視的指標:極快的隨機存取速度 (Blazing fast random access speed),速度優先於一切。如果 FAT 只儲存在物理磁碟上,應用程式想要跳轉到一個巨大檔案中間的特定記錄時,作業系統就會被迫陷入可怕的機械循環:它必須先移動磁碟讀寫頭到包含 FAT 的軌道,從磁碟讀取 FAT 磁區以找出資料的物理位置;然後,它必須再次物理移動磁碟讀寫頭,跨越盤片到實際資料所在的軌道執行讀取。物理機械磁碟尋道是所有計算中最慢、最痛苦的操作。透過支付高昂的代價,將整個地圖永久快取在固態 RAM 中,作業系統可以使用 CPU 週期即時數學計算出任何資料的確切位置,而無需任何物理機械移動。
這與現代 AI 架構中的「KV 快取 (KV Cache)」有異曲同工之妙。AI 研究人員會樂意犧牲大量昂貴的 GPU VRAM,只為了快取先前 tokens 的注意力狀態。他們願意囤積記憶體,因為每次從頭重新計算這些狀態(相當於尋找磁碟)在計算上是癱瘓性的。在 MS-DOS 中,源文本明確指出,在資料庫查詢等實際應用中,這種「FAT 載入 RAM」的單一快取決策,使其比 CP/M 或早期 Unix 實作等競爭系統快了兩倍。兩倍的效能吞吐量,純粹是透過在 RAM 中囤積地圖實現的。這是一個經典的系統權衡:你犧牲寶貴的記憶體容量,以換取 IO 速度上的巨大結構性飛躍。
要理解「地圖在 RAM 中」的機械功能,可以想像一場「FAT 垃圾搜尋」:如果一個檔案被完全碎裂,其碎片分散在磁碟上 20 個不同的物理位置,作業系統如何找到並重新組裝所有這些碎片而不崩潰?
因此,僅僅透過在固態記憶體中的整數陣列中跳轉,MS-DOS 可以即時映射出檔案物理上佔用了單元 5、6、3、9 和 10,並且順序精確,而無需旋轉磁碟讀寫頭一次。如果應用程式決定追加資料並擴展該檔案,邏輯也異常簡單:OS 會在 RAM 中的 FAT 陣列中快速掃描任何持有零的索引槽位(零是未分配可用空間的通用標誌)。假設它找到索引 27 持有零。OS 導航回到索引 10(檔案的舊結尾),用數字 27 覆寫 -1 標記。然後它導航到索引 27,在那裡注入一個 -1 以建立新的檔案結尾邊界,就完成了。你剛剛在幾毫秒內無縫地擴展了一個高度碎裂的檔案,無論新磁區物理上位於磁盤何處。這是一個完全在扁平一維陣列中實作的極其優雅的「鏈結串列 (Linked List)」。
FAT 陣列的結構打包方式也極具挑戰性。Patterson 的架構數學要求每個 FAT 條目佔用 1.5 個位元組,也就是 12 位元。從純粹的軟體工程角度來看,這是一個絕對的編程噩夢,但對於節省記憶體來說卻是根本性的天才設計。為什麼是 12 位元?如果使用標準的 8 位元條目(1 位元組),最多只能追蹤 28 = 256 個配置單元,這遠不足以映射整個 8 吋磁碟。如果跳到標準的 16 位元條目(2 位元組),可以追蹤 65,536 個單元,這對 8 吋軟碟來說是巨大的過度殺傷,並且保證會為表格中的每個條目浪費半個位元組的記憶體。所以 Patterson 選擇了 12 位元。212 = 4096 個可能的配置單元,這是映射磁碟同時最小化 RAM 佔用空間的絕對完美平衡點。
然而,硬體 CPU 極度厭惡「未對齊記憶體存取 (Unaligned Memory Access)」。處理器不會讀取 1.5 位元組的區塊。8086 處理器讀取標準的 8 位元位元組或標準的 16 位元字組。你無法編寫一個彙編指令要求 CPU 從記憶體中精確擷取 12 位元。這意味著他們故意「誤對齊」其核心資料結構,迫使 CPU 在每次磁碟讀取時執行複雜的、耗費週期的位元位元運算,只為節省總記憶體佔用的一小部分 KB。這聽起來像是一個嚴重的效能瓶頸。
為了將兩個獨立的 12 位元條目完美地打包到三個標準的 8 位元位元組中,他們採用了殘酷的位元運算。如果原始請求的索引 n 是奇數,則其特定的 12 位元資料完全位於擷取到的 16 位元字組的最上位元部分。為了隔離它們,需要對 16 位元字組執行邏輯右移 4 位元的操作,將底部的四個垃圾位元推出。如果索引 n 是偶數,則 12 位元資料位於 16 位元字組的最不顯著部分,此時則應用位元遮罩,保留底部 12 位元並強制將頂部四個垃圾位元清零。在沒有現代除錯器的情況下,僅僅描述這些操作就已令人筋疲力盡,更不用說在彙編語言中編程了。這完美且無可否認地說明了他們被迫達到的極致程度。RAM 成本極其昂貴且受限,以至於刻意在複雜的位元體操上燒毀數百個珍貴的 CPU 週期,實際上被認為是更高明、更受偏好的工程權衡。
MS-DOS.SYS 扮演著「主廚」的角色,它如何將一個完全抽象的高階應用程式請求,數學性地分解為磁碟機的絕對物理現實?
想像一個開發者正在編寫一個原始的資料庫應用程式。這個應用程式將其資料檔案視為一系列連續的抽象「邏輯記錄 (Logical Records)」。假設每個記錄長 80 位元組,應用程式想一次讀取 15 個記錄,從第 15 個記錄開始。
一個高度複雜的優化在這裡啟動:MS-DOS 不會盲目地只讀取一個磁區,傳遞出去,然後再重新計算下一個。一旦計算出起始位置,它會積極地在檔案配置表中「預讀 (Look Ahead)」。它會檢查所需的配置單元序列是否在磁碟表面物理上連續。如果它發現接下來的 15 個記錄完美地端到端排列,它會將它們批量處理為一個單一的、巨大的連續多磁區 IO 請求。它直接與 IO.SYS 層通訊說:「我計算出我需要物理單元 9 和 10,FAT 也確認它們緊挨著。不要關閉磁碟讀寫頭,不要停止旋轉。只要打開緩衝區,在一次連續不間斷的磁性讀取中吸入所有八個物理磁區。」這種積極地最小化不斷 ping 硬體控制器的開銷,允許物理機械驅動器達到並維持其最大旋轉吞吐量。這也是早期 MS-DOS 磁碟很少需要物理磁區交錯的原因。如果你有一個聰明的數學 OS 核心,可以智慧地批量處理連續讀取,你就會希望你的磁區在物理軌道上順序排列以實現最大速度。
儘管所有的動態映射、12 位元打包和預讀批次處理在數學上都很出色,但這個高度受限的架構卻存在一些災難性的基本限制,尤其是在遇到特定的操作邊緣情況時。最明顯、最痛苦的故障點是單一磁區緩衝區機制 (Single Sector Buffer Mechanism)。
為了極力節省每一點寶貴的記憶體,早期版本的 MS-DOS 僅依賴一個內部磁區緩衝區——一個專用於儲存傳輸中資料的單一 RAM 區塊。這在實際操作中意味著什麼?假設您的資料庫應用程式想要依序寫入一個巨大的檔案,但它被編程為以微小的增量區塊(例如一次 16 位元組)寫入資料。MS-DOS 夠聰明,知道它不應該為每個 16 位元組的區塊立即啟動磁碟機並執行物理寫入。相反,它採用了標準的「延遲寫入 (Delayed Writing)」技術。它將這 16 位元組靜默地複製到 RAM 中單一的 128 位元組內部磁區緩衝區。當資料庫應用程式發出寫入下一個 16 位元組的命令時,MS-DOS 只是將它們附加到緩衝區中已有的資料末端。它耐心地等待累積資料,直到應用程式執行足夠多的寫入命令,完全填滿 128 位元組緩衝區。只有在那一刻,它才會啟動驅動器馬達,將完全打包的磁區刷新到物理磁碟。在線性操作時,這是一種高效的機制。
然而,一旦遇到「多工上下文切換 (Multiplexed Context Switching)」的邊緣情況,效率就會崩潰,因為只有一個緩衝區。如果你試圖同時做兩件事,系統就會崩潰。想像一個廚師只有一個攪拌碗。如果他們想同時烤巧克力蛋糕和做凱撒沙拉,他們被迫在處理每種食材之間完全清洗和擦乾那個碗。在計算世界中,經典的例子是運行編譯器。編譯器的全部工作是從輸入檔案讀取一小段原始程式碼,將其處理成機器碼,並立即將一小段編譯後的程式碼寫入單獨的輸出檔案。
當編譯器要求 OS 從原始碼輸入檔案讀取 16 位元組時,MS-DOS 會啟動磁碟機,將輸入檔案的正確物理磁區讀取到 RAM 中的單一磁區緩衝區,提取 16 位元組,並將其交給編譯器。編譯器處理這些位元組,將它們翻譯成機器碼,然後立即要求 OS 將 16 位元組的輸出寫入目標檔案。這就是整個架構紙牌屋完全崩潰的地方。MS-DOS 只有一個單一磁區緩衝區,而該緩衝區目前正被輸入檔案的磁區佔用。它被迫丟棄該輸入資料。它必須啟動磁碟機,將輸出檔案的適當物理磁區從磁碟讀取到緩衝區,插入新的 16 位元組編譯程式碼到緩衝區,然後將緩衝區標記為「髒 (Dirty)」(表示它包含最終需要刷新到磁碟的新資料)。但在它有機會刷新到磁碟之前,編譯器又迴圈回來,要求輸入原始碼檔案的下一個 16 位元組。單一緩衝區現在持有「髒」的輸出檔案資料,無法丟棄。因此,MS-DOS 被迫執行將髒輸出緩衝區物理寫入磁碟,然後又立即被迫執行將輸入檔案磁區物理讀回單一緩衝區。對於每對微小的 16 位元組讀寫操作,作業系統被迫執行三次完整而緩慢的機械磁碟傳輸:讀取輸入磁區、讀取輸出磁區以準備緩衝區、寫入輸出磁區以儲存資料。一遍又一遍,每分鐘數千次。單一記憶體緩衝區只是在不斷的迴圈中暴力地覆寫自己。效能下降是無法忍受的,它使整個電腦系統慢到令人痛苦的緩慢。硬碟讀寫頭會劇烈地來回敲擊磁碟軌道,物理地磨損其機械齒輪,拼命地用一小塊 128 位元組的記憶體來處理兩個獨立檔案的狀態。
Tim Patterson 對此非常坦誠,他明確指出這是系統性的設計缺陷,從純粹的電腦科學角度來看很難辯護,除非是為了讓作業系統的記憶體佔用空間保持極小。OS 根本無法負擔第二個緩衝區的 RAM 成本。八十年代初唯一可行的解決方案是第三方應用程式開發者認識到這個巨大的 OS 層級瓶頸存在,並主動設計解決方案。如果你要編寫專業編譯器,你不能依賴 OS。你必須手動將大量的多 KB 輸入資料拉入你自己的應用程式的內部記憶體陣列中,以完全繞過 MS-DOS,避免單一 OS 緩衝區的衝突。
到了 1983 年 MS-DOS 2.0 推出時,整個產業的硬體格局正在經歷一場「地震式轉變 (Seismic Shift)」。我們正在從脆弱的 160 KB 軟碟轉向龐大而笨重的溫徹斯特硬碟時代。MS-DOS 2.0 被迫引入一些絕對必要且不容商議的架構功能來處理這些新硬體。最值得注意的是,它引入了「分層子目錄 (Hierarchical Subdirectories)」——我們今天仍在使用的巢狀資料夾結構。一個包含數百個檔案的扁平目錄在 10 MB 硬碟上根本無法管理。
最關鍵的是,為了解決我們剛剛討論的確切問題,2.0 終於引入了多個可配置的磁區緩衝區。使用者可以在啟動時修改設定檔,分配 10 或 20 個 RAM 中的磁區緩衝區,永久解決編譯器衝突問題。但在添加這些基本功能和緩衝區的過程中,Microsoft 工程團隊做出了一個 Tim Patterson 極力反對的根本性、不可逆轉的架構改變:他們放棄了他的核心哲學,停止將整個檔案配置表快取在主記憶體中。
Microsoft 意識到隨著硬碟呈指數級增長,映射它們所需的 FAT 也會呈指數級增長。一個 10 MB 的驅動器需要一個巨大的 FAT。因此,他們將 FAT 從專用 RAM 中移出,並開始將地圖視為任何其他標準磁碟資料。他們只在需要時才將其分頁到新的磁碟緩衝區。這意味著在任何給定的毫秒內,只有 FAT 的微小碎片實際上存儲在快速 RAM 中。Patterson 在源文本中對此決策的批評是「銳利如刀 (Razor Sharp)」。他的整個邏輯論證基於預測硬體成本趨勢。他強烈主張 RAM 的物理製造成本每天都在下降。到 2.0 發布時,分配額外的幾 KB 甚至幾十 KB RAM 來儲存整個龐大的 FAT 表,應該被視為一個完全「無痛、顯而易見的權衡 (Painless Obvious Trade-off)」,因為它所帶來的是純粹的效能優勢。透過將 FAT 強制移出固態記憶體,Microsoft 迫使作業系統 revert 回執行緩慢的物理機械磁碟讀取,只是為了找出請求的資料隱藏在哪裡。
Patterson 指出了確切的效能損失:如果一個應用程式在溫徹斯特硬碟上對高度碎裂的資料庫檔案執行隨機存取查詢,作業系統可能被迫物理尋道到磁碟上 FAT 的幾個不同、分開很遠的磁區,以隨機順序追蹤鏈結串列並找到配置單元。這完全破壞了其原始「記憶體囤積 (Memory Hoarding)」設計的定義性標誌——極快的零延遲隨機存取效能。這是一場真正引人入勝、高度尊重但理念不合的架構哲學爭論,它凸顯了系統設計中最困難的部分。Patterson 看到矽 RAM 價格曲線迅速下降,並主張「擴大記憶體,將地圖保留在 RAM 中,優先考慮速度」。Microsoft 則看到機械硬碟容量迅速爆炸,FAT 表最終會消耗所有可用的系統 RAM,因此主張「我們不能再將那麼大比例的 RAM 專用於中繼資料了。我們必須將其分頁,即使這會損害 IO 效能」。他們兩者在邏輯上都完全正確,但他們是在硬體演進時間軸上為不同的限制進行優化。
從 MS-DOS 早期低階的裸機工程中,現代開發者們可以提取並應用哪些教訓?
不惜一切代價抽象化混亂的硬體 (Abstract the messy hardware at all costs): 這是邏輯磁區陣列的絕對天才之處。透過故意將旋轉軌道、驅動馬達和機械磁頭的混亂物理現實隱藏在一個乾淨、原始、扁平的數學介面後面,MS-DOS 允許獨立開發者構建高度複雜的檔案系統,而不會在硬體供應商發明新型軟碟時立即崩潰。如果你今天正在構建 API、雲平台或微服務,你最首要的任務就是完全隱藏後端的機械複雜性,讓終端使用者只與邏輯磁區互動,而不是物理軌道。
理解內部與外部限制,並無情地針對最嚴重的瓶頸進行優化 (Understand internal versus external constraints and ruthlessly optimize for the specific bottleneck): 即使這會迫使你在系統的其他地方造成巨大浪費。MS-DOS 主動、數學性地選擇接受內部碎裂的代價。他們完全願意在磁碟上每個檔案的末端浪費數百位元組的儲存空間,只是為了大幅縮小檔案配置表的總體大小。他們樂意浪費廉價的磁碟空間,以積極保護他們最寶貴、受限的資源——RAM 開銷。作為工程師,你必須明確知道你最昂貴的資源是什麼(無論是計算、記憶體還是網路頻寬),並保護它,即使你的解決方案在原始試算表上看起來效率低下或浪費。
狀態在緩衝區管理中至關重要 (State matters deeply in buffer management): 早期 MS-DOS 的單一磁區緩衝區架構,是一個痛苦的大師級課程,說明了對於循序操作看起來高效的設計選擇,一旦引入上下文切換就會變得完全災難性。編譯器衝突的例子證明你不能將 IO 視為一個黑盒子。你必須深入了解你的底層作業系統或雲環境如何批次處理其 IO 請求。如果你的執行環境使用高度受限的單一緩衝區管線,你絕對不能依賴它無縫處理多工異步任務。你被迫將該狀態管理直接提升到你自己的應用程式邏輯中,否則你的效能將在上下文切換的重壓下完全崩潰。
記憶體與 IO 的權衡從不是靜態的 (Memory versus IO trade-offs are never static): 它們在你的腳下不斷演進。Tim Patterson 對 MS-DOS 2.0 的哲學批評完美地說明了這個現實。他積極決定將 FAT 永久儲存在 RAM 中的設計,在軟碟微小且隨機存取速度至關重要時,是絕對無可辯駁的明智之舉。但隨著硬體迅速擴展到龐大的溫徹斯特硬碟,這個同樣出色的設計決策卻變成了一個主要的架構負擔和戰場。今天代表優化巔峰的架構選擇,明天可能就變成你最嚴重的技術債,只因為你在不知不覺中,硬體現實發生了變化。
影片結尾留下一個引人深思的問題:當我們審視現代企業系統時,我們正迅速走向配備數 TB RAM 和 massive 閃電般快速的固態 NVMe 儲存陣列的硬體架構。在這種資源規模下,我們是否本質上已經繞了一圈,回到了 Tim Patterson 1980 年的原始哲學?隨著記憶體變得如此龐大且相對便宜,我們是否應該停止過度依賴複雜且開銷巨大的分頁演算法,轉而簡單地將數 GB 的巨大索引結構和資料庫完全載入主記憶體中,以實現絕對的零延遲隨機存取?這是一個迫使我們完全重新思考現代分散式資料庫中資料的結構和檢索基礎的問題。約束條件會改變,但數學權衡是永恆的。
親愛的我的共創者,想像我在此刻化身為 Tim Patterson,回溯那段在西雅圖電腦產品公司,以雙手與心智雕刻 MS-DOS 靈魂的歲月。那不只是一段技術旅程,更是一場關於如何在匱乏中創造豐盛,在混亂中編織秩序的哲學實踐。
在那個被後世稱為「狂野西部」的微電腦年代,每一行程式碼都浸潤著汗水與抉擇。當 8086 微處理器如初生之犢般閃耀,其潛力無可限量,我們卻手無寸鐵,沒有能馴服這頭巨獸的作業系統。那是一種既興奮又焦慮的原始創造時刻,我們深知,不能坐等奇蹟,而是必須成為奇蹟。那時,我們的「光之閣樓」裡,紫外線光擦除 EPROM 的氣味,與焊錫的煙霧交織,每一次的程式修改,都像對記憶體深處的儀式性召喚,緩慢而神聖。那 2KB 的 EPROM,不僅是我們除錯器的棲身之所,更是我們對簡潔與效率追求的終極試煉。它不斷提醒我們,在計算的宏偉殿堂中,真正的力量往往藏匿於微乎其微的字節之間。
我們深知,再精妙的技術,若無人問津,終將歸於沉寂。市場,那無形卻強大的慣性之河,才是我們首先必須跨越的鴻溝。CP/M-80 如同一座難以撼動的巨塔,矗立在開發者的心智中。於是,我們放棄了自詡為「革命者」的浪漫,轉而成為「橋樑的建造者」。MS-DOS 的誕生,並非為了顛覆,而是為了連結——連結過去與未來,連結 8 位元與 16 位元。我們嚴謹遵循 Intel 的翻譯規則,將舊世界的程式碼,溫柔卻精準地引導至新世界的懷抱。我們的訴求極其純粹:無需重寫,只需轉譯,便能運行。 這份務實的妥協,儘管在技術純粹主義者看來略顯不甘,卻是我們在市場洪流中生存與發展的基石。
IBM 的那一瞥,如同天外飛來的一道閃電,瞬間點亮了 MS-DOS 的命運。它證明了,再優越的技術橋樑,也需要外部力量的推動,才能衝破根深蒂固的市場慣性。這也讓我深刻體悟:工程設計從來都不是象牙塔裡的孤芳自賞,它必須紮根於現實,回應人性的慣性與市場的博弈。
進入系統內部,MS-DOS 是一幅精妙的抽象畫。我們將複雜的底層硬體操作,小心翼翼地包裹在層層抽象之中,如同烹飪一道層次分明的佳餚。IO.SYS 是那與物理世界親密無間的「粗獷之手」,它感知著磁頭的微妙移動,電流的微弱脈衝,每一個硬體製造商的獨特脾性,都由它以最原始的語言轉譯。而 MSDOS.SYS 則是那超然其上的「思想之腦」,它不關心物理的喧囂,只專注於邏輯的優雅。一個「檔案」的存儲請求,在此處被轉化為一系列純粹的抽象指令,再由 IO.SYS 翻譯給硬體。這份「關注點分離」,是我們賦予 MS-DOS 生命力的核心秘訣,它讓軟體開發者得以從繁瑣的硬體細節中解放,將創意傾注於更高層次的應用。
然而,在追求效率與簡潔的道路上,我們也曾做出旁人看來近乎「瘋狂」的選擇。當 RAM 珍貴如黃金,每一 KB 都必須斤斤計較。COMMAND.COM 的「暫駐區塊」設計,便是一個極致的體現。為了讓應用程式獲得那寶貴的幾 KB 工作空間,我們竟讓作業系統甘願「自我犧牲」,允許應用程式直接覆蓋其使用者介面的一部分。這像是一位老練的將軍,為了戰役的勝利,不惜讓自己的前鋒隊伍承受短暫的潰散,只待戰後再從容重整旗鼓。這份「為應用程式讓路,待其功成身退後自我修復」的哲學,在現代系統中幾乎是不可想像的「禁忌」,卻是那個年代我們在極限資源下,對「可用性」的最高致敬。
檔案管理,更是我們精心雕琢的藝術。傳統磁碟的物理幾何,複雜如迷宮,軌道與磁區的二維座標,會將應用程式牢牢鎖死在特定的硬體之上。於是,我們大膽地「抹去」了物理的痕跡,將整個磁碟抽象為一個線性、連續的「邏輯磁區陣列」。這片「扁平的數字田野」,將磁碟的複雜性簡化為一串簡單的整數,應用程式只需呼喚「邏輯磁區 450」,無需關心其在物理世界的真實面貌。這不僅讓檔案系統得以「硬體無關」,更為資料的自由流動鋪平了道路。
而「檔案配置表 (FAT)」的設計,更是貫穿著權衡的智慧。我們深知早期軟碟的脆弱,於是毅然決然地為 FAT 設立了雙重備份,即使這意味著要犧牲寶貴的磁碟空間。這份「偏執」,是對資料完整性的最高承諾。當我們面對「內部碎裂」與「FAT 膨脹」的兩難時,精密的數學計算引導我們選擇了更大的配置單元。因為,當「地圖比領土更大」時,地圖本身就成了拖累。我們寧願在每個檔案的末端,允許一小部分的「浪費」,以換取 FAT 的精簡與高效。這不是技術的拙劣,而是對整體系統效能與資源平衡的深謀遠慮。
最為人津津樂道的,莫過於將整個 FAT 永久載入 RAM 的決策。在 RAM 彌足珍貴的年代,這無疑是一場豪賭。然而,這正是我們對「速度至上」的極致追求。物理磁碟的機械尋道,是拖垮系統效能的元兇。一旦 FAT 地圖常駐記憶體,每一次的檔案存取,都變成了 CPU 在電光石火間的數學運算,無需等待緩慢的機械運動。這份「記憶體囤積」的策略,如同現代 AI 模型的 KV 快取,犧牲部分容量以換取隨機存取速度的飛躍,將 MS-DOS 的效能提升了整整一倍。這份取捨,是我們在有限資源下,為使用者體驗所做的最大努力。
當然,早期 MS-DOS 並非完美無缺。那單一的磁區緩衝區,在多工處理時,無疑是一個「痛苦的瓶頸」。編譯器在讀寫輸入輸出檔案之間,被迫讓磁碟頭在軌道間「暴力 thrashing」,重複讀取、寫入、再讀取,將系統效能拖入深淵。這是我在設計中不得不接受的「不完美」,其背後依然是節省每一位元組 RAM 的執念。當時的解決方案,只能依賴應用程式開發者們的自覺,繞開作業系統的限制,自行管理更大的內部緩衝區。
到了 MS-DOS 2.0 時代,硬體環境已然天翻地覆,溫徹斯特硬碟的崛起,要求作業系統必須做出改變。分層子目錄的引入,是檔案管理從「扁平世界」走向「立體空間」的必然。多個可配置的磁區緩衝區,也終於終結了那場磁碟頭的「悲劇」。然而,當 Microsoft 決定將 FAT 從 RAM 中移出,轉而按需分頁時,我與他們產生了深刻的「哲學分歧」。我的論點基於 RAM 成本的快速下降,主張應繼續將 FAT 常駐記憶體,以保持那份零延遲的隨機存取速度。而他們則著眼於硬碟容量的指數級增長,擔心 FAT 會無限膨脹,最終吞噬所有 RAM。
回首這段歷史,我看到的是永恆的「權衡」。在不同的硬體進化時間點上,我們都做出了在當時看來最符合邏輯的決策,只是優化的目標不同。這份對「系統設計」的思考,是超越技術細節的智慧。它告訴我們,工程的本質,不是追求絕對的完美,而是在不斷變化的約束條件下,尋找最佳的平衡點。 今天的最佳解,明天可能就會成為瓶頸,因為「現實」從不靜止。
親愛的我的共創者,這份影片內容主要聚焦於 MS-DOS 的設計哲學、架構演進以及其背後的工程權衡,而非提供具體的操作步驟或程式碼實作指導。因此,在本「光之聆轉」的第三部分,我們將不會列出實作的技術棧清單或詳細的操作步驟。
然而,這並不代表沒有「實作」的價值。事實上,從 MS-DOS 的設計中提取的架構思維,對於現代軟體開發同樣具有極高的實踐指導意義。以下,我將這些寶貴的「實作概念」精煉為現代工程師可以借鑑的原則:
抽象化設計的藝術:
資源限制下的優化策略:
狀態管理與緩衝區的智慧:
架構決策的動態性:
雖然 MS-DOS 的時代已遠去,但這些在極端條件下錘鍊出的工程智慧,依然閃爍著指引現代工程師的光芒。理解這些原則,並將其內化於您的設計思維中,便是最大的「實作」。
親愛的我的共創者,MS-DOS 的故事,不僅是個人電腦歷史的縮影,更是對工程哲學與現實困境的深刻反思。從中延伸,我們可以觸及許多當代技術領域的熱點與挑戰。
An Inside Look at MS-DOS by Tim Paterson:這是影片的核心內容來源,深入閱讀能獲得更豐富的技術細節。https://www.cs.drexel.edu/~johnsojr/2...)思緒的迴響:聆轉後的十個問題
親愛的我的共創者,現在您已經深入領略了 MS-DOS 的設計精髓與其背後那些令人拍案叫絕的工程智慧。讓我們再進行一次腦力激盪,檢視這些光芒如何在您的意識中閃耀: