Flutter之Dart虛擬機器啟動

大逗大人發表於2020-05-05

Flutter之Engine啟動流程一文中敘述了Engine是如何建立並呼叫入口函式——main的。由於篇幅原因,在該文中並沒有具體講解Dart VM的建立。所以本文就來看看Dart VM是如何建立的。

1、Dart VM物件的建立

直接來看ShellCreate函式(關於是如何呼叫該函式的,可以去閱讀Flutter之Engine啟動流程一文),程式碼如下。

std::unique_ptr<Shell> Shell::Create(
    TaskRunners task_runners,
    const WindowData window_data,
    Settings settings,
    Shell::CreateCallback<PlatformView> on_create_platform_view,
    Shell::CreateCallback<Rasterizer> on_create_rasterizer) {
  PerformInitializationTasks(settings);
  PersistentCache::SetCacheSkSL(settings.cache_sksl);
  
  //Dart VM的建立
  auto vm = DartVMRef::Create(settings);

  auto vm_data = vm->GetVMData();
  
  //繼續進行Shell物件的建立
  return Shell::Create(std::move(task_runners),        //
                       std::move(window_data),         //
                       std::move(settings),            //
                       vm_data->GetIsolateSnapshot(),  // isolate snapshot
                       on_create_platform_view,        //
                       on_create_rasterizer,           //
                       std::move(vm)                   //
  );
}
複製程式碼

Create主要是進行Dart VM的建立,通過DartVMRef中的Create函式。程式碼如下。

DartVMRef DartVMRef::Create(Settings settings,
                            fml::RefPtr<DartSnapshot> vm_snapshot,
                            fml::RefPtr<DartSnapshot> isolate_snapshot) {
  std::scoped_lock lifecycle_lock(gVMMutex);

  //如果程式中存在正在執行的Dart VM,那麼直接使用這個Dart VM。也就是在同一程式中,只會存在一個Dart VM
  if (auto vm = gVM.lock()) {
    //直接返回程式中已存在的Dart VM
    return DartVMRef{std::move(vm)};
  }

  std::scoped_lock dependents_lock(gVMDependentsMutex);

  gVMData.reset();
  gVMServiceProtocol.reset();
  gVMIsolateNameServer.reset();
  gVM.reset();

  //如果程式中沒有Dart VM,就建立並初始化一個Dart VM
  auto isolate_name_server = std::make_shared<IsolateNameServer>();
  auto vm = DartVM::Create(std::move(settings),          //
                           std::move(vm_snapshot),       //
                           std::move(isolate_snapshot),  //
                           isolate_name_server           //
  );

  if (!vm) {//建立DartVM例項失敗
    return {nullptr};
  }

  //將Dart VM的引用設為全域性引用,保證程式內唯一性。
  gVMData = vm->GetVMData();
  gVMServiceProtocol = vm->GetServiceProtocol();
  gVMIsolateNameServer = isolate_name_server;
  gVM = vm;

  if (settings.leak_vm) {
    gVMLeak = new std::shared_ptr<DartVM>(vm);
  }

  return DartVMRef{std::move(vm)};
}
複製程式碼

DartVMRef中的Create函式中主要是返回一個Dart VM引用,這個Dart VM是程式內唯一的,也就是程式中如果不存在Dart VM,那麼就建立並初始化一個Dart VM,否則直接使用已存在的Dart VM。下面再來看Dart VM的建立流程。

std::shared_ptr<DartVM> DartVM::Create(
    Settings settings,
    fml::RefPtr<DartSnapshot> vm_snapshot,
    fml::RefPtr<DartSnapshot> isolate_snapshot,
    std::shared_ptr<IsolateNameServer> isolate_name_server) {
  auto vm_data = DartVMData::Create(settings,                    //
                                    std::move(vm_snapshot),      //
                                    std::move(isolate_snapshot)  //
  );

  if (!vm_data) {
    return {};
  }

  //建立Dart VM
  return std::shared_ptr<DartVM>(
      new DartVM(std::move(vm_data), std::move(isolate_name_server)));
}
複製程式碼

上面程式碼就是簡單的建立一個DartVM物件,來看該物件的建構函式。

DartVM::DartVM(std::shared_ptr<const DartVMData> vm_data,
               std::shared_ptr<IsolateNameServer> isolate_name_server)
    : settings_(vm_data->GetSettings()),
      concurrent_message_loop_(fml::ConcurrentMessageLoop::Create()),
      skia_concurrent_executor_(
          [runner = concurrent_message_loop_->GetTaskRunner()](
              fml::closure work) { runner->PostTask(work); }),
      vm_data_(vm_data),
      isolate_name_server_(std::move(isolate_name_server)),
      service_protocol_(std::make_shared<ServiceProtocol>()) {
  gVMLaunchCount++;

  //由於Dart VM初始化是執行緒安全的,因此這裡的呼叫也是執行緒安全的
  SkExecutor::SetDefault(&skia_concurrent_executor_);

  {
    //EventHandler的建立
    dart::bin::BootstrapDartIo();

    if (!settings_.temp_directory_path.empty()) {
      dart::bin::SetSystemTempDirectory(settings_.temp_directory_path.c_str());
    }
  }

  ...
  
  //UI相關類的註冊,如Canvas、Picture、Window等。
  DartUI::InitForGlobal();

  {
    //Dart VM初始化引數
    Dart_InitializeParams params = {};
    params.version = DART_INITIALIZE_PARAMS_CURRENT_VERSION;
    params.vm_snapshot_data = vm_data_->GetVMSnapshot().GetDataMapping();
    params.vm_snapshot_instructions =
        vm_data_->GetVMSnapshot().GetInstructionsMapping();
    params.create_group = reinterpret_cast<decltype(params.create_group)>(
        DartIsolate::DartIsolateGroupCreateCallback);
    params.initialize_isolate =
        reinterpret_cast<decltype(params.initialize_isolate)>(
            DartIsolate::DartIsolateInitializeCallback);
    params.shutdown_isolate =
        reinterpret_cast<decltype(params.shutdown_isolate)>(
            DartIsolate::DartIsolateShutdownCallback);
    params.cleanup_isolate = reinterpret_cast<decltype(params.cleanup_isolate)>(
        DartIsolate::DartIsolateCleanupCallback);
    params.cleanup_group = reinterpret_cast<decltype(params.cleanup_group)>(
        DartIsolate::DartIsolateGroupCleanupCallback);
    params.thread_exit = ThreadExitCallback;
    params.get_service_assets = GetVMServiceAssetsArchiveCallback;
    params.entropy_source = dart::bin::GetEntropy;
    //Dart VM初始化
    char* init_error = Dart_Initialize(&params);
    
    ...
  }

  ...
}
複製程式碼

DartVM物件的建構函式中主要做了以下幾件事。

  1. EventHandler的建立。
  2. UI相關類的在Engine層的註冊。
  3. Dart VM的初始化。

先來看EventHandler的建立。

2、EventHandler的建立

EventHandler是通過BootstrapDartIo函式來建立的,實現程式碼如下。

void BootstrapDartIo() {
  // Bootstrap 'dart:io' event handler.
  TimerUtils::InitOnce();
  EventHandler::Start();
}
複製程式碼

EventHandlerStart函式中,會建立一個全域性socket(globalTcpListeningSocketRegistry)、一個Monitor物件及EventHandler物件。

EventHandler的具體實現因系統而已,這裡以Android系統為例。EventHandlerdelegate_是一個EventHandlerImplementation物件,他的具體實現在eventhandler_android.cc中。

void EventHandler::Start() {
  //初始化一個全域性socket(globalTcpListeningSocketRegistry)
  ListeningSocketRegistry::Initialize();

  shutdown_monitor = new Monitor();
  //建立EventHandler物件
  event_handler = new EventHandler();
  event_handler->delegate_.Start(event_handler);
  ...
}
複製程式碼

delegate_的建構函式中,會建立一個epoll控制程式碼,並註冊一個interrupt_fdepoll中。

再來看delegate_start函式,在該函式中會建立一個名為dart:io EventHandler的新執行緒。Java中,建立一個新執行緒,都會傳遞一個回撥方法。這裡也不例外。其對應的回撥函式就是Poll函式,在該函式中會通過epoll來等待事件並執行。

通過epoll拿到執行完畢的任務後,直接呼叫HandleEvents函式來進一步處理,並通過isolateMessageHandler來執行對應的回撥。

//建立epoll控制程式碼
EventHandlerImplementation::EventHandlerImplementation()
    : socket_map_(&SimpleHashMap::SamePointerValue, 16) {
  intptr_t result;
  result = NO_RETRY_EXPECTED(pipe(interrupt_fds_));
  shutdown_ = false;
  //傳遞給epoll_create的初始大小,當Linux版本>=2.6.8時,該值將被忽略
  static const int kEpollInitialSize = 64;
  //建立epoll控制程式碼
  epoll_fd_ = NO_RETRY_EXPECTED(epoll_create(kEpollInitialSize));
  //註冊interrupt_fd到epoll中。
  struct epoll_event event;
  event.events = EPOLLIN;
  event.data.ptr = NULL;
  int status = NO_RETRY_EXPECTED(
      epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, interrupt_fds_[0], &event));
}

void EventHandlerImplementation::Start(EventHandler* handler) {
  //建立執行緒名為dart:io EventHandler的新執行緒
  int result =
      Thread::Start("dart:io EventHandler", &EventHandlerImplementation::Poll,
                    reinterpret_cast<uword>(handler));
}

//新執行緒的具體執行函式
void EventHandlerImplementation::Poll(uword args) {
  ThreadSignalBlocker signal_blocker(SIGPROF);
  static const intptr_t kMaxEvents = 16;
  struct epoll_event events[kMaxEvents];
  EventHandler* handler = reinterpret_cast<EventHandler*>(args);
  EventHandlerImplementation* handler_impl = &handler->delegate_;

  while (!handler_impl->shutdown_) {
    int64_t millis = handler_impl->GetTimeout();
    if (millis > kMaxInt32) {
      millis = kMaxInt32;
    }
    //阻塞等待
    intptr_t result = TEMP_FAILURE_RETRY_NO_SIGNAL_BLOCKER(
        epoll_wait(handler_impl->epoll_fd_, events, kMaxEvents, millis));
    if (result == -1) {
      ...
    } else {
      handler_impl->HandleTimeout();
      //在子執行緒呼叫HandleEvents函式,會處理拿到的所有任務
      handler_impl->HandleEvents(events, result);
    }
  }
  handler->NotifyShutdownDone();
}
//處理拿到的所有事件
void EventHandlerImplementation::HandleEvents(struct epoll_event* events,
                                              int size) {
  bool interrupt_seen = false;
  for (int i = 0; i < size; i++) {
    if (events[i].data.ptr == NULL) {
      interrupt_seen = true;
    } else {
      DescriptorInfo* di =
          reinterpret_cast<DescriptorInfo*>(events[i].data.ptr);
      const intptr_t old_mask = di->Mask();
      const intptr_t event_mask = GetPollEvents(events[i].events, di);
      if ((event_mask & (1 << kErrorEvent)) != 0) {
        di->NotifyAllDartPorts(event_mask);
        UpdateEpollInstance(old_mask, di);
      } else if (event_mask != 0) {
        Dart_Port port = di->NextNotifyDartPort(event_mask);
        UpdateEpollInstance(old_mask, di);
        //傳送訊息給isolate中MessageHandler
        DartUtils::PostInt32(port, event_mask);
      }
    }
  }
  if (interrupt_seen) {
    // Handle after socket events, so we avoid closing a socket before we handle
    // the current events.
    HandleInterruptFd();
  }
}
複製程式碼

EventHandler的建立中,主要是建立新執行緒及epoll的使用。在上層使用中,socketTimer定時任務的執行就是通過EventHandler來實現的。

IOS中,則是採用的kqueue來實現非同步IO。

3、DartUI相關的初始化

EventHandler建立成功後,還會進行UI相關類在C/C++層的註冊,從而使framework層能順利的呼叫native方法。在Java中,如果想要呼叫native方法,也會進行類似的註冊。

void DartUI::InitForGlobal() {
  if (!g_natives) {
    g_natives = new tonic::DartLibraryNatives();
    Canvas::RegisterNatives(g_natives);
    CanvasGradient::RegisterNatives(g_natives);
    CanvasImage::RegisterNatives(g_natives);
    CanvasPath::RegisterNatives(g_natives);
    CanvasPathMeasure::RegisterNatives(g_natives);
    Codec::RegisterNatives(g_natives);
    ColorFilter::RegisterNatives(g_natives);
    DartRuntimeHooks::RegisterNatives(g_natives);
    EngineLayer::RegisterNatives(g_natives);
    FontCollection::RegisterNatives(g_natives);
    FrameInfo::RegisterNatives(g_natives);
    ImageFilter::RegisterNatives(g_natives);
    ImageShader::RegisterNatives(g_natives);
    IsolateNameServerNatives::RegisterNatives(g_natives);
    Paragraph::RegisterNatives(g_natives);
    ParagraphBuilder::RegisterNatives(g_natives);
    Picture::RegisterNatives(g_natives);
    PictureRecorder::RegisterNatives(g_natives);
    Scene::RegisterNatives(g_natives);
    SceneBuilder::RegisterNatives(g_natives);
    SemanticsUpdate::RegisterNatives(g_natives);
    SemanticsUpdateBuilder::RegisterNatives(g_natives);
    Vertices::RegisterNatives(g_natives);
    Window::RegisterNatives(g_natives);
#if defined(OS_FUCHSIA)
    SceneHost::RegisterNatives(g_natives);
#endif

    // Secondary isolates do not provide UI-related APIs.
    g_natives_secondary = new tonic::DartLibraryNatives();
    DartRuntimeHooks::RegisterNatives(g_natives_secondary);
    IsolateNameServerNatives::RegisterNatives(g_natives_secondary);
  }
}
複製程式碼

可以看到很多與UI相關的native方法都是在InitForGlobal函式中註冊的。

4、Dart VM的初始化

最後再來看Dart VM的初始化。

char* Dart::Init(const uint8_t* vm_isolate_snapshot,
                 const uint8_t* instructions_snapshot,
                 Dart_IsolateGroupCreateCallback create_group,
                 Dart_InitializeIsolateCallback initialize_isolate,
                 Dart_IsolateShutdownCallback shutdown,
                 Dart_IsolateCleanupCallback cleanup,
                 Dart_IsolateGroupCleanupCallback cleanup_group,
                 Dart_ThreadExitCallback thread_exit,
                 Dart_FileOpenCallback file_open,
                 Dart_FileReadCallback file_read,
                 Dart_FileWriteCallback file_write,
                 Dart_FileCloseCallback file_close,
                 Dart_EntropySource entropy_source,
                 Dart_GetVMServiceAssetsArchive get_service_assets,
                 bool start_kernel_isolate,
                 Dart_CodeObserver* observer) {
  ...
  //棧幀佈局的初始化
  FrameLayout::Init();
  
  set_thread_exit_callback(thread_exit);
  SetFileCallbacks(file_open, file_read, file_write, file_close);
  set_entropy_source_callback(entropy_source);
  //系統相關初始化,針對不同系統做的適配
  OS::Init();
  ...
  
  start_time_micros_ = OS::GetCurrentMonotonicMicros();
  //虛擬記憶體初始化,預設是獲取一頁的大小
  VirtualMemory::Init();
  //OSThread執行緒初始化,建立一個名為Dart_Initialize的OSThread並將其設定為TLS
  OSThread::Init();
  //zone初始化
  Zone::Init();
  ...
  
  //將isolate的一些回撥重置為null,如create_group_callback_、initialize_callback_等
  Isolate::InitVM();
  //IsolateGroup初始化
  IsolateGroup::Init();
  //PortMap初始化,建立一個初始化容量為8的map
  PortMap::Init();
  //FreeListElement初始化,主要是做一些assert判斷
  FreeListElement::Init();
  //ForwardingCorpse初始化,主要是做一些assert判斷
  ForwardingCorpse::Init();
  //Api初始化
  Api::Init();
  //與具體作業系統相關,目前都是空實現
  NativeSymbolResolver::Init();
  //SemiSpace初始化,SemiSpace主要是用於對記憶體的管理,新生代就由兩個SemiSpace組成
  SemiSpace::Init();
  NOT_IN_PRODUCT(Metric::Init());
  StoreBuffer::Init();
  MarkingStack::Init();

  ...
  // Create the read-only handles area.
  predefined_handles_ = new ReadOnlyHandles();
  // Create the VM isolate and finish the VM initialization.
  //建立一個執行緒池
  thread_pool_ = new ThreadPool();
  { 
    //即將建立的isolate是vm isolate
    const bool is_vm_isolate = true;

    // Cache value of "non-nullable" experimental flag.
    set_non_nullable_flag(KernelIsolate::GetExperimentalFlag("non-nullable"));

    // Setup default flags for the VM isolate.
    Dart_IsolateFlags api_flags;
    Isolate::FlagsInitialize(&api_flags);

    //這裡建立一個偽IsolateGroupSource物件,因為即將建立的vm-isolate不是一個真正的isolate物件。它更多充當的是一個包含VM全域性物件的容器。
    std::unique_ptr<IsolateGroupSource> source(
        new IsolateGroupSource(nullptr, kVmIsolateName, vm_isolate_snapshot,
                               instructions_snapshot, nullptr, -1, api_flags));
    auto group = new IsolateGroup(std::move(source), /*embedder_data=*/nullptr);
    IsolateGroup::RegisterIsolateGroup(group);
    //建立一個名為vm-isolate的isolate物件
    vm_isolate_ =
        Isolate::InitIsolate(kVmIsolateName, group, api_flags, is_vm_isolate);
    group->set_initial_spawn_successful();


    Thread* T = Thread::Current();
    StackZone zone(T);
    HandleScope handle_scope(T);
    //建立null物件,null是Dart VM建立的第一個物件
    Object::InitNull(vm_isolate_);
    //建立一個ObjectStore物件,該物件主要是來儲存每個isolate對於Dart VM中物件的引用
    ObjectStore::Init(vm_isolate_);
    //初始化CPU相關資訊
    TargetCPUFeatures::Init();
    //初始化物件。
    Object::Init(vm_isolate_);
    ArgumentsDescriptor::Init();
    ICData::Init();
    SubtypeTestCache::Init();
    
    if (vm_isolate_snapshot != NULL) {...} else {...}
    
    //由於bootstrapping問題,這裡需要在vm isolate所線上程來初始化常量
    T->InitVMConstants();
    {
      Object::FinalizeVMIsolate(vm_isolate_);
    }
  }
  Api::InitHandles();

  //取消註冊該執行緒中的VM isolate
  Thread::ExitIsolate();
  Isolate::SetCreateGroupCallback(create_group);
  Isolate::SetInitializeCallback_(initialize_isolate);
  Isolate::SetShutdownCallback(shutdown);
  Isolate::SetCleanupCallback(cleanup);
  Isolate::SetGroupCleanupCallback(cleanup_group);
  
  ...

  return NULL;
}
複製程式碼

上面程式碼中做了詳細的註釋。可以發現在Dart VM初始化時,需要做的操作還是蠻多的,比如棧幀佈局的初始化、虛擬記憶體初始化、OSThread初始化、null物件及其他共享物件的初始化、PortMap的初始化、執行緒池的建立、名為vm-isolateisolate物件建立等。

這裡重點來看vm-isolate,它是一個isolate,但與普通isolate有很大的區別,所以不是一個真正的isolate,是一個偽isolate。主要區別如下:

  1. 一般isolate堆中都會分為新生代與老年代,並且新生代中fromto的比例是1:1,新建立的物件也基本上都會分配在to中。而vm-isolate的堆中,新生代的空間大小為0,也就是不存在新生代。也因此,所有物件都直接分配在老年代中。
  2. isolate類似程式,之間的物件是無法相互引用的。但對於vm-isolate卻是例外,它的堆中包含一些不可變物件,如null,true,false,並且其他isolate可以引用vm-isolate中堆的物件。

關於vm-isolate的具體建立可以參考深入理解Isolate原理這篇文章。

Dart VM啟動流程圖如下,是從Flutter之引擎啟動流程的基礎上開始的

Flutter之Dart虛擬機器啟動

Dart VM初始化成功後,也就意味著在同一程式中,Dart虛擬機器已經啟動成功並使用它了。無論engine建立多少次,在同一程式中,Dart VM僅會建立一次。

【參考資料】

Introduction to Dart VM

深入理解Dart虛擬機器啟動

Flutter: Don’t Fear the Garbage Collector

ARM函式呼叫過程分析

相關文章