v8的初始化

isolate.cc中定義的
struct StaticInitializer {
  StaticInitializer() {
    Isolate::EnsureDefaultIsolate();
  }
} static_initializer;
這個變量是一個Global變量,在C++初始化,早於main函數被調用之前就被調用了。
Isolate::EnsureDefaultIsolate()主要初始化Isolate中與Thread相關的一些靜態變量
  // defined at isolate.h
  static Thread::LocalStorageKey per_isolate_thread_data_key_;
  static Thread::LocalStorageKey isolate_key_;
  static Thread::LocalStorageKey thread_id_key_;
  static Isolate* default_isolate_;
  static ThreadDataTable* thread_data_table_;

static inline bool EnsureInitializedForIsolate(i::Isolate* isolate,const char* location)
確保Isolate進行了初始化,如果沒有,那麼會調用InitializeHelper進行初始化,該函數會調用
bool V8::Initialize(Deserializer* des) {
  InitializeOncePerProcess();//1

  // The current thread may not yet had entered an isolate to run.
  // Note the Isolate::Current() may be non-null because for various
  // initialization purposes an initializing thread may be assigned an isolate
  // but not actually enter it.
  if (i::Isolate::CurrentPerIsolateThreadData() == NULL) {
    i::Isolate::EnterDefaultIsolate();//2
  }
  
  。。。

  return isolate->Init(des);//3
}
該函數主要有三點可說:
1.InitializeOncePerProcess
3.isolate->Init(des)

InitializeOncePerProcess最終會調用
void V8::InitializeOncePerProcessImpl() {
  FlagList::EnforceFlagImplications();
  if (FLAG_stress_compaction) {
    FLAG_force_marking_deque_overflows = true;
    FLAG_gc_global = true;
    FLAG_max_new_space_size = (1 << (kPageSizeBits - 10)) * 2;
  }
  if (FLAG_trace_hydrogen) FLAG_parallel_recompilation = false;
  OS::SetUp();
  CPU::SetUp();
  use_crankshaft_ = FLAG_crankshaft
      && !Serializer::enabled()
      && CPU::SupportsCrankshaft();
  OS::PostSetUp();
  RuntimeProfiler::GlobalSetUp();
  ElementsAccessor::InitializeOncePerProcess();
  LOperand::SetUpCaches();
  SetUpJSCallerSavedCodeData();
  SamplerRegistry::SetUp();
  ExternalReference::SetUp();
}
其中CPU::SetUp函數會探測當前CPU的一些feature
// The Probe method needs executable memory, so it uses Heap::CreateCode.
// Allocation failure is silent and leads to safe default.
void CpuFeatures::Probe() {
  ASSERT(!initialized_);
  ASSERT(supported_ == 0);
#ifdef DEBUG
  initialized_ = true;
#endif
  if (Serializer::enabled()) {
    supported_ |= OS::CpuFeaturesImpliedByPlatform();
    return;  // No features if we might serialize.
  }

  const int kBufferSize = 4 * KB;
  VirtualMemory* memory = new VirtualMemory(kBufferSize);
  if (!memory->IsReserved()) {
    delete memory;
    return;
  }
  ASSERT(memory->size() >= static_cast<size_t>(kBufferSize));
  if (!memory->Commit(memory->address(), kBufferSize, true/*executable*/)) {
    delete memory;
    return;
  }

  Assembler assm(NULL, memory->address(), kBufferSize);
  Label cpuid, done;
#define __ assm.
  // Save old esp, since we are going to modify the stack.
  __ push(ebp);
  __ pushfd();
  __ push(ecx);
  __ push(ebx);
  __ mov(ebp, esp);

  // If we can modify bit 21 of the EFLAGS register, then CPUID is supported.
  __ pushfd();//把EFLAGS寄存器入棧
  __ pop(eax);
  __ mov(edx, eax);
  __ xor_(eax, 0x200000);  // Flip bit 21.
  __ push(eax);
  __ popfd();
  __ pushfd();
  __ pop(eax);
  //此時eax爲修改後的EFLAGS,edx爲修改前的EFLAGS
  __ xor_(eax, edx);  // Different if CPUID is supported.
  __ j(not_zero, &cpuid);

  // CPUID not supported. Clear the supported features in edx:eax.
  __ xor_(eax, eax);
  __ xor_(edx, edx);
  __ jmp(&done);

  // Invoke CPUID with 1 in eax to get feature information in
  // ecx:edx. Temporarily enable CPUID support because we know it's
  // safe here.
  __ bind(&cpuid);
  __ mov(eax, 1);
  supported_ = (1 << CPUID);
  { CpuFeatureScope fscope(&assm, CPUID);
    __ cpuid();
  }
  supported_ = 0;

  // Move the result from ecx:edx to edx:eax and make sure to mark the
  // CPUID feature as supported.
  __ mov(eax, edx);
  __ or_(eax, 1 << CPUID);
  __ mov(edx, ecx);

  // Done.
  __ bind(&done);
  __ mov(esp, ebp);
  __ pop(ebx);
  __ pop(ecx);
  __ popfd();
  __ pop(ebp);
  __ ret(0);
#undef __

  typedef uint64_t (*F0)();
  F0 probe = FUNCTION_CAST<F0>(reinterpret_cast<Address>(memory->address()));
  uint64_t probed_features = probe();//5.把返回地址強轉爲函數指針,執行之。
  uint64_t platform_features = OS::CpuFeaturesImpliedByPlatform();
  supported_ = probed_features | platform_features;
  found_by_runtime_probing_only_ = probed_features & ~platform_features;

  delete memory;
}
涉及VirtualMemeory的內容,可以參看--------------------,這裏只說明v8是如何探測CPU的,Assembler是一個彙編器,它可以用來編譯當前CPU的彙編指令,該函數流程如下:
1.分配一塊4k內存,權限爲可執行作爲Assembler的執行內存
2.修改EFLAGS的21bit,如果可以修改,那麼該CPU支持CPUID指令
3.如果支持CPUID指令,根據指令手冊說明,輸入參數EAX爲1的時候,ECX:EDX輸出CPU feature,其中ECX作爲高32bit,EDX作爲低32bit。
4.把ECX:EDX賦值給EDX:EAX,按照Intel的ABI約定,這兩個寄存器構成64bit的函數返回值
5.把返回地址強轉爲函數指針,執行之。
6.釋放執行內存

再來看V8::InitializeOncePerProcessImpl函數中的OS::PostSetUp函數
void OS::PostSetUp() {
  // Math functions depend on CPU features therefore they are initialized after
  // CPU.
  MathSetup();
#if defined(V8_TARGET_ARCH_IA32)
  memcopy_function = CreateMemCopyFunction();
#endif
}
MathSetup函數和CreateMemCopyFunction函數做了實際上是類似的事情,即使用當前CPU的指令爲V8生成可調用的函數。其中MathSetup函數主要生成數學函數,sin,cos,tag等,主要用到了FPU的指令,而CreateMemCopyFunction則是生成了memcpy的替代函數。
OS::MemCopyFunction CreateMemCopyFunction(),該函數定義於codegen-ia32.cc,它的流程如下:
1.分配一塊4k的可執行內存,交給MacroAssembler使用
2.使用SSE2指令進行復制
movdqu指令用來複制不對齊的雙四字(即16字節)數據。
movdqa指令用來複制對齊的雙四字數據
prefetch指令用來預取指定內存地址的數據到各級cache中,加快執行速度
這裏貼出主循環部分
__ mov(edx, count);
      Register loop_count = ecx;
      Register count = edx;
      __ shr(loop_count, 5);//除以32,因爲每次都會複製32字節
      {
        // Main copy loop.
        Label loop;
        __ bind(&loop);
        __ prefetch(Operand(src, 0x20), 1);//預取src後32字節的數據,因爲每次都是複製32字節
        __ movdqa(xmm0, Operand(src, 0x00));//複製src地址開始的16字節到xmm0寄存器
        __ movdqa(xmm1, Operand(src, 0x10));//複製src+16地址開始的16字節到xmm0寄存器
        __ add(src, Immediate(0x20));

        __ movdqa(Operand(dst, 0x00), xmm0);//複製mm0寄存器內容到dst地址
        __ movdqa(Operand(dst, 0x10), xmm1);//複製mm1寄存器內容到dst+16地址
        __ add(dst, Immediate(0x20));

        __ dec(loop_count);
        __ j(not_zero, &loop);
      }
該函數其他部分的代碼主要用於不對齊部分的處理。

下面來看V8::Initialize函數的i::Isolate::EnterDefaultIsolate()函數,首先需要弄清幾個類之間的關係:
      ----------------------------------------------------------------------
     1|                                                                                | 1
------------  1        1  -----------------------------  1            1 -----------
| Thread | ------------| PerIsolateThreadData | --------------| Isolate |
------------                -----------------------------                  -----------
               \                              |                                 /
                \                             |                                /
                 -------------------------------------------------------
                 |          Isolate::Thread_data_Table             |
                 -------------------------------------------------------
class PerIsolateThreadData定義於isolate.h
  // A thread has a PerIsolateThreadData instance for each isolate that it has
  // entered. That instance is allocated when the isolate is initially entered
  // and reused on subsequent entries.

我們看到Thread和PerIsolateThreadData,Isolate三者之間是一對一的關係,
Thread可以通過其存儲的Tls key分別得到當前與之關聯的PerIsolateThreadData和Isolate
PerIsolateThreadData類的職責就是關聯當前Thread和一個Isolate,每個Isolate都有一個PerIsolateThreadData實例。
Isolate有一個靜態變量Isolate::Thread_data_Table,它通過Thread_id和isolate可以查詢得到PerIsolateThreadData
void Isolate::Enter() {//Isolate.cc
  Isolate* current_isolate = NULL;
  PerIsolateThreadData* current_data = CurrentPerIsolateThreadData();
  if (current_data != NULL) {
    current_isolate = current_data->isolate_;
    ASSERT(current_isolate != NULL);
    if (current_isolate == this) {
      ASSERT(Current() == this);
      ASSERT(entry_stack_ != NULL);
      ASSERT(entry_stack_->previous_thread_data == NULL ||
             entry_stack_->previous_thread_data->thread_id().Equals(
                 ThreadId::Current()));
      // Same thread re-enters the isolate, no need to re-init anything.
      entry_stack_->entry_count++;
      return;
    }
  }

  // Threads can have default isolate set into TLS as Current but not yet have
  // PerIsolateThreadData for it, as it requires more advanced phase of the
  // initialization. For example, a thread might be the one that system used for
  // static initializers - in this case the default isolate is set in TLS but
  // the thread did not yet Enter the isolate. If PerisolateThreadData is not
  // there, use the isolate set in TLS.
  if (current_isolate == NULL) {
    current_isolate = Isolate::UncheckedCurrent();
  }

  PerIsolateThreadData* data = FindOrAllocatePerThreadDataForThisThread();
  ASSERT(data != NULL);
  ASSERT(data->isolate_ == this);

  EntryStackItem* item = new EntryStackItem(current_data,
                                            current_isolate,
                                            entry_stack_);
  entry_stack_ = item;

  SetIsolateThreadLocals(this, data);

  // In case it's the first time some thread enters the isolate.
  set_thread_id(data->thread_id());
}
本函數是Thread在Enter Isolate的時候調用的,其流程如下:
1.通過Thread的tls key得到與之關聯的PerIsolateThreadData
2.如果PerIsolateThreadData中存儲的isolate與當前isolate相等,說明再次進入了同一個isolate,把當前isolate存儲的EntryStackItem加一,返回
如果不相等,說明進入了一個不同的isolate
3.如果從PerIsolateThreadData獲取的isolate爲空,則使用當前Thread的default isolate。使用這個isolate和當前thread id查詢Isolate::Thread_data_Table,看是否存在PerIsolateThreadData。如果不存在的話,就需要創建一個PerIsolateThreadData實例。
4.設置當前Thread,PerIsolateThreadData,和isolate之間的關聯
5.EntryStackItem是一個Entry Isolate的Stack,它存儲在Isolate中,它在當前的Isolate中形成了一個Stack,棧頂的節點指向當前運行isolate的thread。
  // These items form a stack synchronously with threads Enter'ing and Exit'ing
  // the Isolate. The top of the stack points to a thread which is currently
  // running the Isolate. When the stack is empty, the Isolate is considered
  // not entered by any thread and can be Disposed.
  // If the same thread enters the Isolate more then once, the entry_count_
  // is incremented rather then a new item pushed to the stack.
  class EntryStackItem定義於isolate.h

下面來分析isolate->Init(des)
1.stack_guard_.InitThread(lock);
StackGuard類定義於execution.h,它有一個內部類ThreadLocal,它負責記錄stack limit,以下是ThreadLocal類的一些註釋:
stack limit被分成了Javascript和C++ stack limit,這二者只有一種情況是不同,即運行於模擬器上的時候,此時這兩個stack是分開的。每個stack limit都有兩個值,有real_前綴的是VM真實的stack limit。沒有real_前綴的與帶real前綴的stack limit只在一種情況下是不等的,即發生了中斷,例如調試斷點,或者搶斷,這種情況下,它故意低於閥值使得stack check失敗。在生成的代碼和運行期系統檢查中都使用的是不帶real_前綴的stack limit。
結合以下的代碼會理解的更加清晰點:
bool StackGuard::ThreadLocal::Initialize(Isolate* isolate) {
  bool should_set_stack_limits = false;
  if (real_climit_ == kIllegalLimit) {
    // Takes the address of the limit variable in order to find out where
    // the top of stack is right now.
    const uintptr_t kLimitSize = FLAG_stack_size * KB;
    uintptr_t limit = reinterpret_cast<uintptr_t>(&limit) - kLimitSize;//使用臨時變量limit的地址作爲棧基地址,計算stack limit,在模擬器上使用的stack size是492kB
    ASSERT(reinterpret_cast<uintptr_t>(&limit) > kLimitSize);
    real_jslimit_ = SimulatorStack::JsLimitFromCLimit(isolate, limit);
    jslimit_ = SimulatorStack::JsLimitFromCLimit(isolate, limit);
    real_climit_ = limit;
    climit_ = limit;
    should_set_stack_limits = true;
  }
  nesting_ = 0;
  postpone_interrupts_nesting_ = 0;
  interrupt_flags_ = 0;
  return should_set_stack_li
mits;
}

2.heap_.SetUp()

3.heap_.CreateHeapObjects()
請參看 Heap RootObject的初始化

4.bootstrapper->Initialize
1)SourceCodeCache初始化
SourceCodeCache定義於bootstrapper.h中,它使用一個FixedArray存儲(AsciiString*,JSFunction*),即js文件名稱與預編譯的函數之間的映射。這裏是它的初始化。主要就是從Heap中固定索引empty_fixed_array中獲取FixedArray
2)GCExtention::Register
GCExtention是Extention的派生類,Extention定義了name,source和native function,本函數把GCExtention註冊給RegisteredExtention鏈表,它們之間的關係如下所示:
  First     --------------   next   ---------------
--------->|           --+-------->|               |
             --------------            ---------------
             |              |
             ------|------
                    | Extention
                    \/
      ----------------------------
      |           name           |
      ----------------------------
      |           source          |
      ----------------------------
      |    GetNativeFunction |
      ----------------------------
                     /_\
                      |  
      ----------------------------
      |        GCExtention     |
      ----------------------------
在Genesis::InstallExtention函數中將編譯安裝這個鏈表中所有的Extention,其中在Parse過程中Parser::Declare函數將會調用extention->GetNativeFunction這個虛函數,該函數返回一個FunctionTemplate
ExternalizeStringExtention類與StatisticsExtention類與GCExtention相同,都是繼承自Extention類,它們也分別通過extention註冊了一些函數。

5.builtin.SetUp
6.stub_cache->Initialize


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章