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閱讀 230,048評論 6 542
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,414評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,169評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,722評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,465評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,823評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,813評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,000評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,554評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,295評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,513評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,035評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,722評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,125評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,430評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,237評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,482評論 2 379

推薦閱讀更多精彩內容