plymouth原理

背景

最近遇到桌面系统中,开机动画卡顿的问题,第一印象感觉好像是显卡驱动或者是硬件问题,绘图慢,从而导致卡顿。

但是,打开plymouth相关的调试开关后并没有发现明显的错误打印,甚是疑惑。于是,对开机动画组件plymouth做了一番研究,其实就是看代码了,搞明白其原理后,才明白原来是这么回事儿。

开机动画相关概念

开机动画就是在开机后,系统启动过程中看到的一些动画显示,通常的桌面系统中(服务器系统不一定),都会有这样的动画,目的是不想让用户看到具体的启动过程,另一方面也更美观。

Linux中,开机动画基本都是用开源组件plymouth,没有研究名字的来源,有点怪怪的~

plymouth相关原理

终于到正题了,plymouth总体来说,比我预想中的复杂,并不是简单的一些动画而已,其功能还比较强大,而且跟systemd绑在一起,有点过度设计的嫌疑~

plymouth整体分两个主要部分(还有一些边角的功能,不做分析了),服务端和客户端,典型的C/S模型。服务端和客户端直接通过socket通信。

  • 服务端。是一个后台守护进程plymouthd,用于处理请求,请求种类有很多,比如典型的update、quit等。服务端通过epoll监控相关socket(也有管道),监听来自客户端的信息。
  • 客户端。客户端可以多种多样,典型的客户端有:plymouth程序、systemd。客户端通过socket(也有管道)与服务端建立连接,并通过socket(也有管道)发送具体的请求。

plymouthd服务端

如前面所说,plymouthd守护进程作为开机动画的服务端,是最核心的部分,这节主要分析plymouthd的相关原理。

主函数流程

plymouthd服务端的主函数入口为src/main.c文件中main()函数,主要流程为:

  1. 解析参数
  2. 创建后台守护进程
  3. 初始化环境
  4. 启动服务器(socket),并监听(listen)来自客户端的连接消息
  5. 从cache文件中获取每个服务对应的进度信息
  6. 进入消息循环

代码如下(含注释):

/*plymouthd服务的主函数入口*/
int
main (int    argc,
      char **argv)
{
  state_t state = { 0 };
  int exit_code;
  bool should_help = false;
  bool no_daemon = false;
  bool debug = false;
  bool attach_to_session;
  ply_daemon_handle_t *daemon_handle = NULL;
  char *mode_string = NULL;
  char *kernel_command_line = NULL;
  char *tty = NULL;
  /*参数解析器*/
  state.command_parser = ply_command_parser_new ("plymouthd", "Splash server");
  /*创建默认消息循环*/
  state.loop = ply_event_loop_get_default ();
  /*参数*/
  ply_command_parser_add_options (state.command_parser,
                                  "help", "This help message", PLY_COMMAND_OPTION_TYPE_FLAG,
                                  "attach-to-session", "Redirect console messages from screen to log", PLY_COMMAND_OPTION_TYPE_FLAG,
                                  "no-daemon", "Do not daemonize", PLY_COMMAND_OPTION_TYPE_FLAG,
                                  "debug", "Output debugging information", PLY_COMMAND_OPTION_TYPE_FLAG,
                                  "debug-file", "File to output debugging information to", PLY_COMMAND_OPTION_TYPE_STRING,
                                  "mode", "Mode is one of: boot, shutdown", PLY_COMMAND_OPTION_TYPE_STRING,
                                  "pid-file", "Write the pid of the daemon to a file", PLY_COMMAND_OPTION_TYPE_STRING,
                                  "kernel-command-line", "Fake kernel command line to use", PLY_COMMAND_OPTION_TYPE_STRING,
                                  "tty", "TTY to use instead of default", PLY_COMMAND_OPTION_TYPE_STRING,
                                  NULL);
  /*解析参数*/
  if (!ply_command_parser_parse_arguments (state.command_parser, state.loop, argv, argc))
    {
      char *help_string;

      help_string = ply_command_parser_get_help_string (state.command_parser);

      ply_error_without_new_line ("%s", help_string);

      free (help_string);
      return EX_USAGE;
    }
  /*获取参数*/
  ply_command_parser_get_options (state.command_parser,
                                  "help", &should_help,
                                  "attach-to-session", &attach_to_session,
                                  "mode", &mode_string,
                                  "no-daemon", &no_daemon,
                                  "debug", &debug,
                                  "debug-file", &debug_buffer_path,
                                  "pid-file", &pid_file,
                                  "tty", &tty,
                                  "kernel-command-line", &kernel_command_line,
                                  NULL);

  if (should_help)
    {
      char *help_string;

      help_string = ply_command_parser_get_help_string (state.command_parser);

      if (argc < 2)
        fprintf (stderr, "%s", help_string);
      else
        printf ("%s", help_string);

      free (help_string);
      return 0;
    }
  /*是否开启debug选项,开启后能有详细的日志*/
  if (debug && !ply_is_tracing ())
    ply_toggle_tracing ();

  if (mode_string != NULL)
    {
      if (strcmp (mode_string, "shutdown") == 0)
        state.mode = PLY_MODE_SHUTDOWN;
      else if (strcmp (mode_string, "updates") == 0)
        state.mode = PLY_MODE_UPDATES;
      else
        state.mode = PLY_MODE_BOOT;

      free (mode_string);
    }

  if (tty != NULL)
    {
      state.default_tty = tty;
    }

  if (kernel_command_line != NULL)
    {
      strncpy (state.kernel_command_line, kernel_command_line, sizeof (state.kernel_command_line));
      state.kernel_command_line[sizeof (state.kernel_command_line) - 1] = '\0';
      state.kernel_command_line_is_set = true;
    }

  if (geteuid () != 0)
    {
      ply_error ("plymouthd must be run as root user");
      return EX_OSERR;
    }

  chdir ("/");
  signal (SIGPIPE, SIG_IGN);

  if (! no_daemon)
    {
      /*创建后台守护进程,前台进程退出*/
      daemon_handle = ply_create_daemon ();

      if (daemon_handle == NULL)
        {
          ply_error ("plymouthd: cannot daemonize: %m");
          return EX_UNAVAILABLE;
        }
    }

  if (debug)
    debug_buffer = ply_buffer_new ();

  signal (SIGABRT, on_crash);
  signal (SIGSEGV, on_crash);

  /* before do anything we need to make sure we have a working
   * environment.
   */
  /*初始化环境*/
  if (!initialize_environment (&state))
    {
      if (errno == 0)
        {
          if (daemon_handle != NULL)
            ply_detach_daemon (daemon_handle, 0);
          return 0;
        }

      ply_error ("plymouthd: could not setup basic operating environment: %m");
      if (daemon_handle != NULL)
        ply_detach_daemon (da
  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值