现在的位置: 首页 > 综合 > 正文

Chrome的启动流程 之一

2013年10月02日 ⁄ 综合 ⁄ 共 6244字 ⁄ 字号 评论关闭

Chrome的启动流程
在Chrome的solution中,在Chrome子目录下有一个Chrome Project。

 Chrome的工程结构

在这个工程下的chrome_exe_main.cc中,包含了chrome浏览器的入口函数wWinMain

  1. int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE prev_instance,
  2.                       wchar_t* command_line, int) {
  3.   base::EnableTerminationOnHeapCorruption();
  4.  
  5.   // The exit manager is in charge of calling the dtors of singletons.
  6.   base::AtExitManager exit_manager;
  7.  
  8.   win_util::WinVersion win_version = win_util::GetWinVersion();
  9.   if (win_version < win_util::WINVERSION_VISTA) {
  10.     // On Vista, this is unnecessary since it is controlled through the
  11.     // /NXCOMPAT linker flag.
  12.     // Enforces strong DEP support.
  13.     sandbox::SetCurrentProcessDEP(sandbox::DEP_ENABLED);
  14.   }
  15.  
  16.   // Get the interface pointer to the BrokerServices or TargetServices,
  17.   // depending who we are.
  18.  
  19.   CommandLine::Init(0, NULL);
  20.  
  21.   const wchar_t* dll_name = L"chrome.dll";
  22.   std::wstring dll_full_path;
  23.   std::wstring versionned_path;
  24.   sandbox::SandboxInterfaceInfo sandbox_info = {0};
  25.   sandbox_info.broker_services = sandbox::SandboxFactory::GetBrokerServices();
  26.   if (!sandbox_info.broker_services)
  27.     sandbox_info.target_services = sandbox::SandboxFactory::GetTargetServices();
  28.  
  29. #if defined(GOOGLE_CHROME_BUILD)
  30.   google_update::GoogleUpdateClient client;
  31.  
  32.   // TODO(erikkay): Get guid from build macros rather than hardcoding.
  33.   // TODO(erikkay): verify client.Init() return value for official builds
  34.   client.Init(L"{8A69D345-D564-463c-AFF1-A69D9E530F96}", dll_name);
  35.   dll_full_path = client.GetDLLFullPath();
  36.   versionned_path = client.GetDLLPath();
  37. #else
  38.   wchar_t exe_path[MAX_PATH] = {0};
  39.   client_util::GetExecutablePath(exe_path);
  40.   wchar_t *version;
  41.   if (client_util::GetChromiumVersion(exe_path, L"Software//Chromium",
  42.                                       &version)) {
  43.     versionned_path = exe_path;
  44.     versionned_path.append(version);
  45.     delete[] version;
  46.   }
  47.  
  48.   dll_full_path = client_util::GetDLLPath(dll_name, versionned_path);
  49. #endif
  50.   // If the versionned path exists, we set the current directory to this path.
  51.   if (client_util::FileExists(versionned_path.c_str())) {
  52.     ::SetCurrentDirectory(versionned_path.c_str());
  53.   }
  54.  
  55.   HINSTANCE dll_handle = ::LoadLibraryEx(dll_name, NULL,
  56.                                          LOAD_WITH_ALTERED_SEARCH_PATH);
  57.  
  58.   // Initialize the crash reporter.
  59.   InitCrashReporterWithDllPath(dll_full_path);
  60.  
  61.   bool exit_now = true;
  62.   if (ShowRestartDialogIfCrashed(&exit_now)) {
  63.     // We have restarted because of a previous crash. The user might
  64.     // decide that he does not want to continue.
  65.     if (exit_now)
  66.       return ResultCodes::NORMAL_EXIT;
  67.   }
  68.   
  69. #if defined(GOOGLE_CHROME_BUILD)
  70.   int ret = 0;
  71.   if (client.Launch(instance, &sandbox_info, command_line, "ChromeMain",
  72.                     &ret)) {
  73.     return ret;
  74.   }
  75. #else
  76.   if (NULL != dll_handle) {
  77.     client_util::DLL_MAIN entry = reinterpret_cast<client_util::DLL_MAIN>(
  78.         ::GetProcAddress(dll_handle, "ChromeMain"));
  79.     if (NULL != entry)
  80.       return (entry)(instance, &sandbox_info, command_line);
  81.   }
  82. #endif
  83.    
  84.   return ResultCodes::GOOGLE_UPDATE_LAUNCH_FAILED;
  85. }

 

典型的Windows SDK模式下的Main函数入口。

Line 3 base::EnableTerminationOnHeapCorruption();

这个函数的主要作用是当Chrome的堆被损坏时,比如出现内存溢出,内存非法访问时,系统中止Chrome进程,该函数调用了系统函数

HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);

Line 6 base::AtExitManager exit_manager;

定义了一个栈变量,AtExitManager类类似于CRT atexit()的功能,用于管理Chrome中所有的Singleton(单实例)的析构操作,这个类的功能和原理很简单,各个单实例向这个类注册回调函数,当exit_manager超出它的作用范围时(在这里是超出wWinMain函数),exit_manager将调用各个回调函数。保证各个单实例的资源被释放。

Line 8 -  Line 14 设置Sandbox参数。SandBox顾名思义“沙箱”,设置启动DEP。DEP是Vista中新增加的一个特性,按照百度知道上的介绍:

数据执行保护 (DEP) 是一套软硬件技术,能够在内存上执行额外检查以帮助防止在系统上运行恶意代码。在 Microsoft Windows XP Service Pack 2、 Microsoft Windows Server 2003 Service Pack 1 、Microsoft Windows XP Tablet PC Edition 2005 和 Microsoft Windows Vista 中,由硬件和软件一起强制实施 DEP。
DEP 的主要优点是可以帮助防止数据页执行代码。通常情况下,不从默认堆和堆栈执行代码。硬件实施 DEP 检测从这些位置运行的代码,并在发现执行情况时引发异常。软件实施 DEP 可帮助阻止恶意代码利用 Windows 中的异常处理机制进行破坏。
硬件实施 DEP 是某些 DEP 兼容处理器的功能,可以防止在已标记为数据存储区的内存区域中执行代码。 此功能也称为非执行和执行保护。 Windows XP SP2 还包括软件实施 DEP,其目的在于减少利用 Windows 中的例外处理机制的情况。
与防病毒程序不同,硬件和软件实施 DEP 技术的目的并不是防止在计算机上安装有害程序。 而是监视您的已安装程序,帮助确定它们是否正在安全地使用系统内存。 为监视您的程序,硬件实施 DEP 将跟踪已指定为“不可执行”的内存区域。 如果已将内存指定为“不可执行”,但是某个程序试图通过内存执行代码,Windows 将关闭该程序以防止恶意代码。 无论代码是不是恶意,都会执行此操作。
注:基于软件的 DEP 是 Windows XP SP2 的一部分并默认启用,不考虑处理器的硬件实施 DEP 功能。 默认情况下,软件实施 DEP 应用于核心操作系统组件和服务。
DEP 默认配置的目的在于保护您的计算机,并尽量减小对应用程序兼容性的影响。 但是某些程序也可能无法正确运行,视您的 DEP 配置而定。
在运行 Microsoft Windows XP 64 位版本并附带 DEP 兼容处理器的计算机上,硬件实施 DEP 已默认启用。 64 位应用程序将不会通过内存的“不可执行”区域来运行。不能禁用硬件启用的 DEP。
Windows XP SP2 上的软件启用 DEP 以及在任何处理器上运行的 32 位应用程序可以配置为使用内存的“可执行”或“不可执行”区域。

Line 19 CommandLine::Init(0, NULL);

初始化命令行参数,Chrome的启动参数实在是太多了,CommandLine将在程序启动时解析启动参数,并将参数缓存到内存中,后续的初始化步骤中将会进一步使用到这些参数。

Line 21 -- Line27主要设置SandBox的BrokerService和TargetService实例参数。BrokerService和TargetService是SandBox中的两个专有概念。具体的Sandbox原理在后续专题中介绍。也可以参考Chrome官方网站的介绍

 

Sandbox的结构

Line 46 -- Line 106主要操作就是加载Chrome.dll,并执行Chrome.dll中的ChromeMain函数。
其中Line 81 InitCrashReporterWithDllPath(dll_full_path);
初始化了CrashReport服务,这是BreakPad库提供的功能,BreakPad为每一个进程开辟了一个监控线程,在这个函数的Line 29中初始化线程操作。

  1. void InitCrashReporterWithDllPath(const std::wstring& dll_path) {
  2.   const CommandLine& command = *CommandLine::ForCurrentProcess();
  3.   if (!command.HasSwitch(switches::kDisableBreakpad)) {
  4.     // Disable the message box for assertions.
  5.     _CrtSetReportMode(_CRT_ASSERT, 0);
  6.     // Query the custom_info now because if we do it in the thread it's going to
  7.     // fail in the sandbox. The thread will delete this object.
  8.     scoped_ptr<CrashReporterInfo> info(new CrashReporterInfo);
  9.     info->process_type = command.GetSwitchValue(switches::kProcessType);
  10.     if (info->process_type.empty())
  11.       info->process_type = L"browser";
  12.     info->dll_path = dll_path;
  13.     // If this is not the browser, we can't be sure that we will be able to
  14.     // initialize the crash_handler in another thread, so we run it right away.
  15.     // This is important to keep the thread for the browser process because
  16.     // it may take some times to initialize the crash_service process.  We use
  17.     // the Windows worker pool to make better reuse of the thread.
  18.     if (info->process_type != L"browser") {
  19.       InitCrashReporterThread(info.release());
  20.     } else {
  21.       if (QueueUserWorkItem(
  22.               &InitCrashReporterThread,
  23.               info.release(),
  24.               WT_EXECUTELONGFUNCTION) == 0) {
  25.         // We failed to queue to the worker pool, initialize in this thread.
  26.         InitCrashReporterThread(info.release());
  27.       }
  28.     }
  29.   }

Chrome的Crash Report功能也是可以以很大的一个篇章来描述了。

另外在Line 62 -- Line 68中,主要是检测本次启动是否是从上一次Crash中拉起,如果是的话,则会出现一个确认框,让用户确认是否继续。如果用户选择不继续,则结束返回。

这个函数就基本讲完了,下一篇重点讲述Chrome.dll中的ChromeMain函数了,相对本函数来说,要复杂一些了。

突然发现源码剖析不是一件简单的活,很佩服侯捷那种庖丁解牛的技术功底和文字描述能力,希望能坚持下去!

 

 

 

 

 

 

 

 

 

【上篇】
【下篇】

抱歉!评论已关闭.