上千萬行,十多G源碼,瀏覽器為什么這么“變態(tài)”?
我們來看開源的chromium,這貨確實(shí)相當(dāng)相當(dāng)?shù)膹?fù)雜。源碼拉下來就有十多G。
我們不禁好奇,chromium到底有哪些玩意,為啥平時(shí)感覺只是顯示個(gè)網(wǎng)頁(yè)、幾句HTML而已,怎么會(huì)需要這么多代碼?
第一眼從目錄結(jié)構(gòu)上,chromium包含這些東西:
base,通用代碼,基礎(chǔ)組件,包含字符串、文件、線程、消息隊(duì)列等工具類集合。
cc,Chromium compositor 的縮寫,負(fù)責(zé)渲染合成。
chrome,Chromium 瀏覽器外殼實(shí)現(xiàn)。
content,多進(jìn)程沙盒瀏覽器的核心代碼,管理進(jìn)程架構(gòu)和線程架構(gòu)。
gpu,OpenGL 封裝代碼,包含 CommandBuffer 和 OpenGL 兼容性支持等。
net,網(wǎng)絡(luò)棧實(shí)現(xiàn)。
ipc,進(jìn)程間消息通信實(shí)現(xiàn)。
media,多媒體封裝代碼,包含了媒體內(nèi)容捕獲和播放的組件集合。
mojo,類似于 Android 的 AIDL,提供了跨語(yǔ)言(C++ / Java / JavaScript)跨平臺(tái)的進(jìn)程間對(duì)象(Object)通信機(jī)制;。
skia,圖形庫(kù),這里存放的是 Chromium 對(duì) skia 的 配置和擴(kuò)展代碼,另有 third_party/skia 目錄存放原生的 skia 代碼。
third_party,網(wǎng)頁(yè)排版引擎。第三方庫(kù)
ui,UI 框架。
v8,V8 JavaScript 引擎庫(kù)。
看起來還好吧?但實(shí)際上,這里面每一個(gè)展開來講,都是一本厚厚的工具書的容量。
比如net,看起來只是個(gè)網(wǎng)絡(luò)庫(kù),然而里面包含主機(jī)解析,cookies,網(wǎng)絡(luò)改變探測(cè),SSL,資源緩存,ftp,HTTP, OCSP實(shí)現(xiàn),代理 (SOCKS和HTTP) 配置,解析,腳本獲?。òǜ鞣N不同系統(tǒng)下實(shí)現(xiàn)),QUIC,socket池,SPDY,WebSockets……里面每一項(xiàng)展開來講,就是一本書。
v8層,看起來功能很單一,只是實(shí)現(xiàn)一下js嘛,但里面包括字節(jié)碼解析器,JIT 編譯器,多代GC,inspector (調(diào)試支持),內(nèi)存和 CPU 的 profiler(性能統(tǒng)計(jì)),WebAssembly 支持,兩種 post-mortem diagnostics 的支持,啟動(dòng)快照,代碼緩存、代碼熱點(diǎn)分析……里面每一項(xiàng)展開來講,又是一本書,還是難坑的編譯原理和優(yōu)化方向。
Skia,看起來只是個(gè)圖形庫(kù)嘛,用點(diǎn)畫出各種圖。然而里面包括十幾種矢量的繪制,文字繪制、GPU加速、矢量的指令錄制以及回放(還要能支持線程安全)、各種圖像格式的編解碼、pdf的生成(這個(gè)是個(gè)隱藏的很深的功能,但很有趣。Skia支持把矢量圖繪制成pdf)、GPU渲染優(yōu)化(既以上部分功能需要用gpu來渲染)……里面每項(xiàng)展開來講,又是一本書。
另外值得一提的是,skia是谷歌收購(gòu)的。不知道谷歌是覺得自己沒實(shí)力做,還是太費(fèi)功夫??傊雀柽x擇了直接買別人的代碼來完成這些功能。
ui,看起來只是一套UI 框架嘛。然而chromium需要一套全平臺(tái)適配的ui庫(kù),還要能支持gpu加速。不過可惜的是里面沒實(shí)現(xiàn)richedit。ui庫(kù)的設(shè)計(jì),深入來做,其實(shí)可以說又是個(gè)瀏覽器了。
等一下,以上這些,看起來只是瀏覽器的外層。我們最關(guān)心的網(wǎng)頁(yè)排版呢?這個(gè)難道不是瀏覽器的核心嘛!
是的,神奇的是,chromium把排版引擎blink放到了third_party下,而且架構(gòu)上真的當(dāng)成了一個(gè)第三方庫(kù)一樣對(duì)待。
據(jù)谷歌的員工說,這是歷史原因……好吧姑且信了。然而這個(gè)第三方庫(kù),成了當(dāng)之無愧的最復(fù)雜,功能最重要的第三方庫(kù)。
blink的工作包括:
- 實(shí)現(xiàn)web平臺(tái)的規(guī)范(例如,HTML標(biāo)準(zhǔn)),包括DOM,CSS和Web IDL
- 配合V8運(yùn)行JavaScript
- 從底層網(wǎng)絡(luò)堆棧請(qǐng)求資源
- 構(gòu)建DOM樹
- 計(jì)算樣式和布局
- 請(qǐng)求chrome compositor(上文提到的cc層)并繪制圖形。
說起來簡(jiǎn)單??匆幌卢F(xiàn)在的HTML、CSS規(guī)范,各種細(xì)節(jié)加起來……有快上萬頁(yè)。
除了chromium layout組、firefox的開發(fā)人員等,我想沒幾個(gè)人會(huì)去仔細(xì)閱讀并一個(gè)個(gè)的實(shí)現(xiàn)這些規(guī)范吧。光是看目錄和文字描述,就頭大了,更別說要完整的實(shí)現(xiàn)出來。
往往一個(gè)簡(jiǎn)單的display:gird\flex背后就是龐大復(fù)雜的計(jì)算,而且還要充分考慮性能上如何優(yōu)化,滾動(dòng)時(shí)如何更快的展示…
另外排版還需要支持世界各國(guó)的奇奇怪怪的文字。例如從右往左寫、規(guī)則復(fù)雜無比阿拉伯文。相比之下,漢字這種方塊字的排版簡(jiǎn)直就是弟弟。還有各種奇怪的unicode字符。
怎么能處理好這些字符和語(yǔ)言,并配合幾千頁(yè)的html、css排版規(guī)則正確顯示出來……這是個(gè)極度燒腦的事情。
我們?cè)購(gòu)呐虐孢@個(gè)大泥坑里跳出來看看外面別的東西。這時(shí)候你會(huì)發(fā)現(xiàn)……外面的泥坑好像更大。隨便說幾個(gè),比如:
多進(jìn)程框架。嗯,你需要更多的進(jìn)程來渲染更多的網(wǎng)頁(yè),這樣才能崩潰了也不影響其他網(wǎng)頁(yè)。
注意,chromium把渲染排版放在渲染進(jìn)程,但繪制到窗口又是主進(jìn)程。這里面少不了各種跨進(jìn)程通信、同步。對(duì)于代碼的編寫以及調(diào)試,是個(gè)很考驗(yàn)編程功底的事情。
webrtc。網(wǎng)絡(luò)視頻相關(guān)。又是一個(gè)被收購(gòu)的庫(kù)。關(guān)于webrtc,你需要知道它能實(shí)現(xiàn)多人實(shí)時(shí)語(yǔ)音、降噪、網(wǎng)絡(luò)傳輸視頻、攝像頭的捕獲,音頻算法實(shí)現(xiàn)(比如 fft),視頻算法實(shí)現(xiàn)(比如 h264 協(xié)議格式), Socket、線程、鎖等基礎(chǔ)庫(kù)(是的,webrtc也造了套自己的輪子)。又是個(gè)龐大的組件。
密碼管理、下載管理、擴(kuò)展管理。
一套調(diào)度整個(gè)多進(jìn)程框架以及blink的核心層。在chromium被稱之為content層,負(fù)責(zé)處理一切繁瑣的細(xì)節(jié)。例如各種系統(tǒng)、平臺(tái)的鼠標(biāo)鍵盤消息派發(fā),歷史棧(前進(jìn)后退),頁(yè)面緩存。
沙箱機(jī)制。負(fù)責(zé)隔離以及降低子進(jìn)程的權(quán)限。沙箱的實(shí)現(xiàn)上,在不同系統(tǒng)做了諸多hook操作。
chrome相關(guān)的外殼及應(yīng)用。例如我們常見的標(biāo)題欄、url欄,webui如設(shè)置頁(yè)、歷史記錄頁(yè)。對(duì),其實(shí)chrome單詞的原意就是這個(gè)。
Clound_Print,谷歌云打印相關(guān),提供谷歌瀏覽器頁(yè)面預(yù)覽打印清單。
Courgetter,谷歌提供的二進(jìn)制文件對(duì)比核心算法,用于比較不同版本的二進(jìn)制差異。谷歌為了方便升級(jí),搞了套升級(jí)策略和算法。
神奇的syzygy優(yōu)化。是的,谷歌也嫌chrome太大了、加載太慢了。于是他們開發(fā)了一套工具鏈,優(yōu)化重排布PE二進(jìn)制文件的算法來達(dá)到優(yōu)化程序。Chrome瀏覽器應(yīng)用了Syzygy優(yōu)化之后,程序冷啟動(dòng)的頁(yè)面調(diào)度(paging traffic)優(yōu)化了80%,加載的Image的Working Set優(yōu)化了40%。簡(jiǎn)單的說,谷歌為了優(yōu)化啟動(dòng)性能,從編譯器上對(duì)exe、dll開始做手腳了。
Media,Chrome的多媒體模塊,支持音頻播放和錄音等功能。這里用到了ffmpeg。但在ffmpeg外,為了和blink配合,又是包裹了厚厚的一層,用來處理好渲染管線。另外MSE API也花了不少功夫。
swiftshader。很有趣的一個(gè)模塊,用純軟件的代碼,完整實(shí)現(xiàn)了opengl的接口??梢栽跊]有硬件加速的機(jī)器上跑起opengl。也是個(gè)龐大的庫(kù),而且也是被收購(gòu)的??雌饋砉雀鑼?duì)圖形學(xué)方面的很多工程似乎不擅長(zhǎng)?還是不想覺得應(yīng)該交給更專業(yè)的團(tuán)隊(duì)去做。
gn、gyp、ninja。chromium為了更方便的管理編譯,自己擼了三套輪子。類似makefile、cmake,然后底層調(diào)用ninja再到vs或者clang負(fù)責(zé)具體編譯其他的點(diǎn)還有很多很多,以后想到了再補(bǔ)充。
總之,以上隨意一個(gè)點(diǎn),要正確的實(shí)現(xiàn),都是一個(gè)團(tuán)隊(duì)的工作量,都可以寫成一本書。然而chromium把他們?nèi)繉?shí)現(xiàn)了,而且還在不停的加入新的功能。
看到這里,大家應(yīng)該明白為啥強(qiáng)如微軟,也放棄維護(hù)他們自己的瀏覽器內(nèi)核了。因?yàn)樾枰度氲娜肆ω?cái)力實(shí)在是太恐怖了。chromium團(tuán)隊(duì),光是開發(fā)人員,都已經(jīng)上千了。
假如每個(gè)人員年薪是100w 人民幣,持續(xù)投入十年,這個(gè)支出就是幾十億,這還不算周邊的測(cè)試、產(chǎn)品、UI。
最關(guān)鍵的是,就算微軟愿意投入十億,能保證做到chromium相同的功能嗎?就算能做到相同的功能,還不是另外一套chromium,能做出其他優(yōu)勢(shì)嗎?
于是最后微軟也放棄了,干脆直接從開源的chromium上改起,把微軟需要的功能融入chromium。
所以,chromium的霸權(quán)就是這么來的。看似開源免費(fèi),實(shí)則把所有開發(fā)者和對(duì)手,緊緊的捆綁在自己周圍。
好了,現(xiàn)在吐槽開始了。
chromium稱霸瀏覽器界以來,看起來開源,誰都可以拿去改。然而比起它的前輩,我覺得從“道德”上,chromium要“差”很多。
最讓我受不了的一點(diǎn)是,chromium在無盡的往里面塞功能的時(shí)候,很少想過是否別人可以輕易的移除它們。
chromium代碼號(hào)稱模塊化、高內(nèi)聚低耦合,然而如果你想砍掉一些不需要的東西,對(duì)不起,沒有宏控制,手動(dòng)刪代碼吧。有幾個(gè)人能有精力一點(diǎn)點(diǎn)的去掉里面這些繁瑣的功能呢?
這就導(dǎo)致一個(gè)問題,需要chromium某一部分功能的人,必須被強(qiáng)塞進(jìn)一堆谷歌認(rèn)為你需要的東西。對(duì)比之下,為啥我說比起chromium的前輩要差很多呢,其實(shí)我指的正是webkit。webkit最讓人欣賞的一點(diǎn)就是它在專注實(shí)現(xiàn)內(nèi)核的同時(shí),大部分功能都是可“拆卸”的。有宏可以關(guān)閉。甚至連svg這種排版上的小功能都有宏可以關(guān)閉。
而chromium,如果我需要排版、音視頻,但不需要多進(jìn)程呢?如果我需要音視頻但不需要webrtc呢?對(duì)不起,谷歌沒這考慮。要帶就全都帶上吧,不帶你自己砍代碼吧。
等你辛苦幾個(gè)星期砍完代碼,谷歌告訴你,我都更新了2、3個(gè)版本啦,你要不要更新下代碼?哦?新版本chromium架構(gòu)又大改了?哭了……
這也就造成現(xiàn)在基于chromium的一堆開發(fā)框架,如electron、cef、nwjs,全都動(dòng)不動(dòng)100多M的大小。因?yàn)閺腸hromium的框架設(shè)計(jì)上,就很難把那些極其龐大復(fù)雜的細(xì)節(jié)功能排除掉。
這些功能對(duì)于谷歌作為一個(gè)瀏覽器來說,當(dāng)然是必要的。然而回到本問題,“瀏覽器內(nèi)核”真的需要這么復(fù)雜嗎?
瀏覽器需要這么復(fù)雜,這是真的;然而作為一個(gè)瀏覽器內(nèi)核提供給一些sdk給別人用,也需要這么復(fù)雜嗎?我們用electron寫一套進(jìn)供銷管理系統(tǒng)的客戶端,你會(huì)需要帶上幾十M的webrtc、webgl、多媒體播放、天城文支持嗎?
最后打破這個(gè)疑問的,是我很多年后進(jìn)入了QQ瀏覽器的移動(dòng)端組(其實(shí)就是x5內(nèi)核,微信上被大家吐槽最多的那個(gè))。當(dāng)年的x5內(nèi)核其實(shí)是基于webkit改造的。從chromium回到webkit,我突然有種豁然開朗的感覺。
回到問題開頭,從瀏覽器內(nèi)核的角度,其實(shí)沒那么復(fù)雜,只要做好網(wǎng)絡(luò)、排版、渲染,就足以應(yīng)付大部分使用場(chǎng)景了。
這讓我想起瀏覽器早期年代,群雄爭(zhēng)霸的時(shí)代,那時(shí)候?yàn)g覽器內(nèi)核很小。從幾百K到幾M的瀏覽器都有。我記得早年的移動(dòng)設(shè)備上跑的瀏覽器,css支持的都不好,不過特別小巧,有的才幾百K而已。
然而后來組里架構(gòu)調(diào)整,x5內(nèi)核為了跟上時(shí)代,從webkit切回了chromium(也是因?yàn)楸涣R了太多了,當(dāng)時(shí)x5號(hào)稱移動(dòng)端IE6,做過微信相關(guān)開發(fā)的人應(yīng)該深有體會(huì))。
對(duì)chromium深惡痛絕的我,當(dāng)時(shí)有了一個(gè)大膽的想法。把排版引擎blink(也就是webkit在chromium里的繼承者)重新從chromium里剝離出來,再補(bǔ)上一些周邊的設(shè)施、組件,再次成為一個(gè)完整獨(dú)立的瀏覽器內(nèi)核。
當(dāng)然我還是有自知之明的。一個(gè)瀏覽器內(nèi)核而已,不可能實(shí)現(xiàn)chromium同樣的功能。但能把排版、渲染、網(wǎng)絡(luò)、視頻實(shí)現(xiàn),就差不多了。
其實(shí)我也不是想做個(gè)瀏覽器,而是想專注內(nèi)核這塊,做一個(gè)提供給第三方app嵌入的內(nèi)核。作為一個(gè)內(nèi)核,其實(shí)不需要上面講的所有那一大堆功能。比如,網(wǎng)絡(luò)層,大部分人不需要什么網(wǎng)絡(luò)改變探測(cè),ftp,OCSP實(shí)現(xiàn),代理配置、解析、腳本獲取,QUIC,socket池,SPDY什么的。大部分人僅僅需要一個(gè)http的實(shí)現(xiàn),可以拉取到服務(wù)器資源。
我用300k的curl代替了十余M的chromium net庫(kù),并工作良好。少了的功能可以用插件形式補(bǔ)上嘛。一些和排版渲染無關(guān)的功能,我都打算做成插件。例如音視頻、webgl、webrtc、多進(jìn)程等。
目前這個(gè)項(xiàng)目已經(jīng)擼了5年了,開源在github上,提供C接口方便其他語(yǔ)言調(diào)用。整個(gè)編譯出來就是一個(gè)1-20m左右的dll(視編譯選項(xiàng)而定),甚至還包含了一個(gè)electron的精簡(jiǎn)實(shí)現(xiàn):
https://github.com/weolar/miniblink49。