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=</path/to/executable>\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=</path/to/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<string> 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<brillo::CrosConfig> cros_config =
    base::MakeUnique<brillo::CrosConfig>();
if (!cros_config->Init())
  cros_config = nullptr;
bool is_developer_end_user = false;
map<string, string> env_vars;
vector<string> 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<BrowserJob>(
      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<SessionManagerService> 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();
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,670评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,928评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,926评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,238评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,112评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,138评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,545评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,232评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,496评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,596评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,369评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,226评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,600评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,906评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,185评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,516评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,721评论 2 335

推荐阅读更多精彩内容