nodejs深入學習系列之v8基礎(chǔ)篇

V8這個概念大家都不陌生了辩诞,那么你動手編譯過V8源碼嗎唇兑?編譯后有嘗試去了解V8背后的一些概念嗎酒朵?如果沒有,那么也不用心慌扎附,下文將跟大家一一解釋這些東西蔫耽。在編譯V8之前我們先要了解一個東西-構(gòu)建系統(tǒng)

1、構(gòu)建系統(tǒng)

1.1留夜、構(gòu)建系統(tǒng)是啥匙铡?

寫慣前端的童鞋可能不是很明白這個東西是干啥用的?但是其實平時你都會接觸到碍粥,只是概念不同而已鳖眼。前端我們一般稱其為打包構(gòu)建,類似工具諸如webpack嚼摩、parcel做的事情钦讳。其實最后的目標都是想得到一些目標性的文件。這里可以簡單地提及一下軟件工程中的構(gòu)建系統(tǒng)的歷史低斋。

構(gòu)建系統(tǒng)的需求是隨著軟件規(guī)模的增大而提出的。如果只是做簡單的demo匪凡,通常代碼量比較小膊畴,編寫的源代碼只有幾個文件。比如你編寫了一段代碼放入helloworld.cpp文件中病游,要編譯這段代碼唇跨,只需要執(zhí)行以下命令:

g++ helloworld.c -o helloworld

當軟件規(guī)模逐漸增加稠通,這時可能有幾十個源代碼文件,而且有了模塊劃分买猖,有的要編譯成靜態(tài)庫改橘,有的要編譯成動態(tài)庫,最后鏈接成可執(zhí)行代碼玉控,這時命令行方式就捉襟見肘飞主,需要一個構(gòu)建系統(tǒng)。常見的構(gòu)建系統(tǒng)有GNU Make高诺。需要注意的是碌识,構(gòu)建系統(tǒng)并不是取代gcc這樣的工具鏈,而是定義編譯規(guī)則虱而,最終還是會調(diào)用工具鏈編譯代碼筏餐。

當軟件規(guī)模進一步擴大,特別是有多平臺支持需求的時候牡拇,編寫GNU Makefile將是一件繁瑣和乏味的事情魁瞪,而且極容易出錯。這時就出現(xiàn)了生成Makefile的工具惠呼,比如Cmake导俘、AutoMake等等,這種構(gòu)建系統(tǒng)稱作元構(gòu)建系統(tǒng)(meta build system)罢杉。在Linux上軟件倉庫的概念還沒有普及的時候趟畏,通常我們安裝軟件的步驟是:

./configure
make
make install

第一步就是調(diào)用一些自動化工具,根據(jù)系統(tǒng)環(huán)境(系統(tǒng)的版本眾多滩租,軟件安裝情況也不一樣)赋秀,生成GNU Makefile。然后第二步才使用gcc或者g++命令去編譯所有文件律想,最后一步便是將所有文件鏈接起來成可執(zhí)行命令并安裝到系統(tǒng)的某個指定目錄猎莲。

一般后兩個步驟都是比較固化的,能提高工作效率的也就是在第一步了技即。于是V8團隊針對自己的項目特點著洼,擼了一個叫做GYP(Generate Your Projects)的構(gòu)建系統(tǒng),后面你要是看到node-gyp其實就是基于這個做的js版本而叼。不過后面GYP被v8團隊廢棄掉身笤,改用GN(Generate Ninja)構(gòu)建系統(tǒng)。二者的區(qū)別不是本文重點葵陵,有興趣的童鞋可以查看這篇文章: chromium中的GN構(gòu)建系統(tǒng)液荸。

有意思的是盡管v8徹底廢棄掉了GYP,但是nodejs仍然在使用GYP脱篙,這個R大在創(chuàng)建deno項目的時候有提及到:Design Mistakes in Node娇钱。

image

1.1.1伤柄、GN構(gòu)建系統(tǒng)簡介

GN(Generate Ninja)是chromium project用來取代GYP的新工具,由于GN是用C++編寫文搂,比起用 python寫的GYP快了很多适刀,GN新的DSL的語法也被認為是比較好寫以及維護的。

在v8項目的根目錄下有個.gn文件煤蹭,內(nèi)容如下(去掉所有注釋了):

import("http://build/dotfile_settings.gni")
buildconfig = "http://build/config/BUILDCONFIG.gn"
check_targets = []
exec_script_whitelist = build_dotfile_settings.exec_script_whitelist + []

我們關(guān)注buildconfig這個配置笔喉。.gn所在的目錄會被GN工具認定是項目的根目錄,.gn的內(nèi)容基本就是用buildconfig來指定build config的位置疯兼,其中//build//config/BUILDCONFIG.gn是相對于項目根目錄下路徑的配置文件然遏。

但是你會發(fā)現(xiàn)現(xiàn)在v8源碼目錄下并沒有叫做build的目錄,這個目錄要咋生成呢吧彪?這些知識我們會在稍后的編譯v8代碼中提及待侵。

假設(shè)現(xiàn)在你有build目錄了,我們找到BUILDCONFIG.gn文件姨裸,文件里面會根據(jù)系統(tǒng)和平臺設(shè)置對應的編譯工具鏈:

... ...

if (custom_toolchain != "") {
  set_default_toolchain(custom_toolchain)
} else if (_default_toolchain != "") {
  set_default_toolchain(_default_toolchain)
}

... ...

比如得到的_default_toolchain值為:_default_toolchain = "http://build/toolchain/linux:clang_x86秧倾,那么你在build/toolchain/linux目錄下的BUILD.gn可以找到這么一個配置:

clang_toolchain("clang_x86") {
  # Output linker map files for binary size analysis.
  enable_linker_map = true

  toolchain_args = {
    current_cpu = "x86"
    current_os = "linux"
  }
}

因為GN沒有內(nèi)建的toolchain規(guī)則,toolchain里的各種tool例如 cc,cxx,link等必須自己指定傀缩,指定的文件是build/toolchain/gcc_toolchain.gni文件那先,在文件中我們可以看到GN給定義的一些動作:

tool("cc") {
  depfile = "{{output}}.d"
  precompiled_header_type = "gcc"
  command = "$cc -MMD -MF $depfile ${rebuild_string}{{defines}} {{include_dirs}} {{cflags}} {{cflags_c}}${extra_cppflags}${extra_cflags} -c {{source}} -o {{output}}"
  depsformat = "gcc"
  description = "CC {{output}}"
  outputs = [
    "$object_subdir/{{source_name_part}}.o",
  ]
}

最后項目根目錄下會有一個BUILD.gn的文件,指定生成可執(zhí)行文件的指令赡艰,比如:

v8_executable("v8_hello_world") {
  sources = [
    "samples/hello-world.cc",
  ]

  configs = [
    # Note: don't use :internal_config here because this target will get
    # the :external_config applied to it by virtue of depending on :v8, and
    # you can't have both applied to the same target.
    ":internal_config_base",
  ]

  deps = [
    ":v8",
    ":v8_libbase",
    ":v8_libplatform",
    "http://build/win:default_exe_manifest",
  ]
}

這樣一套完整的GN構(gòu)建系統(tǒng)便完成了售淡。

1.1.2、Ninja構(gòu)建系統(tǒng)

有了GN慷垮,為啥還要Ninja呢揖闸?剛才我們知道GN的英文意思是Generator Ninja,可見GN生成的東西并不是我們最終GNU Makefile形式料身。而Ninja才是最后生成Makefile的終極法器汤纸。Ninja 作為一個新型的編譯工具,小巧而又高效芹血,據(jù)谷歌官方的說法是速度有了好幾倍的提升贮泞。

這個時候我們還沒有生成任何的Ninja文件,需要我們使用GN命令去生成:

gn args out/foo

這下子你在out/foo下就可以看到好多ninja文件:

image

Ninja使用build.ninja文件來定義構(gòu)建規(guī)則幔烛,和Makefile里的元編程不同啃擦,build.ninja幾乎是完全靜態(tài)的,動態(tài)生成依賴其他工具饿悬,如gn或者CMake令蛉。

build.ninja

build.niinja相當于ninja的makefile,一個簡單的build.ninja文件如下乡恕,分為rule和dependency兩部分言询。

image

phony: 可以創(chuàng)建其他target的別名。

default: 如果沒有在命令行中指定target傲宜,可以使用default來指定默認的target运杭。

pools: 為了支持并發(fā)作業(yè),Ninja還支持pool的機制函卒,和用-j并行模式一樣辆憔。

Make vs Ninja Performance Comparison將Ninja和Make進行了測試對比。

2报嵌、編譯并測試V8代碼

接下來我們開始進行v8代碼的編譯操作虱咧。官網(wǎng)的文檔給的已經(jīng)很齊全了,這里只是再簡單說一下锚国,并提及一些官網(wǎng)沒有給出的基本知識腕巡。

2.1、下載v8代碼

這一步注意了血筑,不要直接從v8倉庫使用git clone命令下載代碼绘沉,這樣下載下來的代碼是無效的,會缺失很多東西豺总,要使用官方提供的工具depot_tools

整個步驟匯總?cè)缦拢?/p>

git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
export PATH=$PATH:/path/to/depot_tools
gclient config https://chromium.googlesource.com/v8/v8
gclient sync
mkdir ~/v8
cd ~/v8
fetch v8
cd v8

2.2车伞、編譯v8代碼

編譯v8代碼官網(wǎng)同樣給的很詳細:傳送門,這里總結(jié)一下而已喻喳,有兩種編譯方式

2.2.1另玖、超便捷方式

使用gm這個集成所有為一體的python腳本可以幾個命令就搞定:

alias gm=/path/to/v8/tools/dev/gm.py
gm x64.release
gm x64.release.check

2.2.2、手動編譯方式

按照我們之前說的流程表伦,我們需要使用GN去生成ninja文件谦去,再生成makefile,最后才是編譯绑榴,因此:

可以使用gn args out/foo或者gn gen out/foo --args='is_debug=false target_cpu="x64" v8_target_cpu="arm64" use_goma=true'來生成ninja文件哪轿。

這一行命令官網(wǎng)沒有詳細解釋,我在這里解釋一下:

gn args out/foo => 通過參數(shù)形式指定輸出目錄翔怎,這個命令會彈出文本讓你配置參數(shù)
gn gen out/foo => 指定GN構(gòu)建輸出的目錄, 可以指定參數(shù): --args='is_debug=false target_cpu="x64" v8_target_cpu="arm64" use_goma=true'窃诉,這個命令不會彈出文本窗讓你配置
gn args out/foo --list => 查看這個構(gòu)建輸出目錄當時配置的參數(shù)

如果嫌上面的方式麻煩,那么v8還提供了另外一個腳本來集成這些步驟:v8gen赤套,命令如下:

alias v8gen=/path/to/v8/tools/dev/v8gen.py
v8gen -b 'V8 Linux64 - debug builder' -m client.v8 foo

v8gen的原理是借助mb_config.pyl文件飘痛。根據(jù)master配置(-m)和builder配置(-b)來生成編譯文件,我們在mb_config.pyl找到對應的配置:

image

最后一個參數(shù)foo是指定生成的二級目錄容握,默認一級目錄是out.gn宣脉,如下:

image

你也可以使用默認配置,直接v8gen foo

接下去使用ninja來編譯:

ninja -C out/foo

如果想要指定生成指定目標則:

ninja -C out/foo d8

上述編譯正常會報錯:goma/gomacc: No such file or directory剔氏。因為我們本地沒有安裝goma塑猖,所以想要正常編譯下去竹祷,還需要安裝一下goma,goma是什么東西呢羊苟?從官網(wǎng)上看塑陵,它是一個輔助編譯加速的工具,詳細可以參考:goma

3蜡励、編譯單個引用到v8庫的C++文件

除了上述整體v8工程編譯令花,如果你想利用v8編譯單個文件的話,比如在官網(wǎng)提到的編譯Hello.cc中使用到了g++命令凉倚,對于g++命令有些參數(shù)是你必須了解的兼都,這里整理了一份,請參考:

g++ -I. -Iinclude samples/hello-world.cc -o hello_world -lv8_monolith -Lout.gn/x64.release.sample/obj/ -pthread -std=c++0x

G++命令解釋如下:

-std=
    決定使用的語言標準稽寒,當編譯C和C++的時候該選擇支持配置扮碧。

  上述命令中的`c++0x`表示:
    語言標準使用即將發(fā)布的ISO c++ 0x標準的工作草案。此選項支持可能包含在c++ 0x中的實驗性特性杏糙。工作草案在不斷地變化芬萍,如果GCC的未來版本不屬于c++ 0x標準,那么由這個標志啟用的任何特性都可能被刪除搔啊。

  更多標準請參考:[g++](https://linux.die.net/man/1/g++)

-pthread
  使用POSIX線程庫添加對多線程的支持柬祠。此選項為預處理器和鏈接器設(shè)置標志。它不影響編譯器生成的目標代碼的線程安全性负芋,也不影響與其提供的庫的線程安全性漫蛔。這些是特定于HP-UX的標志。

-I dir
  將目錄dir添加到要搜索頭文件的目錄列表中旧蛾。在系統(tǒng)標準包含目錄之前莽龟,搜索由**-I**指定的目錄。如果目錄*dir*是標準的系統(tǒng)包含目錄锨天,則忽略該選項毯盈,以確保不會破壞系統(tǒng)目錄的默認搜索順序和對系統(tǒng)頭文件的特殊處理。如果*dir*以"="開頭病袄,則"="將被sysroot前綴替換搂赋。

-o file
指定輸出文件。這與將file指定為cpp的第二個非選項參數(shù)相同益缠。gcc 對第二個非選項參數(shù)的有另一種解釋脑奠,因此必須使用-o指定輸出文件

-llibrary
-l library
  鏈接時搜索名為library的庫。(第二種指定庫文件的方式僅適用于POSIX遵從性幅慌,不建議使用宋欺。)
  在命令中編寫這個選項的位置會有所不同;鏈接器按照指定的順序搜索和處理庫和目標文件。因此,`foo.o -lz bar.o`是在文件foo.o之后搜索庫z。但在bar.o之前齿诞。如果bar.o是引用到了z庫中的函數(shù)酸休,這些函數(shù)是不能被加載。
  鏈接器搜索庫的標準目錄列表祷杈,實際上是一個名為`liblibrary.a`的文件雨席。然后鏈接器使用這個文件,就好像它是通過名稱精確指定的一樣吠式。

  搜索的目錄包括幾個標準系統(tǒng)目錄,以及您使用-L指定的任何目錄抽米。
  通常以這種方式找到的文件是庫文件——其成員是目標文件的歸檔文件特占。鏈接器通過掃描成員來處理存檔文件,這些成員定義了到目前為止已經(jīng)引用但尚未定義的符號云茸。但是是目,如果找到的文件是一個普通的對象文件,則以通常的方式鏈接它标捺。
-Ldir
  添加`dir`目錄到搜索目錄列表中去供`-l`使用

這樣上述命令想必一目了然了吧

4懊纳、v8引擎基本概念簡述

[譯文]V8學習的高級進階完整詳細地介紹了很多概念,這里只是再把這些概念簡化掉亡容,讓大家的記憶更加深刻嗤疯。

4.1、isolate

這個概念在[譯文]V8學習的高級進階沒有提及到闺兢,它表示的一個獨立的V8虛擬機茂缚,擁有自己的堆棧。所以才取名isolate屋谭,意為“隔離”脚囊。在v8中使用以下語法進行初始化:

Isolate* isolate = Isolate::New(create_params);

4.2、handle

handle是指向?qū)ο蟮闹羔樛┐牛赩8中悔耘,所有的對象都通過handle來引用,handle主要用于V8的垃圾回收機制我擂。在 V8 中衬以,handle 分為兩種:持久化 (Persistent)handle 和本地 (Local)handle,持久化 handle 存放在堆上校摩,而本地 handle 存放在棧上泄鹏。比如我要使用本地句柄,句柄指向的內(nèi)容是一個string秧耗,那么你要這么定義:

Local<String> source = String::NewFromUtf8(isolate, "'Hello' + ', World'", NewStringType::kNormal).ToLocalChecked();

鑒于一個個釋放Handle比較麻煩备籽,v8又提供了HandleScope來批量處理,你可以在handle之前聲明好:

HandleScope handle_scope(isolate);

4.3、context

context 是一個執(zhí)行器環(huán)境车猬,使用 context 可以將相互分離的 JavaScript 腳本在同一個 V8 實例中運行霉猛,而互不干涉。在運行 JavaScript 腳本是珠闰,需要顯式的指定 context 對象惜浅。創(chuàng)建上下文,需要這樣:

// 創(chuàng)建一個上下文
Local<Context> context = Context::New(isolate);

// 進入上下文編譯和運行腳本
Context::Scope context_scope(context);

4.4伏嗜、V8的數(shù)據(jù)類型

由于 C++ 原生數(shù)據(jù)類型與 JavaScript 中數(shù)據(jù)類型有很大差異坛悉,因此 V8 提供了 Data 類,從 JavaScript 到 C++承绸,從 C++ 到 JavaScrpt 都會用到這個類及其子類裸影,比如:

String::NewFromUtf8(info.GetIsolate(), "version").ToLocalChecked()

這里的String便是V8的數(shù)據(jù)類型。再比如:

v8::Integer::New(info.GetIsolate(), 10);

4.5军熏、對象模板和函數(shù)模板

這兩個模板類用以定義 JavaScript 對象和 JavaScript 函數(shù)轩猩。我們在后續(xù)的小節(jié)部分將會接觸到模板類的實例。通過使用 ObjectTemplate荡澎,可以將 C++ 中的對象暴露給腳本環(huán)境均践,類似的,F(xiàn)unctionTemplate 用以將 C++ 函數(shù)暴露給腳本環(huán)境摩幔,以供腳本使用彤委。

最后

就此,對于v8的了解應該有了一定的雛形了或衡,v8里面有很多重要的概念葫慎,想要繼續(xù)深入的可以參考另外一篇v8的實際應用文章了:如何正確地使用v8嵌入到我們的C++應用中

參考

  1. chromium中的GN構(gòu)建系統(tǒng)
  2. GYP,GN和Ninja
  3. depot_tools_tutorial(7) Manual Page
  4. GN Reference
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末薇宠,一起剝皮案震驚了整個濱河市偷办,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌澄港,老刑警劉巖椒涯,帶你破解...
    沈念sama閱讀 222,000評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異回梧,居然都是意外死亡废岂,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評論 3 399
  • 文/潘曉璐 我一進店門狱意,熙熙樓的掌柜王于貴愁眉苦臉地迎上來湖苞,“玉大人,你說我怎么就攤上這事详囤〔乒牵” “怎么了镐作?”我有些...
    開封第一講書人閱讀 168,561評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長隆箩。 經(jīng)常有香客問我该贾,道長,這世上最難降的妖魔是什么捌臊? 我笑而不...
    開封第一講書人閱讀 59,782評論 1 298
  • 正文 為了忘掉前任杨蛋,我火速辦了婚禮,結(jié)果婚禮上理澎,老公的妹妹穿的比我還像新娘逞力。我一直安慰自己,他們只是感情好糠爬,可當我...
    茶點故事閱讀 68,798評論 6 397
  • 文/花漫 我一把揭開白布寇荧。 她就那樣靜靜地躺著,像睡著了一般秩铆。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上灯变,一...
    開封第一講書人閱讀 52,394評論 1 310
  • 那天殴玛,我揣著相機與錄音,去河邊找鬼添祸。 笑死滚粟,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的刃泌。 我是一名探鬼主播凡壤,決...
    沈念sama閱讀 40,952評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼耙替!你這毒婦竟也來了亚侠?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,852評論 0 276
  • 序言:老撾萬榮一對情侶失蹤俗扇,失蹤者是張志新(化名)和其女友劉穎硝烂,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體铜幽,經(jīng)...
    沈念sama閱讀 46,409評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡滞谢,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,483評論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了除抛。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片狮杨。...
    茶點故事閱讀 40,615評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖到忽,靈堂內(nèi)的尸體忽然破棺而出橄教,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 36,303評論 5 350
  • 正文 年R本政府宣布颤陶,位于F島的核電站颗管,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏滓走。R本人自食惡果不足惜垦江,卻給世界環(huán)境...
    茶點故事閱讀 41,979評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望搅方。 院中可真熱鬧比吭,春花似錦、人聲如沸姨涡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽涛漂。三九已至赏表,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間匈仗,已是汗流浹背瓢剿。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留悠轩,地道東北人间狂。 一個月前我還...
    沈念sama閱讀 49,041評論 3 377
  • 正文 我出身青樓,卻偏偏與公主長得像火架,于是被迫代替她去往敵國和親鉴象。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,630評論 2 359

推薦閱讀更多精彩內(nèi)容

  • 前言 如今大前端代表之一flutter十分火熱何鸡,也是一種大的趨勢纺弊。flutter與rn對大前端上的理解不同,rn是...
    yjy239閱讀 7,751評論 6 14
  • 本文同步發(fā)表在豆米的博客 學習完nodejs基石之一的v8基礎(chǔ)篇(還沒看過的童鞋請?zhí)D(zhuǎn)到這里:nodejs深入學習...
    小兀666閱讀 1,797評論 0 2
  • 源代碼下載 前提條件是打開ss骡男,不然毛都下載不下來 google重新發(fā)明了一套下載俭尖、編譯、測試自家項目的工具(輪子...
    cgnail閱讀 4,997評論 0 2
  • 生活中洞翩,我很喜歡走走看看稽犁。走走看看,滿足了我日成б冢活動的需求已亥。 昨夜微雨,早晨起來来屠,空氣濕潤潤的虑椎≌痧模空氣中那一陣陣涼絲...
    青青姐愛攝影閱讀 663評論 5 16
  • “花開堪折直須折,莫等無花空折枝”捆姜。說的就是做人要懂得珍惜擁有传趾。 青春正年少,就努力吧泥技,學習各種知...
    且行且進且珍惜閱讀 641評論 2 5