Chrome原始碼剖析【一】
阿新 • • 發佈:2019-02-12
開源是口好東西,它讓這個充斥著大量工業垃圾程式碼和教材玩具程式碼的行業,多了一些藝術氣息和美的潛質。它使得每個人,無論你來自米國紐約還是中國鐵嶺,都有機會站在巨人的肩膀上,如果不能,至少也可以抱一把大腿。。。
現在我就是來抱大腿的,這條粗腿隸屬於Chrome(開源專案名稱其實是Chromium,本來Chrome這個名字就夠晦澀了,沒想到它的本名還更上一層樓...),Google那充滿狼子野心的瀏覽器。每一個含著金勺子出生的人都免不了被仰慕並被唾罵,Chrome也不例外。關於Chrome的優劣好壞討論的太多了,基本已經被嚼成甘蔗渣了,沒有人願意再多張一口了。俗話說,內行看門道外行看熱鬧,大部分所謂的外行,是通過使用的真實感受來評定優劣的,這無疑是最好的方式。但偏偏還是有自詡的內行,喜歡說內行話辦外行事,一看到Chrome用到多程序就說垃圾廢物肯定低能。拜託,大家都是搞技術的,你知道多程序的缺點,Google也知道,他們不是政客,除了搞個噱頭扯個蛋就一無所知了,人家也是有臉有皮的,寫一坨屎一樣的開原始碼放出來遭世人恥笑難道會很開心?所謂技術的優劣,是不能一概而論的,同樣的技術在不同場合不同環境不同程式碼實現下,效果是有所不同的。既然Chrome用了很多看上去不是很美的技術,我們是不是也需要了解一下它為什麼要用,怎麼用的,然後再開口說話?(恕不邀請,請自行對號入座...)。。。 人說是騾子是馬拉出來遛遛,Google已經把Chrome這匹驢子拉到了世人面前,大家可以隨意的遛。我們一直自詡是搞科學的,就是在努力和所謂的藝術家拉開,人搞超女評委的,可以隨意塞著屁眼用嘴放屁,楞把李天王說是李天后,你也只能說他是藝術品位獨特。你要搞科學就不行,說的不對,輕的叫無知,重的叫學術欺詐,結果一片慘淡。所以,既然程式碼都有了,再說話,就只能當點心注點意了,先看,再說。。。 我已經開始遛Chrome這頭驢了,確切一點,是頭壯碩的肥驢,專案總大小接近2G。這樣的龐然大物要從頭到腳每個毛孔的大量一遍,那估計不嚥氣也要吐血的,咱又不是做Code review,不需要如此拼命。每一個好的開源專案,都像是一個美女,這世界沒有十全十美的美女,自然也不會有樣樣傑出的開源專案。每個美女都有那麼一兩點讓你最心動不已或者倍感神祕的,你會把大部分的注意力都放在上面細細品味,看開源,也是一樣。Chrome對我來說,有吸引力的地方在於(排名分先後...):
但Chrome是一個跨平臺的瀏覽器,其Linux和Mac版本正在開發過程中,所以我把所有的眼光都放在了windows版本中,所有的程式碼剖析都是基於windows版本的。話說,我本是瀏覽器新手、win api白痴以及併發處理的火星人,為了我的好奇投身到這個溜驢的行業中來,難免有學的不到位看的走眼的時候,各位看官手下超生,有錯誤請指正,實在看不下去,回家自己牽著遛吧。。。
扯淡實在是個體力活,所以後面我會少扯淡多說問題。。。
關於Chrome的原始碼下載和環境配置,大家看這裡(windows版本),只想強調一點,一定要嚴格按照說明來配置環境,特別是vs2005的補丁和windows
SDK的安裝 ,否則肯定是編譯不過的。。。
最後,寫這部分唯一不是廢話的內容,請記住以下這幅圖,這是Chrome最精華的一個縮影,如果你還有空,一定要去這裡進行閱讀,其中重中之重是這一篇。。。
【一】 Chrome的多執行緒模型
0. Chrome的併發模型
如果你仔細看了前面的圖,對Chrome的執行緒和程序框架應該有了個基本的瞭解。Chrome有一個主程序,稱為Browser程序,它是老大,管理Chrome大部分的日常事務;其次,會有很多Renderer程序,它們圈地而治,各管理一組站點的顯示和通訊(Chrome在宣傳中一直宣稱一個tab對應一個程序,其實是很不確切的...),它們彼此互不搭理,只和老大說話,由老大負責權衡各方利益。它們和老大說話的渠道,稱做IPC(Inter-Process Communication),這是Google搭的一套程序間通訊的機制,基本的實現後面自會分解。。。
不論是Browser程序還是Renderer程序,都不只是光桿司令,它們都有一系列的執行緒為自己打理各種業務。對於Renderer程序,它們通常有兩個執行緒,一個是Main thread,它負責與老大進行聯絡,有一些幕後黑手的意思;另一個是Render thread,它們負責頁面的渲染和互動,一看就知道是這個幫派的門臉級人物。相比之下,Browser程序既然是老大,小弟自然要多一些,除了大腦般的Main thread,和負責與各Renderer幫派通訊的IO
thread,其實還包括負責管檔案的file thread,負責管資料庫的db thread等等(一個更詳細的列表,參見這裡),它們各盡其責,齊心協力為老大打拼。它們和各Renderer程序的之間的關係不一樣,同一個程序內的執行緒,往往需要很多的協同工作,這一坨執行緒間的併發管理,是Chrome最出彩的地方之一了。。。
1. Chrome的執行緒模型
仔細回憶一下我們大部分時候是怎麼來用執行緒的,在我足夠貧瘠的多執行緒經歷中,往往都是這樣用的:起一個執行緒,傳入一個特定的入口函式,看一下這個函式是否是有副作用的(Side Effect),如果有,並且還會涉及到多執行緒的資料訪問,仔細排查,在可疑地點上鎖伺候。。。
Chrome的執行緒模型走的是另一個路子,即,極力規避鎖的存在。換更精確的描述方式來說,Chrome的執行緒模型,將鎖限制了極小的範圍內(僅僅在將Task放入訊息佇列的時候才存在...),並且使得上層完全不需要關心鎖的問題(當然,前提是遵循它的程式設計模型,將函式用Task封裝併發送到合適的執行緒去執行...),大大簡化了開發的邏輯。。。
不過,從實現來說,Chrome的執行緒模型並沒有什麼神祕的地方(美女嘛,都是穿衣服比不穿衣服更有盼頭...),它用到了訊息迴圈的手段。每一個Chrome的執行緒,入口函式都差不多,都是啟動一個訊息迴圈(參見MessagePump類),等待並執行任務。而其中,唯一的差別在於,根據執行緒處理事務類別的不同,所起的訊息迴圈有所不同。比如處理程序間通訊的執行緒(注意,在Chrome中,這類執行緒都叫做IO執行緒,估計是當初設計的時候誰的腦門子拍錯了...)啟用的是MessagePumpForIO類,處理UI的執行緒用的是MessagePumpForUI類,一般的執行緒用到的是MessagePumpDefault類(只討論windows,
windows, windows...)。不同的訊息迴圈類,主要差異有兩個,一是訊息迴圈中需要處理什麼樣的訊息和任務,第二個是迴圈流程(比如是死迴圈還是阻塞在某訊號量上...)。下圖是一個完整版的Chrome訊息迴圈圖,包含處理Windows的訊息,處理各種Task(Task是什麼,稍後揭曉,敬請期待...),處理各個訊號量觀察者(Watcher),然後阻塞在某個訊號量上等待喚醒。。。
當然,不是每一個訊息迴圈類都需要跑那麼一大圈的,有些執行緒,它不會涉及到那麼多的事情和邏輯,白白浪費體力和時間,實在是不可饒恕的。因此,在實現中,不同的MessagePump類,實現是有所不同的,詳見下表:
現在我就是來抱大腿的,這條粗腿隸屬於Chrome(開源專案名稱其實是Chromium,本來Chrome這個名字就夠晦澀了,沒想到它的本名還更上一層樓...),Google那充滿狼子野心的瀏覽器。每一個含著金勺子出生的人都免不了被仰慕並被唾罵,Chrome也不例外。關於Chrome的優劣好壞討論的太多了,基本已經被嚼成甘蔗渣了,沒有人願意再多張一口了。俗話說,內行看門道外行看熱鬧,大部分所謂的外行,是通過使用的真實感受來評定優劣的,這無疑是最好的方式。但偏偏還是有自詡的內行,喜歡說內行話辦外行事,一看到Chrome用到多程序就說垃圾廢物肯定低能。拜託,大家都是搞技術的,你知道多程序的缺點,Google也知道,他們不是政客,除了搞個噱頭扯個蛋就一無所知了,人家也是有臉有皮的,寫一坨屎一樣的開原始碼放出來遭世人恥笑難道會很開心?所謂技術的優劣,是不能一概而論的,同樣的技術在不同場合不同環境不同程式碼實現下,效果是有所不同的。既然Chrome用了很多看上去不是很美的技術,我們是不是也需要了解一下它為什麼要用,怎麼用的,然後再開口說話?(恕不邀請,請自行對號入座...)。。。 人說是騾子是馬拉出來遛遛,Google已經把Chrome這匹驢子拉到了世人面前,大家可以隨意的遛。我們一直自詡是搞科學的,就是在努力和所謂的藝術家拉開,人搞超女評委的,可以隨意塞著屁眼用嘴放屁,楞把李天王說是李天后,你也只能說他是藝術品位獨特。你要搞科學就不行,說的不對,輕的叫無知,重的叫學術欺詐,結果一片慘淡。所以,既然程式碼都有了,再說話,就只能當點心注點意了,先看,再說。。。 我已經開始遛Chrome這頭驢了,確切一點,是頭壯碩的肥驢,專案總大小接近2G。這樣的龐然大物要從頭到腳每個毛孔的大量一遍,那估計不嚥氣也要吐血的,咱又不是做Code review,不需要如此拼命。每一個好的開源專案,都像是一個美女,這世界沒有十全十美的美女,自然也不會有樣樣傑出的開源專案。每個美女都有那麼一兩點讓你最心動不已或者倍感神祕的,你會把大部分的注意力都放在上面細細品味,看開源,也是一樣。Chrome對我來說,有吸引力的地方在於(排名分先後...):
1. 它是如何利用多程序(其實也會有多執行緒一起)做併發的,又是如何解決多程序間的一些問題的,比如程序間通訊,程序的開銷;
2. 做為一個後來者,它的擴充套件能力如何,如何去權衡對原有外掛的相容,提供怎麼樣的一個外掛模型;
3. 它的整體框架是怎樣,有沒有很NB的架構思想;
4. 它如何實現跨平臺的UI控制元件系統;
5. 傳說中的V8,為啥那麼快。
圖1 Chrome的執行緒和程序模型 |
【一】 Chrome的多執行緒模型
0. Chrome的併發模型
如果你仔細看了前面的圖,對Chrome的執行緒和程序框架應該有了個基本的瞭解。Chrome有一個主程序,稱為Browser程序,它是老大,管理Chrome大部分的日常事務;其次,會有很多Renderer程序,它們圈地而治,各管理一組站點的顯示和通訊(Chrome在宣傳中一直宣稱一個tab對應一個程序,其實是很不確切的...),它們彼此互不搭理,只和老大說話,由老大負責權衡各方利益。它們和老大說話的渠道,稱做IPC(Inter-Process Communication),這是Google搭的一套程序間通訊的機制,基本的實現後面自會分解。。。
Chrome的程序模型
|
閒話併發
在第二種場合下,我們會自然而然的關注資料的分離,從而很好的利用上多CPU的能力;而在第一種場合,我們習慣了單CPU的模式,往往不注重資料與行為的對應關係,導致在多CPU的場景下,效能不升反降。。。 |
1. Chrome的執行緒模型
仔細回憶一下我們大部分時候是怎麼來用執行緒的,在我足夠貧瘠的多執行緒經歷中,往往都是這樣用的:起一個執行緒,傳入一個特定的入口函式,看一下這個函式是否是有副作用的(Side Effect),如果有,並且還會涉及到多執行緒的資料訪問,仔細排查,在可疑地點上鎖伺候。。。
Chrome的執行緒模型走的是另一個路子,即,極力規避鎖的存在。換更精確的描述方式來說,Chrome的執行緒模型,將鎖限制了極小的範圍內(僅僅在將Task放入訊息佇列的時候才存在...),並且使得上層完全不需要關心鎖的問題(當然,前提是遵循它的程式設計模型,將函式用Task封裝併發送到合適的執行緒去執行...),大大簡化了開發的邏輯。。。
不過,從實現來說,Chrome的執行緒模型並沒有什麼神祕的地方(美女嘛,都是穿衣服比不穿衣服更有盼頭...),它用到了訊息迴圈的手段。每一個Chrome的執行緒,入口函式都差不多,都是啟動一個訊息迴圈(參見MessagePump類),等待並執行任務。而其中,唯一的差別在於,根據執行緒處理事務類別的不同,所起的訊息迴圈有所不同。比如處理程序間通訊的執行緒(注意,在Chrome中,這類執行緒都叫做IO執行緒,估計是當初設計的時候誰的腦門子拍錯了...)啟用的是MessagePumpForIO類,處理UI的執行緒用的是MessagePumpForUI類,一般的執行緒用到的是MessagePumpDefault類(只討論windows,
windows, windows...)。不同的訊息迴圈類,主要差異有兩個,一是訊息迴圈中需要處理什麼樣的訊息和任務,第二個是迴圈流程(比如是死迴圈還是阻塞在某訊號量上...)。下圖是一個完整版的Chrome訊息迴圈圖,包含處理Windows的訊息,處理各種Task(Task是什麼,稍後揭曉,敬請期待...),處理各個訊號量觀察者(Watcher),然後阻塞在某個訊號量上等待喚醒。。。
圖2 Chrome的訊息迴圈 |