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


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