本文主要介绍 session manager 对浏览器进程的管理过程,在读此之前可以阅读文章 session_manager简介 。
概述
针对关注的功能模块,绘制类图如下所示:
SessionManagerService 模块:整个 session manager 的核心,根据源码中的注释信息,这个类提供运行浏览器的方法,监控浏览器运行状况,并在必要的时候重启浏览器。
模块1:监控浏览器进程的退出信号,并在浏览器进程退出时,清理浏览器进程创建的子进程,如果有必要会重启浏览器。
模块2:浏览器进程在 session manager 中的描述信息,提供了对浏览器进程的各种管理方法。
模块3:浏览器进程的活性检测模块,负责周期性的 ping 浏览器进程,如果浏览器进程在规定的时间内没有回复消息,会发送 SIGABRT 信号终止浏览器进程。
模块4:session manager 对外提供的一些其他 DBus 接口,例如允许浏览器进程通过 session manager 提供的接口来实现结束会话、保存登录密码等功能。
session manager 还提供了对安卓容器 、锁频功能、电源和虚拟机的管理,这里没有过多的关注。
SessionManagerService
session manager 是一个独立的进程,所以有独立的主函数入口,分析 session manager 是从文件 platform2\login_manager\session_manager_main.cc 入手的,按照 main() 函数的流程进行依次分析。
从上面的类图可以看出,SessionManagerService 是整个 session manager 的核心,所有的工作都围绕这个类进行。从 session_manager_main.cc 的 main() 函数也可以看出 SessionManagerService 重要,大概 130 行的主函数,有 100 行是为执行 new SessionManagerService 做准备的。
在 main() 函数中有一段代码如下:
// Allow waiting for all descendants, not just immediate children.
if (::prctl(PR_SET_CHILD_SUBREAPER, 1))
PLOG(ERROR) << "Couldn't set child subreaper";
这段代码设置了 session manager 进程可以等待子进程的子进程,也就是说,如果 session manager 在等待浏览器进程退出,那么浏览器创建的子进程,例如 renderer 等,也会接受 session manager 的管理。
在执行 SessionManagerService 的构造函数时,函数体内只调用了一个函数 SetUpHandlers() ,这个函数主要是针对子进程(本文主要关注的是浏览器进程)退出时的信号注册关注这个事件的对象,设置捕获处理函数。
主函数中,在生成 SessionManagerService 后,执行这个对象的初始化操作,初始化成功之后,如果需要运行浏览器,就向线程消息循环中抛一个运行浏览器的任务,然后启动消息循环,整个主函数就差不多结束了。
在 SessionManagerService 的初始化过程中,主要是初始化 DBus 服务,获取锁屏、电源、虚拟机等的 DBus 代理对象,实例化一个浏览器活性检测的对象和一个 SessionManagerImpl 对象,最后启动 DBus 服务,至此就完成了初始化工作。
刚才提到“如果需要运行浏览器”,那是根据什么来判断是否要运行呢?
// This job encapsulates the command specified on the command line, and the
// runtime options for it.
auto browser_job = std::make_unique<BrowserJob>(
command, env_vars, &checker, &metrics, &system, config,
std::make_unique<login_manager::Subprocess>(uid, &system));
bool should_run_browser = browser_job->ShouldRunBrowser();
从代码片段可以看出,是根据 BrowserJob 对象来判断是否需要运行浏览器的,进一步深入分析,发现可以通过创建一个禁止浏览器重启的文件来让 BrowserJob 不启动浏览器,默认情况下这个文件是不存在的,所以会启动浏览器。
启动浏览器
在 session_manager_main.cc 主函数中,为浏览器启动做了大量的工作,主要是为浏览器启动准备参数,以及是否要对浏览器进行活性检测的判断设置,这些参数都保存在 BrowserJob 对象中。因此,启动浏览器的过程看上去就很简单了:
void SessionManagerService::RunBrowser() {
browser_->RunInBackground();
DLOG(INFO) << "Browser is " << browser_->CurrentPid();
liveness_checker_->Start();
}
在 BrowserJob::RunInBackground() 函数中,就是进一步的整合启动参数,最终调用系统函数 execve() 执行真正的启动。
浏览器退出状态监控
在 SessionManagerService 的 SetUpHandlers() 方法中,会实例化一个 ChildExitDispatcher 对象,在这个类的构造函数中,会注册一个对 SIGCHLD 信号的处理函数, SIGCHLD 是子进程退出的信号。
在 SIGCHLD 信号的处理函数中,会通过 waitid 函数来等待 session manager 子进程状态改变,并收集子进程退出的原因,再将导致子进程退出的信号分发给前面注册的对象中去处理。关于 waitid() 函数的资料网上比较多,感兴趣可以搜索查看。
在这里比较感兴趣的是 SessionManagerService 对子进程退出原因的处理,可以进入到对应的成员函数 HandleExit() 中查看处理过程。
在 HandleExit() 中,首先判断当前退出的子进程是否为浏览器进程,如果不是处理就结束了,如果是浏览器进程,就会执行清理动作,关闭浏览器进程的所有子进程,关闭安卓容器以及虚拟机,如果需要运行浏览器,就会再次启动浏览器。
浏览器活性检测
因为只有在运行浏览器的时候才需要执行浏览器活性检测,所以活性检测的启动是在运行浏览器的代码中。
在 LivenessCheckerImpl 的 Start() 函数中,定义了一个 base::CancelableClosure 对象,然后向主线程消息循环中抛了一个延时任务,延时时间就是检测周期。
如果在进行下一次检测前,浏览器进程尚未对上一次的 ping 进行回复,就会终止浏览器进程。
如果已经回复了上一次的检测,就执行当前的检测,最后再向主消息循环抛一个延时任务,从而实现周期性的检测。
小结
session manager 功能不止这些,不过本文主要关注 session manager 对浏览器生命周期的管理,所以其他功能都直接忽略了。