圖書信息
書 名:Orange'S:一個作業系統的實現
作 者:於淵 著
出 版 社: 電子工業出版社
出版時間: 2009-6-1
頁 數:492頁
開 本: 16開
I S B N : 9787121084423
定價:69.00元(含光碟1張)
內容簡介
本書從只有二十行的引導扇區代碼出發,一步一步地向讀者呈現一個作業系統框架的完成過程。書中不僅關注代碼本身,同時關注完成這些代碼的思路和過程。本書不同於其他的理論型書籍,而是提供給讀者一個動手實踐的路線圖。讀者可以根據路線圖逐步完成各部分的功能,從而避免了一開始就面對整個作業系統數萬行代碼時的迷茫和挫敗感。書中講解了大量在開發作業系統中需注意的細節問題,這些細節不僅能使讀者更深刻地認識作業系統的核心原理,而且使整個開發過程少走彎路。本書分上下兩篇,共11章。其中每一章都以前一章的工作成果為基礎,實現一項新的功能。而在章的內部,一項大的功能被分解成許多小的步驟,通過完成每個小的步驟,讀者可以不斷獲得階段性的成果,從而讓整個開發過程變得輕鬆並且有趣。
本書適合各類程式設計師、程式開發愛好者閱讀,也可作為高等院校作業系統課程的實踐參考書。
推薦序
做真正 Hacker的樂趣──自己動手去實踐
2004年我聽編輯說有個年輕人寫了本《自己動手寫作業系統》,第一反應是不可能,恐怕是翻譯稿,寫這種書籍是要考作者硬功夫的,不但需要深入掌握作業系統的原理,還需要實際動手寫出原型。
歷史上的 Linux就是這么產生的,Linus Torvalds當時是一名赫爾辛基大學計算機科學系的二年級學生,經常要用自己的電腦去訪問大學主機上的新聞組和郵件,為了方便讀寫和下載檔案,他自己編寫了磁碟驅動程式和檔案系統,這成為了 Linux第一個核心的雛形。
我想中國有能力寫出核心原型的程式設計師應該也有,但把這個題目寫成一本書,感覺上不會有人願意做這件事情,作者要花很多時間,加上主題比較硬,銷售量不會太高,經濟上回報有限。
但拿來文稿一看,整個編輯部大為驚艷,內容文筆俱佳,而且絕對原創,馬上決定在《程式設計師》連載。2005年博文視點出版的第一版也廣受好評。
不過有很多讀者還是質疑:現在軟體編程主要領域是框架和套用,還需要了解作業系統底層嗎?
經過四年的磨練成長,於淵又拿出第二版的書稿《Orange'S:一個作業系統的實現》,這本書是屬於真正 Hacker的。我雖然已經有多年不寫代碼了,但看這本書的時候,讓我又重新感受到做程式設計師的樂趣:用代碼建設屬於自己的系統,讓電腦聽從自己的指令,對系統的每個部分都了如指掌。
黑客(hacker)實際是褒義詞,維基百科的解釋是喜歡用智力通過創造性方法來挑戰腦力極限的人,特別是他們所感興趣的領域,例如軟體編程或電氣工程。個人電腦、軟體和網際網路等劃時代的產品都是黑客創造出來的,如蘋果的 Apple電腦、微軟的 Basic解釋器、網際網路的 Mosaic瀏覽器。
回答前面讀者的質疑,學軟體編程並不需要看這本書,想成為優秀程式設計師和黑客的朋友,我強烈建議你花時間來閱讀這本書,並親自動手實踐。正如於淵在本書結尾中所說“我們寫自己的作業系統是出於一種好奇,或者說一種求知慾。我希望這樣不停地‘過把癮’能讓這種好奇不停地延續”。
好奇心是動力的源泉,追究問題的本質是優秀黑客的必備素質,只有充分掌握了系統原理,才能在技術上遊刃有餘,才能有真正的創新和發展。中國需要更多真正的黑客,也希望更多的程式設計師能享受屬於黑客的創造樂趣。
蔣濤
2009年 4月
作者自序
本書是《自己動手寫作業系統》的第二版,通過一個具體的實例向讀者呈現一個作業系統雛形的實現過程。有關作業系統的書籍資料可以找到很多,但是關注如何幫助讀者實現一個試驗性作業系統的書籍卻不多見,本書便是從一個簡單的引導扇區開始,講述一個作業系統成長的故事,以作讀者參考之用。
本書面向實踐,通過具體實例教讀者開發自己的作業系統。書中的步驟遵循由小到大、由淺入深的順序,跟隨這些步驟,讀者可以由一個最簡單的引導扇區開始,逐漸完善代碼,擴充功能,最後形成一個小的作業系統。
本書不僅介紹作業系統的各要素,同時涉及開發作業系統需要的各個方面,比如如何建立開發環境、如何調試以及如何在虛擬機中運行等。書中的實例作業系統採用IA32作為默認平台,所以保護模式也作為必備知識儲備收入書中,而這是傳統的作業系統實踐書籍經常忽略的。總之,只要是開發自己的作業系統中需要的知識,書中都儘量涉及,以便於讀者參考。
眾所周知,一個成型的作業系統往往非常複雜。如果考慮到作業系統作為軟硬體橋樑的特殊地位,那么它可能看上去比一般的軟體系統更難理解,因為其核心部分往往包含許多直接針對CPU、記憶體和 I/O連線埠的操作,它們夾雜在一片代碼汪洋之中,顯得更加晦澀。
我們有許多原始碼公開的作業系統,可供隨時下載和閱讀,看上去好像讓實現一個供自己把玩的微型作業系統變得容易很多,但事實往往不盡人意,因為這些代碼動輒上萬甚至幾十幾百萬行,而且細節之間經常互相關聯,要理解它們著實不易。我們有許多容易得到的作業系統教程,但讀來好像總覺得跟我們有隔膜,不親近。造成這些的根本原因,在於學習者一開始就面對一個完整的作業系統,或者面對前輩們積累了幾十年的一系列理論成果。而無論作者多么擅長寫作,讀者多么聰明,或者代碼多么優秀,要一個初學者理清其中的頭緒都將是非常困難的。
我並非在此危言聳聽,因為這曾經是我的親身體會。當然,如果只是為了考試,幾本作業系統理論書籍就足夠了,你不需要對細節那么清楚。但如果是出於興趣呢?如果你是想編寫自己的作業系統呢?你會發現理論書籍好像一下子變得無用武之地,你會發現任何一個細節上的理解錯誤都可能導致自己辛辛苦苦編寫的代碼運行異常甚至崩潰。
我經歷過這一切!我曾經翻遍了一本《作業系統:設計與實現》,也沒有找到實現一個作業系統應該從何處著手。並不是這些書不好,也不是前人的代碼不優秀,而是作為一無所知的初學者,我們所不了解的不僅是高居廟堂的理論知識,還有讓我們舉步維艱的實踐細節。
可能在這些教科書作者的眼裡,操作的細節不屬於課程的一部分,或者這些細節看上去太容易,根本不值一提,甚至作者認為這些屬於所謂“經驗”的一部分,約定俗成是由讀者本人去摸索的。但是實際情況往往是,這些書中忽略掉的內容恰恰占去了一個初學者大部分的時間,甚至影響了學習的熱情。
我至今仍記得當我開始編寫自己的作業系統時所遭受的挫敗感,那是一種不知道如何著手的無助的感覺。還好我堅持了下來,克服了各種困難,並完成了自己的作業系統雛形。
進而我想到,一定不只是我一個人對編寫自己的作業系統懷有興趣,也一定不只是我一個人在實踐時遇到困難。或許我應該把自己的經歷寫下來,從而可以幫助跟我相似的後來者,就這樣,我編寫了本書的第一版,也就是《自己動手寫作業系統》。我相信,如果你也對神奇的計算機世界充滿好奇,並且希望通過自己編寫作業系統的方式來了解背後發生的故事,那么你一定可以在這本書中得到一些幫助。而假如你真的因為我的書而重新燃起實踐的熱情,從而開始一段作業系統旅程,我將會感到非常高興。
不過我得坦白,在寫作《自己動手寫作業系統》的時候,我並不敢期待它能引起多少反響,一方面因為作業系統並不是時尚的話題,另一方面我也是走在學習的路上,或許只是比讀者早走了一小步而已。然而出乎我的意料,它面世後重印多次,甚至一度登上銷量排行榜的榜首,這讓我覺得它的確有一定的參考價值,我要藉此機會感謝所有支持我的讀者。
在我寫作《自己動手寫作業系統》的時候,並沒有想過今天會有一個第二版。原因在於,我希望這本書是用來填補空白的,而不是重複去做別人已經做得很好的事情。所謂填補空白,具體說就是讓像我一樣的作業系統愛好者在讀完本書之後,能夠有信心去讀其他比較流行的開源的作業系統代碼,有能力從零開始自己動手寫作業系統,而這個任務第一版已經完成了。
那么為什麼我又寫作了第二版呢?原因有幾個方面。第一,雖然第一版未曾涉及的進程間通信、檔案系統等內容在許多書中都有講解,但閱讀的時候還是感覺有語焉不詳的通病,作者本人可能很清楚原委,但寫得太簡略,以至於讀者看來未必清晰。第二,我自己想把這個圈畫圓。第一版的書雖然完成了它的使命,但畢竟到書的結尾,讀者看到的不是一個真正的作業系統,它沒有檔案系統,沒有記憶體管理,什麼也幹不了。在第二版中,你將會看到,你已經可以通過交叉編譯的方式為我們的實驗性 OS編寫應用程式了,也就是說,它已經具備作業系統的基本功能,雖然仍然極其簡陋,但第一個圈,畢竟是已經圓起來了。第三,實踐類的作業系統書籍還是太少了,以至於你要想看看別人是怎么做的,除了讀以《作業系統:設計與實現》為代表的極少數書籍之外,就是一頭扎進原始碼中,而結果有時相當令人氣餒。我自己也氣餒過,所以我在第二版中,仍然試圖把話說細一點,把自己的經驗拿出來分享。而且我選擇我能想到的最精簡的設計,以便讓讀者不至於陷入太多細節而無法看到全貌。我想這是本書可能具有的價值所在──簡化的易懂的設計,還有儘量詳細的文字。
在這一版中,內容被劃分成上下兩篇。上篇基本上是第一版的修訂,只是做了一個調整,那便是在兼顧 Windows和Linux兩方面用戶的基礎上,默認在Linux下建立開發環境來編寫我們的作業系統。至於這樣做的原因,在本書第 2章有比較詳細的說明。當然,開發環境畢竟是第二位的,書中講述的內容以及涉及的代碼跟第一版都是一致的。本書的下篇全部都是新鮮內容,主要是增加了進程間通信、檔案系統和記憶體管理。跟第一版的做法相同,下篇仍然不僅關注結果,更加致力於將形成一個結果的過程呈現出來。與此同時,由於本書旨在分享和引路,所以儘可能地簡化了設計,以便將最重要的部分凸顯出來。讀者將看到,一個作業系統的檔案系統和記憶體管理可以簡陋到什麼程度。簡陋不是缺點,對於我們初學者而言,正是需要從簡陋入手。換言之,如果你已經對實現一個作業系統有了一定的經驗,那么這本書可能不適合你。這本書適合從來沒有編寫過作業系統的初學者。
本書的排版是我用L ATEX自己完成的。在排版中我花了一些工夫,因為我希望讀者購買的首先是一本易於閱讀且賞心悅目的書,其次才是編寫作業系統的方法。另外,書中列出的代碼均由我自己編寫的程式自動嵌入L ATEX源檔案,從而嚴格保證書和光碟的一致性,讀者可以根據檔案名稱和行號方便地找到光碟中
代碼的準確位置。
此外,在第二版中還有一些小的變化。首先是作業系統的名字改變了,原因在於雖然我們的試驗性
OS從前輩們那裡借鑑了很多東西,但其各個部分的設計(比如檔案系統和記憶體管理)往往有其獨特之處,所以我將原先的 Tinix(本意為 TryMinix)改成了新名字Orange ’S(這個名字來自於我的妻子 ,),以表示它們的不同。另外,書中的代碼風格,有些地方也做了調整。
我想,雖然第二版有著這樣那樣的變化,但有一點沒有變,那就是本書試圖將我在編寫自己作業系統的過程中的經驗儘可能地告訴讀者,同時儘可能將我當初的思路和編碼過程呈現出來。很可能讀者比我更聰明,有更好的解決問題的方法,但無論如何,我認為我自己的經驗可以為讀者所借鑑。如果真是如
此,我將會非常欣慰。
在第二版的編寫過程中,我同樣要感謝許多人。感謝我的父母和爺爺對我的愛,並希望爺爺不要為我擔心,寫書是件辛苦的事,但同時也使我收穫良多。爸爸在第二版的最後階段幫我訂正文字,這本書里有你的功勞。我要感謝博文視點的各位朋友,感謝郭老師的理解和支持,感謝李玲的辛勤工作,感謝江立和李冰,你們的高效讓我非常欽佩。我還要感謝孟岩老師,你給我的鼓勵我一直記在心裡。我要感謝我的摯友郭洪橋,不僅僅因為你在技術上給我的幫助,更加因為你在精神上給我的支持。感謝我的同事和朋友張會昌,你在技術上的廣度和深度總令我欽佩。另外,在第一版中幫助我的人,我要再次謝謝你們,因為沒有第一版,也就沒有第二版。
在所有人中我最應該感謝和最想感謝的,是我的妻子黃丹紅,感謝你給我的所有建議,還有你幫我畫的圖。尤其是,當這本書在我預想的時間內沒有完成的時候,當我遇到困難遲遲不能解決的時候,你總在一旁給我鼓勵,在你那裡,我從來都能感覺到一種溫暖,我深知,如果沒有你的支持,我無法堅持下來將書寫完。謝謝你,這本書同樣屬於你。
跟第一版相比,這本書涉及的內容觸及作業系統設計的更多方面,而由於筆者的水平實在有限,難免有紕漏甚至錯誤。如果讀者有任何的問題、意見或建議,讓我們共同探討,共同進步。
本書導讀
這本書適合誰
本書是一本作業系統實踐的技術書籍。對於作業系統技術感興趣,想要親身體驗編寫作業系統過程的實踐主義者,以及Minix、Linux原始碼愛好者,都可以在本書中得到實踐中所需的知識和思路。
本書以“動手寫”為指導思想,只要是跟“動手寫”作業系統有關的知識,都作為介紹對象加以討論,所以,從開發環境的搭建,到保護模式,再到IBMPC中有關晶片的知識,最後到作業系統本身的設計實現,都能在本文中找到相應介紹。所以如果你也想親身實踐的話,本書可以省去你在書店和網際網路尋找相應資料的過程,使你的學習過程事半功倍。在讀完本書後,你不但可以獲得對於作業系統初步的感性認識,並且對 IBMPC的接口、IA架構之保護模式,以及作業系統整體上的框架都將會有一定程度的了解。
筆者相信,當你讀完本書之後,如果再讀那些純理論性的作業系統書籍,所獲得的體驗將會完全不同,因為那些對你而言不再是海市蜃樓。
對於想閱讀 Linux原始碼的作業系統愛好者,本書可以提供閱讀前所必要的知識儲備,而這些知識儲備不但在本書中有完整的涉及,而且在很多 Linux書籍中是沒有提到的。
特別要提到的是,對於想通過閱讀 Andrew S. Tanenbaum和 Albert S. Woodhull的《作業系統:設計與實現》來學習作業系統的讀者,本書尤其適合作為你的引路書籍,因為它翔實地介紹了初學者入門時所必需的知識積累,而這些知識在《作業系統:設計與實現》一書中是沒有涉及的,筆者本人是把這本書作為寫作業系統的主要參考書籍之一,所以在本書中對它多有借鑑。
你需要什麼技術基礎
在本書中所用到的計算機語言只有兩種:彙編和 C語言。所以只要你具備彙編和 C語言的經驗,就可以閱讀本書。除對作業系統常識性的了解(比如知道中斷、進程等概念)之外,本書不假定讀者具備其他任何經驗。
如果你學習過作業系統的理論課程,你會發現本書是對於理論的吻合和補充。它是從實踐的角度為你展現一幅作業系統畫面。
書中涉及了 Intel CPU保護模式、Linux命令等內容,到時候會有儘可能清晰的講解,如果筆者認為某些內容可以通過其他教材系統學習,會在書中加以說明。
另外,本書只涉及 Intel x86平台。
統一思想——讓我們在這些方面達成共識
道篇
讓我們有效而愉快地學習
你大概依然記得在你親自敲出第一個“Hello world”程式並運行成功時的喜悅,那樣的成就感助燃了你對編寫程式濃厚的興趣。隨後你不斷地學習,每學到新的語法都迫不及待地在計算機上調試運行,在調試的過程中克服困難,學到新知,並獲得新的成就感。
可現在請你構想一下,假如課程不是這樣的安排,而是先試圖告訴你所有的語法,中間沒有任何實踐的機會,試問這樣的課程你能接受嗎?我猜你唯一的感受將是索然寡味。
原因何在?只是因為你不再有因為不斷實踐而獲得的源源不斷的成就感。而成就感是學習過程中快樂的源泉,沒有了成就感,學習的愉快程度將大打折扣,效果於是也將變得不容樂觀。
每個人都希望有效而且愉快的學習過程,可不幸的是,我們見到的作業系統課程十之八九令我們失望,作者喋喋不休地講述著進程管理存儲管理I/O控制調度算法,可我們到頭來也沒有一點的感性認識。我們好像已經理解卻又好像一無所知。很明顯,沒有成就感,一點也沒有。筆者痛恨這樣的學習過程,也決不會重蹈這樣的覆轍,讓讀者獲得成就感將是本書的靈魂。
其實這本書完全可以稱作一本回憶錄,記載了筆者從開始不知道保護模式為何物到最終形成一個小小
OS的過程,這樣的回憶錄性質保證了章節的安排完全遵從操作的時間順序,於是也就保證了每一步的可操作性,毫無疑問,順著這樣的思路走下來,每一章的成果都需要努力但又盡在眼前,步步為營是我
們的戰術,成就感是我們的宗旨。
我們將從二十行代碼開始,讓我們最簡單的作業系統嬰兒慢慢長大,變成一個翩翩少年,而其中的每一步,你都可以在書中的指導下自己完成,不僅僅是看到,而是自己做到!你將在不斷的實踐中獲得不斷的成就感,筆者真心希望在閱讀本書的過程中,你的學習過程可以變得愉快而有效。
學習的過程應該是從感性到理性
在你沒有登過泰山之前,無論書中怎樣描寫它的樣子你都無法想像出它的真實面目,即便配有插圖,你對它的了解仍會只是支離破碎。毫無疑問,一千本對泰山描述的書都比不上你一次登山的經歷。文學家的描述可能是華麗而優美的,可這樣的描述最終產生的效果可能是你非去親自登泰山不可。反過來想呢,假如你已經登過泰山,這樣的經歷產生的效果會是你想讀盡天下描述泰山的書而後快嗎?可能事實恰恰相反,你可能再也不想去看那些文字描述。
是啊,再好的講述,又哪比得上親身的體驗?人們的認知規律本來如此,有了感性的認識,才能上升為理性的理論。反其道而行之只能是事倍功半。
如果作業系統是一座這樣的大山,本書願做你的導遊,引領你進入它的門徑。傳統的作業系統書籍僅僅是給你講述這座大山的故事,你只是在聽講,並沒有身臨其境,而隨著這本書親身體驗,則好像置身於山門之內,你不但可以看見眼前的每一個細節,更是具有了走完整座大山的信心。
值得說明的是,本書旨在引路,不會帶領你走完整座大山,但是有興趣的讀者完全可以在本書最終形成的框架的基礎上容易地實現其他作業系統書籍中講到的各種原理和算法,從而對作業系統有個從感性到理性的清醒認識。
暫時的錯誤並不可怕
當我們對一件事情的全貌沒有很好理解的時候,很可能會對某一部分產生理解上的誤差,這就是所謂的斷章取義。很多時候斷章取義是難免的,但是,在不斷學習的過程中,我們會逐漸看到更多,了解更多,對原先事物的認識也會變得深刻甚至不同。
對於作業系統這樣複雜的東西來說,要想了解所有的細節無疑是非常困難的,所以在實踐的過程中,可能在很多地方,會有一些誤解發生。這都沒有關係,隨著了解的深入,這些誤解總會得到澄清,到時你會發現,自己對某一方面已經非常熟悉了,這時的成就感,一定會讓你感到非常愉悅。
本書內容的安排遵從的是代碼編寫的時間順序,它更像是一本開發日記,所以在書中一些中間過程不完美的產物被有意保留了下來,並會在以後的章節中對它們進行修改和完善,因為筆者認為,一些精妙的東西背後,一定隱藏著很多中間的產物,一個偉大的發現在很多情況下可能不是天才們剎那間的靈光一閃,背後也一定有著我們沒有看到的不偉大甚至是謬誤。筆者很想追尋前輩們的腳步,重尋他們當日的足跡。做到這一點無疑很難,但即便無法做到,只要能引起讀者的一點思索,也是本書莫大的幸事。
擋住了去路的,往往不是大樹,而是小藤
如果不是親身去做,你可能永遠都不知道,困難是什麼。
就好像你買了一台功能超全的微波爐回家,研究完了整本說明書,躊躇滿志想要烹飪的時候,卻突然發現家裡的油鹽已經用完。而當時已經是晚上十一點,所有的商店都已經關門,你氣急敗壞,簡直想摸起鐵勺砸向無辜的微波爐。
研究說明書是沒有錯的,但是在沒開始之前,你永遠都想不到讓你無法烹飪的原因居然是十塊錢一瓶的油和一塊錢一袋的更加微不足道的鹽。你還以為困難是微波爐面板上密密麻麻的控制鍵盤。
其實做其他事情也是一樣的,比如寫一個作業系統,即便一個很小的可能受理論家們譏笑的作業系統雛形,仍然可能遇到一大堆你沒有想過的問題,而這些問題在傳統的作業系統書籍中根本沒有提到。所以唯一的辦法,便是親自去做,只有實踐了,才知道是怎么回事。
術篇
用到什麼再學什麼
我們不是在考試,我們只是在為了自己的志趣而努力,所以就讓我們忠於自己的喜好吧,不必為了考試而看完所有的章節,無論那是多么的乏味。讓我們馬上投入實踐,遇到問題再圖解決的辦法。筆者非常推崇這樣的學習方法:
實踐 →遇到問題 →解決問題 →再實踐
因為我們知道我們為什麼學習,所以我們才會非常投入;由於我們知道我們的目標是解決什麼問題,所以我們才會非常專注;由於我們在實踐中學習,所以我們才會非常高效。而最有趣的是,最終你會發現你並沒有因為選擇這樣的學習方法而少學到什麼,相反,你會發現你用更少的時間學到更多的東西,並且格外的紮實。
只要用心,就沒有學不會的東西
筆者還清楚地記得剛剛下載完 Intel Architecture Software Developer Manual那三個可怕的 PDF檔案時的心情,那時心裡暗暗嘀咕,什麼時候才能把這些東西讀懂啊!可是突然有一天,當這些東西真的已經被基本讀完的時候,我想起當初的畏懼,時間其實並沒有過去多少。
所有的道理都是相通的,沒有什麼真正可怕,尤其是,我們所做的並非創造性的工作,所有的問題前人都曾經解決,所以我們更是無所畏懼,更何況我們不僅有書店,而且有網際網路,動動手腳就能找到需要的資料,我們只要認真研究就夠了。
所以當遇到困難時,請靜下心來,慢慢研究,因為只要用心,就沒有學不會的東西。
適當地囫圇吞棗
如果囫圇吞棗僅僅是學習的一個過程而非終點,那么它並不一定就是壞事。大家都應該聽說過魯迅先生學習英語的故事,他建議在閱讀的過程中遇到不懂的內容可以忽略,等到過一段時間之後,這些問題會自然解決。
在本書中,有時候可能先列出一段代碼,告訴你它能完成什麼,這時你也可以大致讀過,因為下面會有對它詳細的解釋。第一遍讀它的時候,你只要了解大概就夠了。
本書的原則
1.寧可囉嗦一點,也不肯漏掉細節
在書中的有些地方,你可能覺得有些很“簡單”的問題都被列了出來,甚至顯得有些囉嗦,但筆者寧可讓內容寫得囉嗦點,因為筆者自己在讀書的時候有一個體驗,就是有時候一個問題怎么也想不通,經過很長時間終於弄明白的時候才發現原來是那么“簡單”。可能作者認為它足夠簡單以至於可以跳過不提,但讀者未必那么幸運一下子就弄清楚。
不過本書到後面的章節,如果涉及的細節是前面章節提到過的,就有意地略過了。舉個非常簡單的例子,開始時本書會提醒讀者增加一個源檔案之後不要忘記修改Makefile,到後來就假定讀者已經熟悉了這個步驟,可能就不再提及了。
2.努力做到平易近人
筆者更喜歡把本書稱作一本筆記或者學習日誌,不僅僅是因為它基本是真實的學習過程的再現,而且筆者不想讓它有任何居高臨下甚至是晦澀神秘的感覺。如果有一個地方你覺得書中沒有說清楚以至於你沒有弄明白,請你告訴我,我會在以後做出改進。
3.代碼注重可讀性但不注重效率
本書的代碼力求簡單易懂,在此過程中很少考慮運行的效率。一方面因為書中的代碼僅僅供學習之用,暫時並不考慮實際用途;另一方面筆者認為當我們對作業系統足夠了解之後再考慮效率的問題也不遲。
本書附帶光碟說明
本書附帶光碟中有本書用到的所有原始碼。值得一提的是,其中不止包含完整的作業系統代碼,還包含各個步驟的中間產物。換句話說,開發中每一步驟的代碼,都可在光碟中單獨資料夾中找到。舉例說明,書的開篇介紹引導扇區,讀者在相應資料夾中就只看到引導扇區的代碼;第 9章介紹檔案系統,在相應資料夾中就不會包含第 10章記憶體管理的代碼。在任何一個步驟對應的資料夾中,都包含一個完整可編譯運行的代碼樹,以方便讀者試驗之用。這樣在學習的任何一個階段,讀者都可徹底了解階段性成果,且不必擔心受到自己還未學習的內容的影響,從而使學習不留死角。
在書的正文中引用的代碼會標註出出自哪個檔案。以“chapter5/b/bar.c”為例:如果你使用Linux,並且光碟掛載到“/mnt/cdrom”,那么檔案的絕對路徑為“/mnt/cdrom/chapter5/b/bar.c”;如果你使用Windows,並且光碟是 X:盤,那么檔案的絕對路徑為“X:nchapter5nbnbar.c”。
目 錄
上 篇
第1章 馬上動手寫一個最小的“作業系統” 2
1.1 準備工作 2
1.2 十分鐘完成的作業系統 3
1.3 引導扇區 4
1.4 代碼解釋 4
1.5 水面下的冰山 6
1.6 回顧 7
第2章 搭建你的工作環境 8
2.1 虛擬計算機bochs 8
2.1.1 Bochs初體驗 8
2.1.2 Bochs的安裝 9
2.1.3 Bochs的使用 10
2.1.4 用Bochs調試作業系統 12
2.2 qemu 15
2.3 平台之爭:Windows還是*nix 16
2.4 GNU/Linux下的開發環境 20
2.5 Windows下的開發環境 22
2.6 總結 23
第3章 保護模式(Protect Mode) 25
3.1 認識保護模式 25
3.1.1 保護模式的運行環境 29
3.1.2 GDT(Global Descriptor Table) 31
3.1.3 實模式到保護模式,不一般的jmp 33
3.1.4 描述符屬性 35
3.2 保護模式進階 38
3.2.1 海闊憑魚躍 38
3.2.2 LDT(Local Descriptor Table) 44
3.2.3 特權級概述 48
3.2.4 特權級轉移 51
3.2.5 關於“保護”二字的一點思考 65
3.3 頁式存儲 65
3.3.1 分頁機制概述 66
3.3.2 編寫代碼啟動分頁機制 67
3.3.3 PDE和PTE 68
3.3.4 cr3 71
3.3.5 回頭看代碼 72
3.3.6 克勤克儉用記憶體 73
3.3.7 進一步體會分頁機制 81
3.4 中斷和異常 87
3.4.1 中斷和異常機制 87
3.4.2 外部中斷 90
3.4.3 編程操作8259A 91
3.4.4 建立IDT 94
3.4.5 實現一個中斷 95
3.4.6 時鐘中斷試驗 96
3.4.7 幾點額外說明 98
3.5 保護模式下的I/O 100
3.5.1 IOPL 100
3.5.2 I/O許可點陣圖(I/O Permission Bitmap) 100
3.6 保護模式小結 101
第4章 讓作業系統走進保護模式 102
4.1 突破512位元組的限制 102
4.1.1 FAT12 103
4.1.2 DOS可以識別的引導盤 108
4.1.3 一個最簡單的Loader 108
4.1.4 載入Loader入記憶體 109
4.1.5 向Loader交出控制權 116
4.1.6 整理boot.asm 116
4.2 保護模式下的“作業系統” 117
第5章 核心雛形 119
5.1 在Linux下用彙編寫Hello World 119
5.2 再進一步,彙編和C同步使用 120
5.3 ELF(Executable and Linkable Format) 123
5.4 從Loader到核心 127
5.4.1 用Loader載入ELF 127
5.4.2 跳入保護模式 131
5.4.3 重新放置核心 137
5.4.4 向核心交出控制權 142
5.5 擴充核心 143
5.5.1 切換堆疊和GDT 144
5.5.2 整理我們的資料夾 148
5.5.3 Makefile 149
5.5.4 添加中斷處理 155
5.5.5 兩點說明 168
5.6 小結 169
第6章 進程 171
6.1 遲到的進程 171
6.2 概述 171
6.2.1 進程介紹 172
6.2.2 未雨綢繆——形成進程的必要考慮 172
6.2.3 參考的代碼 173
6.3 最簡單的進程 174
6.3.1 簡單進程的關鍵技術預測 175
6.3.2 第一步——ring0→ring1 178
6.3.3 第二步——豐富中斷處理程式 189
6.4 多進程 200
6.4.1 添加一個進程體 200
6.4.2 相關的變數和宏 200
6.4.3 進程表初始化代碼擴充 202
6.4.4 LDT 203
6.4.5 修改中斷處理程式 203
6.4.6 添加一個任務的步驟總結 206
6.4.7 號外:Minix的中斷處理 207
6.4.8 代碼回顧與整理 212
6.5 系統調用 220
6.5.1 實現一個簡單的系統調用 222
6.5.2 get_ticks的套用 227
6.6 進程調度 232
6.6.1 避免對稱——進程的節奏感 232
6.6.2 優先權調度總結 240
第7章 輸入/輸出系統 242
7.1 鍵盤 242
7.1.1 從中斷開始——鍵盤初體驗 242
7.1.2 AT、PS/2鍵盤 243
7.1.3 鍵盤敲擊的過程 244
7.1.4 用數組表示掃描碼 248
7.1.5 鍵盤輸入緩衝區 251
7.1.6 用新加的任務處理鍵盤操作 253
7.1.7 解析掃描碼 254
7.2 顯示器 263
7.2.1 初識TTY 264
7.2.2 基本概念 264
7.2.3 暫存器 267
7.3 TTY任務 270
7.3.1 TTY任務框架的搭建 272
7.3.2 多控制台 277
7.3.3 完善鍵盤處理 281
7.3.4 TTY任務總結 288
7.4 區分任務和用戶進程 289
7.5 printf 291
7.5.1 為進程指定TTY 292
7.5.2 printf()的實現 292
7.5.3 系統調用write() 294
7.5.4 使用printf() 296
下 篇
第8章 進程間通信 300
8.1 微核心還是宏核心 300
8.1.1 Linux的系統調用 302
8.1.2 Minix的系統調用 303
8.1.3 我們的選擇 305
8.2 IPC 306
8.3 實現IPC 306
8.3.1 assert()和panic() 309
8.3.2 msg_send()和msg_receive() 313
8.3.3 增加訊息機制之後的進程調度 321
8.4 使用IPC來替換系統調用get_ticks 322
8.5 總結 324
第9章 檔案系統 325
9.1 硬碟簡介 325
9.2 硬碟操作的I/O 連線埠 326
9.3 硬碟驅動程式 327
9.4 檔案系統 337
9.5 硬碟分區表 338
9.6 設備號 344
9.7 用代碼遍歷所有分區 347
9.8 完善硬碟驅動程式 352
9.9 在硬碟上製作一個檔案系統 355
9.9.1 檔案系統涉及的數據結構 356
9.9.2 編碼建立檔案系統 358
9.10 創建檔案 366
9.10.1 Linux下的檔案操作 366
9.10.2 檔案描述符(file descriptor) 367
9.10.3 open() 369
9.11 創建檔案所涉及的其他函式 377
9.11.1 strip_path() 377
9.11.2 search_file() 378
9.11.3 get_inode()和sync_inode() 379
9.11.4 init_fs() 381
9.11.5 read_super_block()和get_super_block() 382
9.12 關閉檔案 383
9.13 查看已創建的檔案 384
9.14 打開檔案 386
9.15 讀寫檔案 387
9.16 測試檔案讀寫 390
9.17 檔案系統調試 393
9.18 刪除檔案 395
9.19 插曲:奇怪的異常 401
9.20 為檔案系統添加系統調用的步驟 403
9.21 將TTY納入檔案系統 404
9.22 改造printf 411
9.23 總結 413
第10章 記憶體管理 414
10.1 fork 414
10.1.1 認識fork 414
10.1.2 fork前要做的工作(為fork所做的準備) 417
10.1.3 fork()庫函式 421
10.1.4 MM 421
10.1.5 運行 427
10.2 exit和wait 427
10.3 exec 432
10.3.1 認識exec 433
10.3.2 為自己的作業系統編寫應用程式 434
10.3.3 “安裝”應用程式 436
10.3.4 實現exec 442
10.4 簡單的shell 447
10.5 總結 449
第11章 尾聲 451
11.1 讓mkfs()只執行一次 451
11.2 從硬碟引導 455
11.2.1 編寫硬碟引導扇區和硬碟版loader 455
11.2.2 “安裝”hdboot.bin和hdldr.bin 461
11.2.3 GRUB 461
11.2.4 小結 463
11.3 將OS安裝到真實的計算機 465
11.3.1 準備工作 465
11.3.2 安裝Linux 466
11.3.3 編譯原始碼 466
11.3.4 開始安裝 467
11.4 總結 467
參考文獻 470