Anbox 的总体架构如 运行 Anbox 一文的相关内容所述,其运行时主要由两个分开的实例构成,容器管理器和会话管理器。anbox 用同一个可执行文件,在启动时通过不同的参数实现运行时执行两块完全不同的逻辑,完成容器管理和会话管理的任务。
在命令行中,为 anbox 可执行文件提供不同的 command 参数来确定具体执行什么样的实例。Anbox 通过同一个可执行文件,将多个功能完全不同的逻辑粘合起来。查看 anbox 的 help 信息,内容如下:
anbox 可执行文件支持的 command 参数除了容器管理器的 container-manager 和会话管理器的 session-manager,还包括 help,system-info,version,launch 等。
anbox 应用程序的 main() 函数(位于 anbox/src/main.cpp)如下:
在 main() 函数中,创建了 anbox::Daemon 对象,通过 anbox::utils::collect_arguments() 函数将 C 风格的命令行参数字符串数组,转为命令行参数的 std::string 数组表示。anbox::utils::collect_arguments() 定义(位于 anbox/src/anbox/utils.cpp 文件中)如下:
main() 函数完成一个简单的命令行参数转发,实际的应用程序入口位于 anbox::Daemon 类,该类定义(位于 anbox/src/anbox/daemon.h)如下:
这个类只有一个类型为 cli::CommandWithSubcommands 的成员变量 cmd,用于组织 Anbox 支持的所有命令。
anbox::Daemon 类的实现(位于 anbox/src/anbox/daemon.cpp)如下:
在 anbox::Daemon 类的构造函数中,收集支持的所有命令,并设置全局的日志等级,在 Run() 函数中,由标准输入流,标准输出流和参数数组构建 cli::Command::Context 传给 cli::CommandWithSubcommands 类的 run() 函数。cli::CommandWithSubcommands 类的 run() 函数是在应用的整个声明周期中永不结束的函数,anbox::Daemon::Run() 函数也一样,因而为 cli::CommandWithSubcommands 类的 run() 函数传递在栈上临时构造的 cli::Command::Context 对象的引用不会产生问题。
Anbox 的设计通过组合模式来组织各个命令,相关各个类的类图如下:

Anbox 的这些 Command 类的基类 anbox::cli::Command 定义(位于 anbox/src/anbox/cli.h)如下:
Name、Usage 和 Description 都是长度受限的字符串的封装。anbox::cli::Command 类实现(位于 anbox/src/anbox/cli.cpp)如下
anbox::cli::Command 类本身的实现主要是构造函数和几个 Getter 函数。run() 函数是命令执行的主体,也是 anbox::cli::Command 类最为重要的成员函数,其实现会交给其子类来完成。
anbox::cli::Command 类的子类 anbox::cli::CommandWithSubcommands 是 anbox::cli::Command 的容器,它集合了 Anbox 支持的所有命令,在执行时根据参数选择具体的 anbox::cli::Command 子类执行。anbox::cli::CommandWithSubcommands 类定义(位于 anbox/src/anbox/cli.h)如下:
anbox::cli::CommandWithSubcommands 类用一个 std::unordered_map 保存它维护的所有的 anbox::cli::Command 具体子类。anbox::cli::CommandWithSubcommands 类的实现(位于 anbox/src/anbox/cli.cpp)如下:
anbox::cli::CommandWithSubcommands 类的 command() 函数主要用于添加 Command 元素,flag() 函数用于添加 Flag 元素。help() 函数用于输出帮助信息,它主要是根据格式字符串,将 CommandWithSubcommands 及所有的子命令的名字、描述等内容格式化并输出。
run() 函数解析命令行参数,选择适当的具体 Command 并执行。
anbox::cli::Command 类的子类 anbox::cli::CommandWithFlagsAndAction 用于描述可以带一些参数选项的具体的 Command,如容器管理器,会话管理器等。Anbox 的具体 Command 的定制行为,不是通过 override 该类的 run() 函数,而是通过定义一个 std::function<int(const Context&)> Action 函数来实现的。Anbox 的具体 Command 通过 action() 函数将定制了行为的 Action 提交给 anbox::cli::CommandWithFlagsAndAction。
anbox::cli::CommandWithFlagsAndAction 定义(位于 anbox/src/anbox/cli.h)如下:
Anbox 用 Flag 表示命令行参数选项,boost 可以辅助解析命令行参数并设置一些类型为 std::string 或 bool 之类的状态。通过 flag() 函数可以为具体 Command 添加一个命令行参数选项。
anbox::cli::CommandWithFlagsAndAction 的实现(位于 anbox/src/anbox/cli.cpp)如下:
add_to_desc_for_flags() 函数将 flags_ 添加进 po::options_description,在后面通过 boost 的 command_line_parser 解析命令行参数时,与特定命令行参数选项相关联的状态会得到适当的更新。
cli::CommandWithFlagsAndAction::run(const Context& ctxt) 解析命令行参数并执行 Action。cli::CommandWithFlagsAndAction::help(std::ostream& out) 函数与 cli::CommandWithSubcommands 的相同函数的实现类似,它根据格式字符串,将命令行参数选项格式化并输出。
经过上面对 Anbox 的 Command 类结构体系的分析,我们获得了一个分析 Anbox 中如 SessionManager 和 ContainerManager 这样的具体 Command 实现的框架:
通过 flag() 函数可以提交一个 Flag,即一个命令行参数选项的描述及其关联的状态,该状态将在 Command 的 run() 函数执行初期通过解析命令行参数来更新;通过 action() 函数可以提交一个函数,作为 Command 行为的主体,该函数将会在 Command 的 run() 函数的最后执行。
无论是对哪个 cli::CommandWithFlagsAndAction 的子类的分析,我们都可以把它分成两部分来看:一是通过 flag() 函数提交 Flag,二是通过 action() 提交的函数。
打赏
Done。
 
        