關于不可變操作系統的設想
英文原文:An immutable operating system
為什么會想到操作系統
大概一年以前,我有一個想法,就是基于不可變值實現 OpenGL 的渲染器。我把這個想法發到博客上了,并且在 Hacker News 和 Reddit 上得到了不少關注。還有些人甚至給我發來了郵件。
在吊床上思考了很長一段時間后,最后這個想法轉移了操作系統的身上。大致是:如果操作系統是由不可變值組成的會是怎樣的呢?
不可變的操作系統?世界都是一直改變的
不可變值是觀察世界來說是一個非常合理的方式。作為一個觀察者,你看著這個世界隨著時間流逝在不停的變化。當你想要做些什么的時候,你會拍下張照片。這個照片是靜止的,你愿意花多長時間去分析它都無所謂。
再回到操作系統這個話題上來。在這里唯一可變的只有一個交換操作。你會有一個叫原子(atom)這么個東西,它會指向一個不可變的值。你可以隨 時獲取到它當前指向的不可變值。從原子這總能獲取到一個完整的值,正如它名字所說的,它是個原子。你可以給它賦值,這樣的話這個原子會指向另一個新的不可 變值。你不能夠直接修改某個值。原子應該有某些機制來保證,上次讀完后沒人進行更新的話,才能對它進行修改,這樣的話,它會更可控些。
細心的讀者會發現,我剛才說的其實就是 Clojure 里面的狀態模型。這也正是我這個操作系統將要實現的,狀態就是由一系列的不可變值組成的。
不可變的內存模型
那么,一個進程會擁有很多指向不可變值的原子,而所有這些值都是無法修改的。沒錯,這里我說的就是所有。你可以將字節序列存儲到一個值里,不過這些字節是不可變的。位移操作?創建一個新的值來完成。要修改第四個字節?也是創建新值。
在這個模型里面,內存可以在進程間安全的進行共享。把一個不可變值傳給另外一個進程有什么關系呢?沒事。非常安全。這里也不需要什么保護性拷貝或者別的保護機制。只有原子本身需要保護。應該有一個機制來管理進程間原子共享,只有創建這個原子的進程可以去共享它。
創建進程幾乎就沒有任何開銷。在 Linux 里面,fork 操作有個非常聰明的寫時復制(C0W)的機制,因此 fork 操作一開始什么也不干,一旦這兩個進程有人開始修改內存里的值的時候,才會發生拷貝操作。當然這也有有一定的開銷的。但如果值是不可變的話,這就完全沒有 任何開銷了,根本就不需要進行拷貝。不可變的就是不可變的!唯一的開銷就是原子本身了,可以用拷貝模式(時間復雜度是O(n)),也可以使用 Linux 系統的 COW 的機制。
如果可以進行指針運算的話就一切都完蛋了。解決方法就是對于這個操作系統上的開發語言而言,它是沒有指針的。指針還會占內存,我們可不喜歡占地方的東西。我們要存儲的只是值。
當然了,還可以進行許多優化。Clojure 有一個 transients 的概念,它的意思是”當你在一個函數里創建一個值的時候,你可以對它進行修改,一旦它從函數里返回了,它就是不可變的。因此從外面看來,函數沒有修改任何 范西,因為這個可變的值只是函數內部可見的。我會替你自動檢測這個。我們還可以監測是否有別人引用了你的值,如果沒有的話,你可以對這些值進行修改。這只 是一個優化,對程序員來說完全是不可見的。
垃圾回收
一個不可變值組成的系統也是需要垃圾回收的。這些都會在系統底層來完成。
如果垃圾回收器知道所有這些值都是不可變的,那么事情就非常有意思了。一般來說,垃圾回收器在整理老生代堆的內存碎片的時候,會暫停所有的操作 (stop the world)。當所有值都不可變的時候,如果要碎片整理,你就把一個值拷貝到另一塊區域,然后更新下內部的指針就好了。
還有一點,通過這種拷貝并交換指針的方式,你還可以優化內部的字節編碼,而不用暫停程序的運行。假設我們發現老生代中的值X是一個 map,并且它已經很久沒有更新了。或話我們可以把它優化成某種結構,也就是把它拷貝到內存中另外一個地方,不過用一種不同的可能是更高效的方式來進行存 儲。除了內核沒人知道這事,也不需要暫停程序的執行。
性能
我們一直都在努力能更好的與硬件進行協作以 提升性能。事實上,盡可能把操作系統的C代碼來硬件來實現。我有一個荒謬的想法就是讓英特爾在硬件層來實現我這個操作系統,觀眾席上有人大聲高喊,”我們 終于可以用 XXX 換掉我們的內存啦!“或者類似的。我不是一個硬件工程師。那么在硬件里實現不可變值的垃圾回收?想想罷了。
這并不是說我自己不去提升性能。也許這個操作系統在某些方面比傳統的操作系統性能要好,因為它不需要寫時復制或者保護性拷貝,它的值可以自由的 共享。在處理器看來,代碼也是不可變的,緩存了代碼的處理器的效率可比緩存了數據的要快得多。如果 CPU 知道內存中的哪些數據頁是不可變的話,那么一級緩存和二級緩存應該會高效得多。或許我們可以通過將數據拷貝到不同的芯片上來解決馮諾依曼體系的瓶頸問題? 因為這是完全不可變的數據,你可以放心的去拷貝。不過誰知道的,時間或許能證明這一切。
文件系統
這塊想的不是很多。說實話我自己很少在程序中使用文件,我一般直接訪問數據庫。所以我想這個系統的持久層應該是一個 key/value 的數據庫。或許跟 Datomic 那樣只能追加數據,不能修改?又或者在這個不可變系統里,文件系統也存儲在一個不可變的地方?好吧,誰知道呢。
開發語言
當然是 Lisp。我會為 Lisp 定制一種字節碼格式,可能會和 EDN 很像。這不完全是編譯后的字節碼,所以字節碼這個說法可能欠妥。我的想法是 Lisp 的抽象語法樹(AST)應該用一種編碼后的巧妙的格式存儲,而不僅僅是純文本。純文本太痛苦了。最后會有一個 JIT 編譯器來讀取這種格式,然后編譯成本地的機器碼。
代碼當然是不可變的。因此可以放心去做熱部署,已有的代碼會繼續運行,新的調用會分發到新部署的代碼上。
沒想好應該用靜態語言還是動態語言。哪個更容易實現就用哪個吧。
再強調一遍,這個系統看起來很像是 Clojure 的一個衍生物。我也不太確定是不是就用 Clojure 作為開發語言就好了(Clojure 已經移植到不同的平臺上了,JS,JVM,CLR)。還是那句話,誰知道呢。
這很可能是個錯誤的想法
這是我寫的第一個操作系統。
很有可能我講的每一點都是錯的。這也正是我實現它的原因。這只是一個研究性的項目,沒有什么實際的目的,比如解決程序缺陷過多什么的。我只能說,這只是一個愛好,不會像 GNU 那樣那么專業全面。在我證明這個想法可行之前,不會考慮到什么會議上去分享這些想法。
這個系統還有很長的路要走,因此現在實現的東西還不多。等我先把進程和 GC 這塊搞完再向大家匯報進度吧。
<span id="shareA4" class="fl"> </span> </div>