session_manager启动

启动环境

session_manager在文件/etc/init/ui.conf中通过UpStart启动,具体启动内容为:

env UI_LOG_DIR=/var/log/ui
env UI_LOG_FILE=ui.LATEST

env CHROME_COMMAND_FLAG

exec session_manager \
  "${CHROME_COMMAND_FLAG}" \
  >"${UI_LOG_DIR}/${UI_LOG_FILE}" 2>&1

相关定义

session_manager启动代码位于源码目录src/platform2/login_manager/session_manager_main.cc

开关定义

src/platform2/login_manager/session_manager_main.cc中首先为Chrome运行定义了部分开关:

namespace switches {

// Name of the flag that contains the command for running Chrome.
static const char kChromeCommand[] = "chrome-command";
static const char kChromeCommandDefault[] = "/opt/google/chrome/chrome";

// Name of the flag that contains the path to the file which disables restart of
// managed jobs upon exit or crash if the file is present.
static const char kDisableChromeRestartFile[] = "disable-chrome-restart-file";
// The default path to this file.
static const char kDisableChromeRestartFileDefault[] =
    "/run/disable_chrome_restart";

// Flag that causes session manager to show the help message and exit.
static const char kHelp[] = "help";
// The help message shown if help flag is passed to the program.
static const char kHelpMessage[] =
    "\nAvailable Switches: \n"
    "  --chrome-command=\n"
    "    Path to the Chrome executable. Split along whitespace into arguments\n"
    "    (to which standard Chrome arguments will be appended); a value like\n"
    "    \"/usr/local/bin/strace /path/to/chrome\" may be used to wrap Chrome "
    "in\n"
    "    another program. (default: /opt/google/chrome/chrome)\n"
    "  --disable-chrome-restart-file=\n"
    "    Magic file that causes this program to stop restarting the\n"
    "    chrome binary and exit. (default: /run/disable_chrome_restart)\n";
}  // namespace switches

变量定义

src/platform2/login_manager/session_manager_main.cc中还定义了如下这些变量:

// Directory in which per-boot metrics flag files will be stored.
static const char kFlagFileDir[] = "/run/session_manager";

// Hang-detection magic file and constants.
static const char kHangDetectionFlagFile[] = "enable_hang_detection";
static const uint32_t kHangDetectionIntervalDefaultSeconds = 60;
static const uint32_t kHangDetectionIntervalShortSeconds = 5;

// Time to wait for children to exit gracefully before killing them
// with a SIGABRT.
static const int kKillTimeoutDefaultSeconds = 3;
static const int kKillTimeoutLongSeconds = 12;

其中目录/run/session_manager中存储了与启动相关的一些信息,该目录下包含三个文件:

  1. logged_in: 用于表明本次启动是否成功登陆。值为1则表示登陆成功。
  2. machine-info: 用于启动过程中写入机器信息,信息如下:
    "mlb_serial_number"="QCC0C81LA51900261"
    "initial_locale"="en-US"
    "initial_timezone"="America/Los_Angeles"
    "keyboard_layout"="xkb:us::eng"
    "region"="us"
    "serial_number"="F5N0CX222377209"
    "___ro_rw_delimiter___"="___RW_VPD_below___"
    "gbind_attribute"="1873ca856c1b05ca21a0a6c86f1bcc96fa97d8d473d572ba377c2039b101df1cd155f4c9"
    "ubind_attribute"="3ef20362e87e105a655b2510c70bc8ba97ef3d358ed16e0ace0389c263dcf3a223e9140e"
    "ActivateDate"="2015-30"
    root_disk_serial_number=0x017bdd97
    

1 per_boot_flag: 启动度量信息,通常为空。

执行分析

初始化处理

session_manager主函数在文件src/platform2/login_manager/session_manager_main.cc中,进入主函数,首先做了如下几件事:

  1. 声明退出函数;
  2. 初始化命令行参数;
  3. 获取当前进程的命令行参数;
  4. 初始化日志子系统;

对应代码为:

  base::AtExitManager exit_manager;
  base::CommandLine::Init(argc, argv);
  base::CommandLine* cl = base::CommandLine::ForCurrentProcess();
  brillo::InitLog(brillo::kLogToSyslog | brillo::kLogHeader);

超级进程设置

使用系统函数prctl的参数PR_SET_CHILD_SUBREAPER将当前进程设置为subreaper进程,之后该进程就可以像进程ID号为1的进程那样,递归收养所有由该进程派生出的孤儿进程,该功能在Linux Kernel 3.4之后加入,对应代码如下:

  // Allow waiting for all descendants, not just immediate children
  if (::prctl(PR_SET_CHILD_SUBREAPER, 1))
    PLOG(ERROR) << "Couldn't set child subreaper";

处理非必要参数

如果命令行参数指定为--help,则打印帮助信息然后退出即可:

  if (cl->HasSwitch(switches::kHelp)) {
    LOG(INFO) << switches::kHelpMessage;
    return 0;
  }

处理传入的Chrome参数

接下来就是处理传入Chrome所需的命令行参数:

  // Parse the base Chrome command.
  string command_flag(switches::kChromeCommandDefault);
  if (cl->HasSwitch(switches::kChromeCommand))
    command_flag = cl->GetSwitchValueASCII(switches::kChromeCommand);
  vector command =
      base::SplitString(command_flag, base::kWhitespaceASCII,
                        base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);

这里的操作具体有:默认chrome的二进制文件路径为/opt/google/chrome/chrome,但是如果有设置开关chrome-command的值,则将其对应的值设置为二进制文件路径。接下来通过、将空白字符转义、保留空白和去除空串的方式,对命令行进行正规化处理。

设置Chrome命令行参数

然后是设置session_manager所启用的Chrome的命令行参数:

// Set things up for running Chrome.
std::unique_ptr cros_config =
    base::MakeUnique();
if (!cros_config->Init())
  cros_config = nullptr;
bool is_developer_end_user = false;
map env_vars;
vector args;
uid_t uid = 0;
PerformChromeSetup(
    cros_config.get(), &is_developer_end_user, &env_vars, &args, &uid);
command.insert(command.end(), args.begin(), args.end());

最终设置的命令行参数如下所示:

/opt/google/chrome/chrome
--ui-prioritize-in-gpu-process
--use-gl=egl
--gpu-sandbox-failures-fatal=yes
--enable-logging
--log-level=1
--use-cras
--enable-wayland-server
--user-data-dir=/home/chronos
--max-unused-resource-memory-usage-percentage=5
--system-developer-mode
--login-profile=user
--has-chromeos-keyboard
--enable-prefixed-encrypted-media
--enable-consumer-kiosk
--enterprise-enrollment-initial-modulus=15
--enterprise-enrollment-modulus-limit=19
--login-manager
--first-exec-after-boot
--vmodule=*chromeos/login/*=1,auto_enrollment_controller=1,*plugin*=2,*zygote*=1,*/ui/ozone/*=1,*/ui/display/manager/chromeos/*=1,power_button_observer=2,webui_login_view=2,lock_state_controller=2,webui_screen_locker=2,screen_locker=2

其中核心函数PerformChromeSetup实现在文件src/platform2/login_manager/chrome_setup.cc

声明系统核心调用

声明一个SystemUtilsImpl类型的变量system用于系统调用和操作:

  // Shim that wraps system calls, file system ops, etc.
  SystemUtilsImpl system;

检查是否停止session_manager管理浏览器进程

检查是否设置了手动停止或启动浏览器时,session_manager保持运行状态,也就是说session_manager在此时不再管理浏览器进程:

  // Checks magic file that causes the session_manager to stop managing the
  // browser process. Devs and tests can use this to keep the session_manager
  // running while stopping and starting the browser manaually.
  string magic_chrome_file =
      cl->GetSwitchValueASCII(switches::kDisableChromeRestartFile);
  if (magic_chrome_file.empty())
    magic_chrome_file.assign(switches::kDisableChromeRestartFileDefault);
  FileChecker checker((base::FilePath(magic_chrome_file)));  // So vexing!

该文件默认为/var/run/disable_chrome_restart,如果该文件存在则表示启用该项设置。

创建度量信息保存目录

  // Used to report various metrics around user type (guest vs non), dev-mode,
  // and policy/key file status.
  base::FilePath flag_file_dir(kFlagFileDir);
  if (!base::CreateDirectory(flag_file_dir))
    PLOG(FATAL) << "Cannot create flag file directory at " << kFlagFileDir;
  LoginMetrics metrics(flag_file_dir);

默认的度量信息保存目录为/run/session_manager/

检查是否停止session_manager检查浏览器存活状态

默认情况下,session_manager会周期性检查浏览器是否存活,在开发模式下会导致部分问题,因为调试浏览器会导致该功能不可用,因此可以通过创建一个标签文件的方式来禁用该功能,默认情况下该文件目录为/run/session_manager/enable_hang_detection:

  // The session_manager supports pinging the browser periodically to check that
  // it is still alive.  On developer systems, this would be a problem, as
  // debugging the browser would cause it to be aborted. Override via a
  // flag-file is allowed to enable integration testing.
  bool enable_hang_detection = !is_developer_end_user;
  uint32_t hang_detection_interval = kHangDetectionIntervalDefaultSeconds;
  if (base::PathExists(flag_file_dir.Append(kHangDetectionFlagFile)))
    hang_detection_interval = kHangDetectionIntervalShortSeconds;

修改浏览器平滑退出检测时间

对于包含旋转磁盘的平台来说,Chrome会花费更多的时间来关闭,在这种情况下,需要知道是什么原因导致关闭花费了更多的时间,也因此在杀次Chrome并触发报告之前需要等待更久,这里提出了如何修改该事件的方法:

  // On platforms with rotational disks, Chrome takes longer to shut down. As
  // such, we need to change our baseline assumption about what "taking too long
  // to shutdown" means and wait for longer before killing Chrome and triggering
  // a report.
  int kill_timeout = kKillTimeoutDefaultSeconds;
  if (BootDeviceIsRotationalDisk())
    kill_timeout = kKillTimeoutLongSeconds;
  LOG(INFO) << "Will wait " << kill_timeout << "s for graceful browser exit.";

正规化命令行

这里对命令行参数和调用者的UID进行了整合,为执行最终的命令做好了最后的准备:

  // This job encapsulates the command specified on the command line, and the
  // UID that the caller would like to run it as.
  auto browser_job = base::MakeUnique(
      command, env_vars, uid, &checker, &metrics, &system);
  bool should_run_browser = browser_job->ShouldRunBrowser();

创建消息循环

基于brillo下的BaseMessageLoop创建消息循环:

  base::MessageLoopForIO message_loop;
  brillo::BaseMessageLoop brillo_loop(&message_loop);
  brillo_loop.SetAsCurrent();

创建session_manager服务

创建的session_manager服务对应SessionManagerService类的实现:

  scoped_refptr manager = new SessionManagerService(
      std::move(browser_job),
      uid,
      kill_timeout,
      enable_hang_detection,
      base::TimeDelta::FromSeconds(hang_detection_interval),
      &metrics,
      &system);

执行消息循环

初始化管理其后执行消息循环:

  if (manager->Initialize()) {
    // Allows devs to start/stop browser manually.
    if (should_run_browser) {
      brillo_loop.PostTask(
          FROM_HERE, base::Bind(&SessionManagerService::RunBrowser, manager));
    }
    // Returns when brillo_loop.BreakLoop() is called.
    brillo_loop.Run();
  }

执行清理动作

在退出循环后,还需要执行部分清理工作:

  manager->Finalize();

退出服务

运行的最后退出session_manager服务:

  LOG_IF(WARNING, manager->exit_code() != SessionManagerService::SUCCESS)
      << "session_manager exiting with code " << manager->exit_code();
  return manager->exit_code();

你可能感兴趣的:(session_manager启动)