Chrome启动流程 之二
本文主要分析chrome.dll中的ChromeMain函数。
- DLLEXPORT int __cdecl ChromeMain(HINSTANCE instance,
- sandbox::SandboxInterfaceInfo* sandbox_info,
- TCHAR* command_line) {
- #elif defined(OS_POSIX)
- int ChromeMain(int argc, const char** argv) {
- #endif
- #if defined(OS_MACOSX)
- // If Breakpad is not present then turn off os crash dumps so we don't have
- // to wait eons for Apple's Crash Reporter to generate a dump.
- if (IsCrashReporterDisabled()) {
- DebugUtil::DisableOSCrashDumps();
- }
- #endif
- RegisterInvalidParamHandler();
- // The exit manager is in charge of calling the dtors of singleton objects.
- base::AtExitManager exit_manager;
- // We need this pool for all the objects created before we get to the
- // event loop, but we don't want to leave them hanging around until the
- // app quits. Each "main" needs to flush this pool right before it goes into
- // its main event loop to get rid of the cruft.
- base::ScopedNSAutoreleasePool autorelease_pool;
- #if defined(OS_POSIX)
- base::GlobalDescriptors* g_fds = Singleton<base::GlobalDescriptors>::get();
- g_fds->Set(kPrimaryIPCChannel,
- kPrimaryIPCChannel + base::GlobalDescriptors::kBaseDescriptor);
- #if defined(OS_LINUX)
- g_fds->Set(kCrashDumpSignal,
- kCrashDumpSignal + base::GlobalDescriptors::kBaseDescriptor);
- #endif
- #endif
- // Initialize the command line.
- #if defined(OS_WIN)
- CommandLine::Init(0, NULL);
- #else
- CommandLine::Init(argc, argv);
- #endif
- #if defined(OS_MACOSX)
- // Needs to be called after CommandLine::Init().
- InitCrashProcessInfo();
- #endif
- const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess();
- #if defined(OS_WIN)
- // Must do this before any other usage of command line!
- if (HasDeprecatedArguments(parsed_command_line.command_line_string()))
- return 1;
- #endif
- #if defined(OS_POSIX)
- // Always ignore SIGPIPE. We check the return value of write().
- CHECK(signal(SIGPIPE, SIG_IGN) != SIG_ERR);
- #endif // OS_POSIX
- int browser_pid;
- std::wstring process_type =
- parsed_command_line.GetSwitchValue(switches::kProcessType);
- if (process_type.empty()) {
- browser_pid = base::GetCurrentProcId();
- } else {
- #if defined(OS_WIN)
- std::wstring channel_name =
- parsed_command_line.GetSwitchValue(switches::kProcessChannelID);
- browser_pid = StringToInt(WideToASCII(channel_name));
- DCHECK(browser_pid != 0);
- #else
- browser_pid = base::GetCurrentProcId();
- #endif
- #if defined(OS_POSIX)
- // When you hit Ctrl-C in a terminal running the browser
- // process, a SIGINT is delivered to the entire process group.
- // When debugging the browser process via gdb, gdb catches the
- // SIGINT for the browser process (and dumps you back to the gdb
- // console) but doesn't for the child processes, killing them.
- // The fix is to have child processes ignore SIGINT; they'll die
- // on their own when the browser process goes away.
- // Note that we *can't* rely on DebugUtil::BeingDebugged to catch this
- // case because we are the child process, which is not being debugged.
- if (!DebugUtil::BeingDebugged())
- signal(SIGINT, SIG_IGN);
- #endif
- }
- SetupCRT(parsed_command_line);
- // Initialize the Chrome path provider.
- app::RegisterPathProvider();
- chrome::RegisterPathProvider();
- // Initialize the Stats Counters table. With this initialized,
- // the StatsViewer can be utilized to read counters outside of
- // Chrome. These lines can be commented out to effectively turn
- // counters 'off'. The table is created and exists for the life
- // of the process. It is not cleaned up.
- // TODO(port): we probably need to shut this down correctly to avoid
- // leaking shared memory regions on posix platforms.
- if (parsed_command_line.HasSwitch(switches::kEnableStatsTable)) {
- std::string statsfile =
- StringPrintf("%s-%d", chrome::kStatsFilename, browser_pid);
- StatsTable *stats_table = new StatsTable(statsfile,
- chrome::kStatsMaxThreads, chrome::kStatsMaxCounters);
- StatsTable::set_current(stats_table);
- }
- StatsScope<StatsCounterTimer>
- startup_timer(chrome::Counters::chrome_main());
- // Enable the heap profiler as early as possible!
- EnableHeapProfiler(parsed_command_line);
- // Enable Message Loop related state asap.
- if (parsed_command_line.HasSwitch(switches::kMessageLoopHistogrammer))
- MessageLoop::EnableHistogrammer(true);
- // Checks if the sandbox is enabled in this process and initializes it if this
- // is the case. The crash handler depends on this so it has to be done before
- // its initialization.
- SandboxInitWrapper sandbox_wrapper;
- #if defined(OS_WIN)
- sandbox_wrapper.SetServices(sandbox_info);
- #endif
- sandbox_wrapper.InitializeSandbox(parsed_command_line, process_type);
- #if defined(OS_WIN)
- _Module.Init(NULL, instance);
- #endif
- // Notice a user data directory override if any
- const std::wstring user_data_dir =
- parsed_command_line.GetSwitchValue(switches::kUserDataDir);
- if (!user_data_dir.empty())
- CHECK(PathService::Override(chrome::DIR_USER_DATA, user_data_dir));
- bool single_process =
- #if defined (GOOGLE_CHROME_BUILD)
- // This is an unsupported and not fully tested mode, so don't enable it for
- // official Chrome builds.
- false;
- #else
- parsed_command_line.HasSwitch(switches::kSingleProcess);
- #endif
- if (single_process)
- RenderProcessHost::set_run_renderer_in_process(true);
- #if defined(OS_MACOSX)
- // TODO(port-mac): This is from renderer_main_platform_delegate.cc.
- // shess tried to refactor things appropriately, but it sprawled out
- // of control because different platforms needed different styles of
- // initialization. Try again once we understand the process
- // architecture needed and where it should live.
- if (single_process)
- InitWebCoreSystemInterface();
- #endif
- bool icu_result = icu_util::Initialize();
- CHECK(icu_result);
- logging::OldFileDeletionState file_state =
- logging::APPEND_TO_OLD_LOG_FILE;
- if (process_type.empty()) {
- file_state = logging::DELETE_OLD_LOG_FILE;
- }
- logging::InitChromeLogging(parsed_command_line, file_state);
- #ifdef NDEBUG
- if (parsed_command_line.HasSwitch(switches::kSilentDumpOnDCHECK) &&
- parsed_command_line.HasSwitch(switches::kEnableDCHECK)) {
- #if defined(OS_WIN)
- logging::SetLogReportHandler(ChromeAssert);
- #endif
- }
- #endif // NDEBUG
- if (!process_type.empty())
- CommonSubprocessInit();
- startup_timer.Stop(); // End of Startup Time Measurement.
- MainFunctionParams main_params(parsed_command_line, sandbox_wrapper,
- &autorelease_pool);
- // TODO(port): turn on these main() functions as they've been de-winified.
- int rv = -1;
- if (process_type == switches::kRendererProcess) {
- rv = RendererMain(main_params);
- } else if (process_type == switches::kPluginProcess) {
- rv = PluginMain(main_params);
- } else if (process_type == switches::kUtilityProcess) {
- rv = UtilityMain(main_params);
- } else if (process_type == switches::kWorkerProcess) {
- #if defined(OS_WIN)
- rv = WorkerMain(main_params);
- #else
- NOTIMPLEMENTED();
- #endif
- } else if (process_type == switches::kZygoteProcess) {
- #if defined(OS_LINUX)
- if (ZygoteMain(main_params)) {
- // Zygote::HandleForkRequest may have reallocated the command
- // line so update it here with the new version.
- const CommandLine& parsed_command_line =
- *CommandLine::ForCurrentProcess();
- MainFunctionParams main_params(parsed_command_line, sandbox_wrapper,
- &autorelease_pool);
- RendererMain(main_params);
- }
- #else
- NOTIMPLEMENTED();
- #endif
- } else if (process_type.empty()) {
- #if defined(OS_LINUX)
- // Glib type system initialization. Needed at least for gconf,
- // used in net/proxy/proxy_config_service_linux.cc. Most likely
- // this is superfluous as gtk_init() ought to do this. It's
- // definitely harmless, so retained as a reminder of this
- // requirement for gconf.
- g_type_init();
- // gtk_init() can change |argc| and |argv|, but nobody else uses them.
- gtk_init(&argc, const_cast<char***>(&argv));
- SetUpGLibLogHandler();
- #endif
- ScopedOleInitializer ole_initializer;
- rv = BrowserMain(main_params);
- } else {
- NOTREACHED() << "Unknown process type";
- }
- if (!process_type.empty()) {
- ResourceBundle::CleanupSharedInstance();
- }
- #if defined(OS_WIN)
- #ifdef _CRTDBG_MAP_ALLOC
- _CrtDumpMemoryLeaks();
- #endif // _CRTDBG_MAP_ALLOC
- _Module.Term();
- #endif
- logging::CleanupChromeLogging();
- return rv;
- }
Line 15 RegisterInvalidParamHandler();
主要是注册一些回调函数,用于调试和定位(后续采用在代码中增加一些中文注释的方法来解释)
- void RegisterInvalidParamHandler() {
- #if defined(OS_WIN)
- //当调用CRT函数时,参数不合法时,将调用InvalidParameter函数
- //InvalidParameter函数也就是产生中断,触发CrashReport服务
- _set_invalid_parameter_handler(InvalidParameter);
- //当系统调用纯虚函数时,将调用PureCall函数,用于调试
- _set_purecall_handler(PureCall);
- // Gather allocation failure.
- //当内存分配失败时,将调用OnNoMemory,
- std::set_new_handler(&OnNoMemory);
- // Also enable the new handler for malloc() based failures.
- //支持Malloc模式
- _set_new_mode(1);
- #endif
- }
Line 50 -- Line54可以不用关注,主要是为了规避早期Chrome的一个BUG而已。
LIne61 -- Line 75主要是获取当前Browser进程的ID。因为当前启动的是Browser进程,所以获取的肯定是Browser ID。
Line 91 设置CRT参数,比较简单。
Line 94 -- Line 95 主要初始化各种路径辅助类PathService参数。该类主要提供各种路径服务,比如获取当前目录,用户数据存储目录等服务。
Line 103 -- Line110 当启动参数中包含了 --enable-stats-table时,将初始化状态统计表辅助类,用于统计线程数量。
Line 112 -- Line 114 初始化状态统机辅助类StatsCounterTimer,该类将会启动一个计数器,在这里主要统计本函数所花时间。
Line 116 EnableHeapProfiler(parsed_command_line); 加载memory_watcher.dll,Chrome中的一个辅助类,用于统计chrome的内存占用情况。在Chrome中按下Shift + Esc可以查看内存情况,在后面就是memory_watch.dll起作用。
Line 119 -- Line120 当Chrome启动模式指定了message-loop-histogrammer模式时,用于启动线程消息频率统计功能,当在浏览器地址栏输入about:histograms/Loop时,可以查看到效果(前提是以message-loop-histogrammer模式启动,缺省情况下是不会启动的)。
Line 135 -- Line 139 如果启动时参数中用户指定了用户数据目录,则在PathService类中替换用户数据目录为用户制定的目录。
Line 141 -- Line 159 检测启动参数是否包含了--single-process参数,如果包含了该参数,则采用单进程模式,Renderer也将包含在Browser进程中。下面这两幅图可以明显的看出在两种进程模式下的进程情况。相关的进程模式可以参考另外一篇文章(尚未发出),也可参考Chrome的 官方网站说明。
单进程模式下的进程模式(所有页面都在同一进程中,与FireFox,IE一样模式)
缺省模式下的进程模式(每一个网站实例一个进程)
Line 161 -- Line 162 初始化Unicode语言库。IBM提供的一个多语言支持库,比较大。
Line 164 -- Line 169 初始化LOG库,涉及到一个重新启动Chrome时是否删除旧的LOG选项。
process_type这个参数,在缺省情况下,都是为brower进程,但是在启动时也可以指定当前进程类型为Renderer、Plugin、Utility、Worker。这个主要通过启动参数--type=XXX来指定( 这里还不是很清楚,后续搞清楚后再更新)。
Line 180 -- Line 245 主要根据不同的进程类型(process_type),调用下一步的初始化过程。在缺省情况下(不指定type参数),将会执行下一个函数BrowserMain,那将是一个更复杂一些的函数。
总结一下,在本函数中,主要也是初始化一些辅助库、全局参数等,相对起来比较好理解。
到目前为止,应该说还没有进入正题,Chrome的两个核心进程Browser和Renderer的初始化工作还没有开始。