【考驗對象:丹尼斯·里奇。當前水平——??】
【考驗形式:情景模擬】
【考驗內容:考查對C語言的使用能力、理解水平】
【注意——勝,則破境;敗,則退回當前境界大圓滿,需重新積累】
揮去面板,眼前的畫面才逐漸清晰起來。
房間不大,天花板的日光燈管有些昏暗,照不清牆上掛著幾張手寫的草圖。
李林抬眼望去。
一台PDP-11小型機占據了就擺在房間中央的桌子上,機箱側蓋被卸了下來,露出裡面密密麻麻的線路板。
UNIX,現代作業系統的奠基者,就在這裡誕生。
同樣會在今天誕生的,還有作業系統的原初聖言、資訊時代的拉丁語、程式設計師的白月光……
咳咳,什麼叫這裡放不下這麼多語言。
總之,李林此行就是為此而來——
C語言。
他深吸一口氣,終於直視面前的男人,也是這次考驗的「考官。」
三十歲出頭、深色頭髮有些凌亂,依舊格子襯衫、依舊黑框眼鏡,也許是程式設計師的刻板印象來源也說不定。
丹尼斯·里奇,C語言之父。
他一隻手撐著桌面,另一隻手夾著筆,眉頭緊鎖。
和上次一樣。
「孩子,」
注意到李林的瞬間,里奇開口了,嗓音有些沙啞,
「我需要為內核構建一個更可靠的內存分配器,來管理所有內核對象。現在的版本太脆弱了,一碰到複雜的操作就崩潰。」
「說來慚愧,即便是我,也沒法判斷親手釋放「指針」這個怪物,究竟是對是錯。有時候它太危險,恨不得讓我把它封裝進盒子裡;但有時候,它帶來的高效又是如此美妙,總讓我想起我為自己定下的那句誓言——」
「Keep it simple stupid。」
「保持簡單吧,孩子。」
他說著,把一張幾乎畫滿的草圖推到李林面前,眼裡有疲憊,也有審視,
「我需要你提出一個方案,一個簡單的方案。」
李林不是第一次聽到這個問題了。
上一次站在這裡的時候,他束手無策。
面對里奇那雙深邃的眼睛,他連第一步都沒能邁出去。
那時候他的腦子裡還只有基礎的內存分配。
malloc、free、棧上分配、堆上分配……他連從哪裡開始都不知道。
所以他失敗了。
這一次不一樣了。
他點點頭,平靜地接過了這個委託。
忽然,面前的空間展開,化成一張巨大的、向四面八方無盡延伸的方格紙。
每一個小格子代表一個內存單元,被占用的、空閒的都一一陳列於此。
它們都在等待創造者的規劃安排。
前提是你能在它們服從秩序之前,保證自己不被混亂擊穿大腦。
「這就是你的畫布,孩子。B語言沒能征服它,我們來試試。」
里奇的聲音從身後的虛空中傳來,李林定了定心神。
動手。
首先要解決「我是誰」的問題。
每一個分配出去的內存塊,它必須知道自己多大、屬於誰、下一個是誰。
李林開始用struct定義內存塊的頭部信息,包括塊的大小、是否空閒和鍊表指針。
他在方格紙上畫下第一道箭頭,從一塊內存的頭部指向下一塊,再指向下一塊,把那些散落的格子串成一條鏈。
由此,他便實現了最基礎的alloc和free。
遍歷鍊表,找到第一塊足夠大的空閒塊,分配出去,釋放時標記為空閒,和相鄰的空閒塊合併。
方格紙上,逐漸開始有了秩序。
「太慢了。」
里奇的聲音再次響起,這次帶著毫不掩飾的失望,
「每次分配都要從頭遍歷,這在系統內核里根本不能用。你這個版本比那些老古董手寫的彙編還要差。」
李林盯著方格紙上那條長長的鍊表。
是啊,所以需要一些新東西了。
李林握了握拳頭,閉上眼睛,回想起這些天來自己的所學所思。
首先,他想到的是自己在現代編程中見過的——
內存對齊技術。
對齊到2的冪次,用地址本身來編碼信息。
再結合之前胡云程提過的「細胞」,兩個思路撞在一起,解法也就應運而生了。
夥伴算法。
將內存按2的冪次分割成塊。
當請求來臨,找到剛好大於等於請求大小的塊。
如果塊太大,就一分為二,「夥伴對夥伴」,直到大小合適。
回收的時候,檢查夥伴是否也空閒,如果是就合併回去。
最關鍵的是,計算夥伴地址不需要遍歷,不需要查表。
只需要一次位運算。
於是,李林在方格紙上重新畫了一遍。
這次不再是長長的鍊表,而是一棵二叉樹。
每個節點都有它的夥伴,每個夥伴都在一個固定的位置上等它。
他用位運算代替了浮點和除法,用地址本身作為定位夥伴的坐標。
方格紙上,一棵由箭頭組成的二叉樹緩緩展開。
從根到葉,從小到大,每一對夥伴節點都互相呼應。
里奇第一次露出了驚訝的表情。
他盯著那張方格紙,沉默了好一會兒,才呢喃道,
「不是鍊表,是樹嗎……好,太好了。」
里奇不由自主地鼓起掌來,抬起頭看著李林,
「能做到這一步,你其實已經算是通過考驗了。」
李林聞言,還沒來及高興,就聽到里奇的後半句——
「但是,孩子,我不知道你來自哪裡、也不知道你要去往何方。」
李林:?
不是,你這話說的就好像……
李林皺起眉頭,下意識後退了一步。
里奇笑了笑,眼中閃過一道精光,
「我唯一知道的事情是,你,真的讓我興奮起來了。」
【檢測到考驗已通過,即將脫離模擬考驗……】
「哦,真是有趣,這也是計算機的一種嗎?」
隨著里奇輕輕揮手,李林眼前藍色的光幕竟然消失了——
【&%*#¥%%】
系統提示變成了一串亂碼。
「不要著急,再陪我待會兒吧,孩子。」
考驗沒有結束。
「速度可以了。」
里奇敲了幾下鍵盤,方格紙上又起了變化。
有幾塊內存被標記為「已分配」,每一塊上面都掛著一個指針。
「但我的同事不小心寫了一段代碼,錯誤地釋放了同一個對象兩次。」
free(ptr); free(ptr);
同一塊內存,被釋放了兩次。
一瞬間,方格紙被染成血紅。
第二次釋放觸發了已經合併的夥伴節點,指針指向了已經被重新分配出去的地址。
錯誤像連鎖閃電一樣從一個節點蔓延到所有節點,整棵二叉樹開始崩塌。
那些串聯的箭頭一根接一根地斷裂、消散。
「你該如何應對呢?」
不是,哥們兒。
什麼叫考完試,考官拉著我不讓走啊?
李林額頭微微冒汗。