Chromium shutdown
参考文档:
https://www.chromium.org/developers/shutdown/
配置文件销毁
本文档记录了 Windows、Mac 和 Linux 上的关机步骤。
在 Android 上,系统可以随时终止 Chrome 应用程序,而无需运行任何关机步骤。
请参阅下文,了解该过程在 ChromeOS 上的不同之处。
步骤 0:配置文件销毁
从 M98 开始,Chrome 可以Profile
与关机分开销毁对象;在 Windows 和 Linux 上,这发生在多配置文件场景中。在 macOS 上,它也可能发生在单一配置文件的场景中,因为 Chrome 的生命周期与浏览器窗口是分开的。
通常,当所有浏览器窗口都关闭时会触发此逻辑,但其他事情可以保持Profile
活动状态。
~ScopedProfileKeepAlive
发布要运行的任务RemoveKeepAliveOnUIThread
。这会减少 中的引用计数ProfileManager
,如果它达到零,则DestroyProfileWhenAppropriate
调用。
ProfileDestroyer::DestroyProfileWhenAppropriate
...
ProfileManager::RemoveProfile
ProfileManager::RemoveKeepAlive
ScopedProfileKeepAlive::RemoveKeepAliveOnUIThread
与常规配置文件不同,OTR 配置文件不会重新计算。相反,~Browser
在删除自身后检查配置文件的浏览器计数。如果为零,则DestroyProfileWhenAppropriate
直接调用。
ProfileDestroyer::DestroyProfileWhenAppropriate
Browser::~Browser
您可以使用ProfileManager
日志记录来检查配置文件的保活状态:
$ ./out/Default/chrome --enable-logging=stderr --v=0 --vmodule=profile_manager=1 [71002:259:0328/133310.430142:VERBOSE1:profile_manager.cc(1489)] AddKeepAlive(Default, kBrowserWindow). keep_alives=[kWaitingForFirstBrowserWindow (1), kBrowserWindow (1)] [71002:259:0328/133310.430177:VERBOSE1:profile_manager.cc(1543)] ClearFirstBrowserWindowKeepAlive(Default). keep_alives=[kBrowserWindow (1)] [71002:259:0328/133314.468135:VERBOSE1:profile_manager.cc(1489)] AddKeepAlive(Default, kExtensionUpdater). keep_alives=[kBrowserWindow (1), kExtensionUpdater (1)] [71002:259:0328/133314.469444:VERBOSE1:profile_manager.cc(1522)] RemoveKeepAlive(Default, kExtensionUpdater). keep_alives=[kBrowserWindow (1)] [71002:259:0328/133315.396614:VERBOSE1:profile_manager.cc(1489)] AddKeepAlive(Default, kOffTheRecordProfile). keep_alives=[kBrowserWindow (1), kOffTheRecordProfile (1)] [71002:259:0328/133417.078148:VERBOSE1:profile_manager.cc(1522)] RemoveKeepAlive(Default, kBrowserWindow). keep_alives=[kOffTheRecordProfile (1)] [71002:259:0328/133442.705250:VERBOSE1:profile_manager.cc(1522)] RemoveKeepAlive(Default, kOffTheRecordProfile). keep_alives=[] [71002:259:0328/133442.705296:VERBOSE1:profile_manager.cc(1567)] Deleting profile Default
第一步:退出主循环
当没有任何东西可以让 Chrome 保持活力时,就会开始关机。通常,当所有浏览器窗口都关闭时会发生这种情况,但其他事情可以让 Chrome 保持活动状态。
当没有任何东西可以让 Chrome 保持活动状态时,BrowserProcessImpl::Unpin
一旦它不再有准备好立即运行的任务,就会要求主线程的消息循环退出。
base::RunLoop::QuitWhenIdle … BrowserProcessImpl::Unpin BrowserProcessImpl::OnKeepAliveStateChanged KeepAliveRegistry::OnKeepAliveStateChanged KeepAliveRegistry::Unregister ScopedKeepAlive::~ScopedKeepAlive ... Browser::UnregisterKeepAlive BrowserList::RemoveBrowser Browser::~Browser
在此请求之后,ChromeBrowserMainParts::MainMessageLoopRun
退出。保证在此之前没有延迟地发布到主线程的任务已经运行;此后发布到主线程的任务将永远不会运行。
第 2 步:清理,在主循环退出后
BrowserMainRunnerImpl::Shutdown
在主线程上调用。在该方法中,BrowserMainLoop::ShutdownThreadsAndCleanUp
协调主要的关闭步骤。
ChromeBrowserMainParts::PostMainMessageLoopRun
被调用。它调用PostMainMessageLoopRun
每个ChromeBrowserMainExtraParts
实例的方法。这是执行需要 IO 线程ThreadPool
或Profile
仍然可用的组件的关闭步骤的好地方。
ChromeBrowserMainParts::PostMainMessageLoopRun
还调用BrowserProcessImpl::StartTearDown
删除BrowserProcessImpl
(aka g_browser_process
) 拥有的许多服务。其中一项服务是ProfileManager
. 删除ProfileManager
删除Profiles
。作为删除 a 的一部分Profile
,它KeyedServices
被删除,包括:
- 同步服务
- 历史服务
第 3 步:加入其他线程
IO 线程已加入。在此之后无法收到 IPC 或 Mojo。
ThreadPool
关机开始。此时,没有新的SKIP_ON_SHUTDOWN
或CONTINUE_ON_SHUTDOWN
任务可以开始运行(它们在没有运行的情况下被删除)。主线程阻塞,直到所有在关机开始SKIP_ON_SHUTDOWN
之前开始运行的任务ThreadPool
都完成,并且所有BLOCK_SHUTDOWN
任务都完成(无论它们是在ThreadPool
关机开始之前还是之后发布)。当没有SKIP_ON_SHUTDOWN
更多的任务在运行并且没有更多的BLOCK_SHUTDOWN
任务排队或运行时,主线程被解除阻塞并且ThreadPool
关闭被认为是完成的。注意:在关机CONTINUE_ON_SHUTDOWN
前启动的任务ThreadPool
可能仍在运行。
此时,发布到 IO 线程或发布到 IO 线程的新任务ThreadPool
无法运行。BLOCK_SHUTDOWN
将任务发布到ThreadPool
(由 a 强制执行)是非法的DCHECK
。
第四步:清理,加入其他线程后
ChromeBrowserMainParts::PostDestroyThreads
被调用。它调用BrowserProcessImpl::PostDestroyThreads
. 由于可以保证此时没有SKIP_ON_SHUTDOWN
任务BLOCK_SHUTDOWN
正在运行,因此最好删除从这些任务中直接访问的对象。
然后,如果是新的 Chrome 可执行文件,它会与当前的可执行文件交换(仅限 Windows)。
base::RunLoop::QuitWhenIdle … BrowserProcessImpl::Unpin BrowserProcessImpl::OnKeepAliveStateChanged KeepAliveRegistry::OnKeepAliveStateChanged KeepAliveRegistry::Unregister ScopedKeepAlive::~ScopedKeepAlive ... Browser::UnregisterKeepAlive BrowserList::RemoveBrowser Browser::~Browser
Chrome 操作系统差异
在 ChromeOS 上,ash 浏览器应该只在用户注销时退出。
当用户注销时,浏览器会向session_managerStopSession
发送一条消息。session_manager 然后向主浏览器进程发送一个 SIGTERM 以导致退出。一旦收到 SIGTERM,它就会开始关闭主循环并按照上述顺序进行清理。
与其他桌面平台不同,关机是有时间限制的。如果浏览器进程在某个时间范围内(通常为 3 秒)没有退出,则 session_manager 将 SIGKILL 浏览器进程,因为用户正在查看一个空白屏幕并且在浏览器退出之前无法使用他们的 Chromebook。
浏览器进程关闭
BrowserProcess
也被引用,很像Profile
. ScopedKeepAlive
禁止拆卸,并且 refcount 由KeepAliveRegistry
.
停止 UI 消息循环
- 接收到导致
~ScopedKeepAlive
(关闭窗口、关闭最后一个选项卡、应用程序终止的键盘快捷键等)的 UI 事件。 - 如果 refcount
KeepAliveRegistry
下降到零,那么我们开始应用程序终止。- 请注意,macOS 通过添加额外
ScopedKeepAlive
的AppController
.
- 请注意,macOS 通过添加额外
- 如果 refcount 达到零,则将任务发布到消息循环以退出
- 通知
content::NOTIFICATION_APP_TERMINATING
被广播 - UI 消息循环最终停止运行,我们退出
RunUIMessageLoop()
. 请注意,所有其他主要浏览器线程仍在运行它们的消息循环。因此,即使主 (UI) 线程比所有其他加入的浏览器线程寿命长,它也会MessageLoop
首先终止。
BrowserProcessImpl 删除
- 退出 UI 消息循环后,在 BrowserMainParts::RunMainMessageLoopParts 中启动关闭序列,它调用 PostMainMessageLoopRun。
- 在 ChromeBrowserMainParts::PostMainMessageLoopRun 中:
- ProcessSingleton 被释放。
- MetricsService(记录 UMA 指标)已停止,其中:
- 创建所有剩余指标的日志(以供将来上传)。
- 记录成功关机。
- 磁盘上的持久性指标文件仍然存在,以防在此之后更新其他指标。任何此类值都将在浏览器启动后的某个时间在下次运行期间发送。
- 我们将本地状态持久化到磁盘。
- 我们删除 g_browser_process,其中:
- 删除 ProfileManager(删除所有配置文件并保持其状态,例如 Preferences)
- 按顺序加入看门狗、IO、CACHE、PROCESS_LAUNCHER 线程
- 关闭 DownloadFileManager 和 SaveFileManager
- 加入 FILE 线程
- 删除加入 WEBKIT 线程的 ResourceDispatcherHost
- 加入数据库线程
ContentMain() 退出
- AtExitManager 超出范围并销毁所有 Singletons/LazyInstances
渲染器进程关闭
浏览器进程通过以下方式触发关闭:
- 关闭选项卡(解释从 UI 线程对象到渲染器进程的 IO 线程终止的事件序列)