深入V8引擎-預設Platform之mac篇(1)
又到了常規的堆砌程式碼湊文章字數環節,很多API我就直接貼官方的英文釋義,個人翻譯其實有時候並不是很準確,搞錯了甚至會誤導,還是儘量自己去理解。
首先看看入口方法。
std::unique_ptr<v8::Platform> platform = v8::platform::NewDefaultPlatform(); // int型別 thread_pool_size => 0 // 列舉值 idle_task_support => kDisabled // 列舉值 in_process_stack_dumping => kDisabled // 類 tracing_controller => NULL std::unique_ptr<v8::Platform> NewDefaultPlatform( int thread_pool_size, IdleTaskSupport idle_task_support, InProcessStackDumping in_process_stack_dumping, std::unique_ptr<v8::TracingController> tracing_controller) { // 不會進這裡 if (in_process_stack_dumping == InProcessStackDumping::kEnabled) { v8::base::debug::EnableInProcessStackDumping(); } // 1 std::unique_ptr<DefaultPlatform> platform(new DefaultPlatform(idle_task_support, std::move(tracing_controller))); // 2 platform->SetThreadPoolSize(thread_pool_size); // 3 platform->EnsureBackgroundTaskRunnerInitialized(); return std::move(platform); }
這裡比較頭疼的是我沒找到NewDefaultPlatform方法是在哪裡定義的,所以預設引數不知道是什麼,只能打斷點除錯看變數值,已經在註釋標註了。
預設引數情況下,那個if分支是不會進去的,所以無視,後面的三個語句都各自負責了一部門功能,分別是初始化預設Platform物件、設定執行緒池大小、啟動工作執行緒,分塊來看各部分原始碼。
第一條語句直接用new構造了一個DefaultPlatform物件,如下,加了一些標註。
// 1 DefaultPlatform::DefaultPlatform( IdleTaskSupport idle_task_support, std::unique_ptr<v8::TracingController> tracing_controller) : thread_pool_size_(0), idle_task_support_(idle_task_support), tracing_controller_(std::move(tracing_controller)), // 1-1 page_allocator_(new v8::base::PageAllocator()), time_function_for_testing_(nullptr) { if (!tracing_controller_) { // 1-2 tracing::TracingController* controller = new tracing::TracingController(); // 1-3 controller->Initialize(nullptr); // 智慧指標替換 tracing_controller_.reset(controller); } }
這是接受兩個引數的建構函式,而且DefaultPlatform只有這一個建構函式。除了用給定的2個引數初始化屬性,一些其他屬性也用預設的引數初始化了,對於0、nullptr這種就不用管,其中比較特殊的是那個page_allocator初始化,上一篇給出的類宣告是基類,在V8的名稱空間有一個同名的實現類。
class PageAllocator : public ::v8::PageAllocator { // ... private: const size_t allocate_page_size_; const size_t commit_page_size_; } PageAllocator::PageAllocator() : allocate_page_size_(base::OS::AllocatePageSize()), commit_page_size_(base::OS::CommitPageSize()) {}
我也是服了V8,弄了個同名的類,第一次看楞了好久。建構函式呼叫的是OS名稱空間的方法,這個名稱空間是用來取一些系統引數,呼叫mac系統上<unistd.h>標頭檔案的一些API,看一下allocate_page_size的初始化就明白了。
size_t OS::AllocatePageSize() { return static_cast<size_t>(sysconf(_SC_PAGESIZE)); }
這裡用了一個sysconf方法,在其他的很多地方也有使用,官方解釋如下。
get configuration information at run time
當成NODE_ENV來理解就差不多了,也就是一個獲取系統配置引數的API,對於PageAllocator的兩個屬性,官方的解釋依次如下。
1、Size of a page in bytes. Must not be less than 1.2、memory page size
兩個其實都是page size(記憶體頁大小,關於Linux的記憶體模型我不太懂,後面有空再去了解),我本地測試了一下,都返回的4096。
對屬性初始化完後,建構函式會繼續走程式碼塊裡的語句,由於外部傳進來的tracing_controller是個NULL,所以這裡還需要手動new一個。
TracingController::TracingController() = default;
然而這個建構函式沒啥好講的,因為是預設建構函式,所以直接跳過了,後面的兩步也沒什麼講的,reset是智慧指標的API,替換管理內容。
下面是第二條語句,從命名直接能看出來了,就是設定執行緒池的大小,方法也比較簡單暴力了。
// 2 void DefaultPlatform::SetThreadPoolSize(int thread_pool_size) { base::MutexGuard guard(&lock_); DCHECK_GE(thread_pool_size, 0); if (thread_pool_size < 1) { // The number of processors currently online (available) => 4 thread_pool_size = base::SysInfo::NumberOfProcessors() - 1; } // max(min(8, size), 1) thread_pool_size_ = std::max(std::min(thread_pool_size, kMaxThreadPoolSize), 1); }
這裡實際上也是呼叫了一個類似於上面的sysconf來獲取系統引數,返回的系統處理器數量,由於其中一個要用來作為主執行緒,所以可用的執行緒池數量要減一,簡單處理一下返回一個size。
第三條語句內容相當的麻煩,看得我腦子疼,語義上理解就是保證後臺執行緒runner的初始化執行。
說得簡單,由於之前的操作只是初始化了一個空白platform類,算了算執行緒池的大小,所以剩下的所有實際操作都在這裡。大概包含了初始化執行緒池、生成執行緒同時分配任務、管理task佇列、啟動執行緒等一系列操作,其中的思想倒並不複雜,實際上跟libuv的非同步原理相差無幾,但是深入原始碼的每一步還是挺噁心的,下一篇再來