Anbox 运行时主要由两个分开的实例构成,即容器管理器 ContainerManager 和会话管理器 SessionManager,但无论是 ContainerManager 还是 SessionManager,所做的最重要的事情就是处理网络 I/O 事件了。I/O 模型指一个应用处理 I/O 事件的整体框架设计,对于 Anbox 而言主要是处理各种网络 I/O 事务的整体框架设计。ContainerManager 和 SessionManager 基于一个相同的 I/O 模型运行,为了获得对 Anbox 整体行为设计的理解,有必要对其 I/O 模型有个整体的认识。
I/O 模型
Anbox 的 I/O 模型基于 boost.asio 构建。Anbox 中所有的 I/O 事件,在一个线程池中,通过一个 boost::asio::io_service
对象来派发并处理。Anbox 用 anbox::Runtime
类封装一个 boost::asio::io_service
对象,并管理执行任务的线程池。
anbox::Runtime
类的定义(位于 anbox/src/anbox/runtime.h
)如下:
anbox::Runtime
类封装了一个 boost::asio::io_service
对象及多个工作线程 std::thread
,它还继承 std::enable_shared_from_this<Runtime>
以获得从 this
指针创建智能指针 std::shared_ptr
的能力,同时继承了 DoNotCopyOrMove
,以禁掉类的拷贝和移动操作。
anbox::Runtime
类的实现(位于 anbox/src/anbox/runtime.cpp
)如下:
anbox::Runtime
类有两大职责,一是 boost::asio::io_service
对象的生命周期管理;二是向 boost::asio::io_service
中提交任务。
在 anbox::Runtime::start()
函数中创建并启动多个线程,执行一个执行 boost::asio::io_service::run()
函数的函数 exception_safe_run()
。在 anbox::Runtime::stop()
函数中停掉 boost::asio::io_service
的执行。anbox::Runtime
的析够函数中,还会调用 stop()
函数停掉 boost::asio::io_service
的执行。anbox::Runtime
的类型为 boost::asio::io_service::work
的成员变量 keep_alive_
也是用于管理 boost::asio::io_service
对象的生命周期的,该对象在析够时也会停掉 boost::asio::io_service
的执行。
对于不熟悉 boost 库的朋友来说,boost::asio::io_service
可以理解为一个 I/O 多路复用器,就像许多网络库通过 select/poll/epoll/kqueue 实现的那样,或者可以理解为以 Reactor 模式实现的网络库中的一个事件循环,可以向其中提交 I/O 任务、定时器及其它任务等。boost::asio::io_service::strand
可以理解为 boost::asio::io_service
中的一个特殊的子任务队列,该类保证向其提交的所有任务都不会并发执行,以此消除这些任务之间的同步问题。
anbox::Runtime::to_dispatcher_functional()
函数返回一个接收一个函数为参数的函数,返回的这个函数可以将它接收的函数作为一个 task,通过 boost::asio::io_service::strand
提交给 boost::asio::io_service
执行。anbox::Runtime::service()
返回 boost::asio::io_service
用于方便一些 boost I/O 组件,直接向该 io_service
中提交任务。
继承自 std::enable_shared_from_this<Runtime>
的 shared_from_this()
可以从 this
指针创建一个指向当前对象的 std::shared_ptr
智能指针。通过对标准库源码的分析,可以知道 std::enable_shared_from_this
的实现原理大体如下:std::enable_shared_from_this<Runtime>
有一个类型为 std::weak_ptr<Runtime>
的成员,该成员在首次创建指向对象的 std::shared_ptr
智能指针的时候会被初始化。shared_from_this()
函数被调用时,会从类型为 std::weak_ptr<Runtime>
的成员变量创建指向当前对象的 std::shared_ptr
智能指针。
Anbox 的 I/O 模型可以理解为,底层有 一个 多路复用器或事件循环 boost::asio::io_service
,有一个包含了 8 个线程的线程池基于此 boost::asio::io_service
运行,处理 I/O 事件及其它各种类型的任务。
有了用于执行 I/O 事件处理程序的 boost::asio::io_service
,接下来来看一下,Anbox 都会向其中提交哪些任务。
Anbox 需要处理如下这样一些网络 I/O 过程:
监听 Unix 域 Socket 接受连接。Anbox 的 SessionManager 通过 Unix 域 Socket 与 ContainerManager 进行通信,同时也通过 Unix 域 Socket 与 ContainerManager 启动的 Android 容器内的应用程序通信。首先 ContainerManager 监听在特定位置的 Unix 域 Socket 上。随后 SessionManager 监听几个位置上的 Unix 域 Socket,然后请求 ContainerManager 启动 Android 容器,并将这几个 Unix 域 Socket 映射到容器内的
/dev/
目录下。Android 容器启动后,一些进程,如 surfaceflinger、cameraservice 等连接这些 Unix 域 Socket,并通过这些 Unix 域 Socket 与 SessionManager 通信,进而操作宿主机的硬件设备。监听 TCP Socket 接受连接。Anbox 的 SessionManager 作为容器中运行的 Android 与 ADB 进行通信的桥梁,它在与容器中运行的 Android 通过 Unix 域 Socket 通信的同时,也需要与宿主机上的 ADB 通信。SessionManager 通过 TCP 与宿主机上的 ADB 守护进程通信。如同模拟器等 Android 设备一样,SessionManager 遵从 ADB 的通信协议,在发起与 ADB 之间的 TCP 连接的同时,也需要监听一个 TCP 端口,等待 ADB 守护进程发起的连接,以完成整个 ADB 协议。
处理从监听的 Unix 域 Socket 接受的 Unix 域 Socket。监听的 Unix 域 Socket 接受新连接之后,需要将新创建的 Unix 域 Socket 提交给底层的 I/O 多路复用器,并为该 Socket 提供读写等 I/O 事件处理处理回调,以完成 Anbox 的应用逻辑。
处理从监听的 TCP Scoket 接受的 TCP Socket。监听的 TCP Socket 接受新连接之后,需要将新创建的 TCP Socket 提交给底层的 I/O 多路复用器,并为该 Socket 提供读写等 I/O 事件处理处理回调,以完成 Anbox 的应用逻辑。
发起一个到 TCP 服务器的连接。如前面提到的,Anbox 的 SessionManager 通过 TCP 连接与 ADB 守护进程通信,它会先发起一个到 ADB 守护进程的 TCP 连接。
发起一个到 Unix 域 Socket 服务的连接。Anbox 的 SessionManager 与 ContainerManager 之间通过 Unix 域 Socket 通信,SessionManager 会发起到 ContainerManager 监听的 Unix 域 Socket 服务的连接。
监听 Unix 域 Socket 接受连接
在 boost.asio 中,监听 Socket 并接受新连接,通过 acceptor 完成,对于具体的 Unix 域 Socket 而言,是通过 boost::asio::local::stream_protocol::acceptor
。boost::asio::local::stream_protocol::acceptor
类对象在创建时,需要为构造函数传入 boost::asio::io_service
及 Unix 域 Socket 的路径,通过 acceptor 的 async_accept()
函数,可以将 Unix 域 Socket 及处理新连接到来时的回调函数提交给底层的 I/O 多路复用器,异步地启动对 Unix 域 Socket 的监听。
在 Anbox 中,通过 anbox::network::PublishedSocketConnector
封装 boost::asio::local::stream_protocol::acceptor
,并为它定义处理新连接到来时的回调函数。anbox::network::PublishedSocketConnector
类的定义(位于 anbox/src/anbox/network/published_socket_connector.h
)如下:
anbox::network::PublishedSocketConnector
保存了 Unix 域 Socket 文件的路径,指向 anbox::Runtime
对象的智能指针,一个指向 ConnectionCreator<boost::asio::local::stream_protocol>
对象的智能指针,及 一个 boost::asio::local::stream_protocol::acceptor
。指向 anbox::Runtime
对象的智能指针主要用于从 anbox::Runtime
对象获得其管理的 boost::asio::io_service
的引用。boost::asio::local::stream_protocol::acceptor
的作用如前所述,ConnectionCreator<boost::asio::local::stream_protocol>
的作用后面再来讨论。
anbox::network::PublishedSocketConnector
类的实现(位于 anbox/src/anbox/network/published_socket_connector.cpp
)如下:
anbox::network::PublishedSocketConnector
类对象构造完成之后,即会调用 start_accept()
启动对 Unix 域 Socket 的监听。
anbox::network::PublishedSocketConnector
的 start_accept()
首先创建一个 boost::asio::local::stream_protocol::socket
对象用于保存下次接受的新 Socket 的相关信息,然后将这个对象,及作为处理新连接到来时的回调函数而创建的 lambda 表达式传递给 boost::asio::local::stream_protocol::acceptor
的 async_accept()
函数以启动对 Unix 域 Socket 的监听。
接收到新到来的连接之后,传给 boost::asio::local::stream_protocol::acceptor
的 async_accept()
的 lambda 回调函数直接将 Socket 交给 anbox::network::PublishedSocketConnector::on_new_connection()
来处理,anbox::network::PublishedSocketConnector::on_new_connection()
又将新连接交给 ConnectionCreator<boost::asio::local::stream_protocol>
来处理。
由此不难理解,在 Anbox 的设计中,监听并接受连接与对新连接的处理,由两个完全不同的组件来处理,前者由 anbox::network::Connector
处理,后者由 anbox::network::ConnectionCreator
处理。anbox::network::ConnectionCreator
定义(位于 anbox/src/anbox/network/connection_creator.h
)如下:
监听 TCP Socket 接受连接
Anbox 中监听 TCP Socket 并接受连接的逻辑与监听 Unix 域 Socket 并接受连接的逻辑非常类似,只是依赖的底层 boost.asio 的 acceptor 变为了 boost::asio::ip::tcp::acceptor
。封装 boost::asio::ip::tcp::acceptor
组件等由 anbox::network::TcpSocketConnector
完成,这个类的定义(位于 anbox/src/anbox/network/tcp_socket_connector.h
)如下:
监听的地址,不像 anbox::network::PublishedSocketConnector
那样用一个路径来描述就可以了,而是要通过 IP 地址和端口号来描述。
anbox::network::TcpSocketConnector
类的实现(位于 anbox/src/anbox/network/tcp_socket_connector.cpp
)如下:
启动监听及新连接到来时的回调处理都与 anbox::network::PublishedSocketConnector
类似,只是调用 acceptor 的 async_accept()
之前创建的是 boost::asio::ip::tcp::socket
对象,新连接到来之后,则是交给 ConnectionCreator<boost::asio::ip::tcp>
来处理的。
Anbox 中,监听 Socket 相关(包括对 Unix 域 Socket 的监听和对 TCP Socket 的监听)的类结构如下:
处理从监听的 Unix 域 Socket 接受的 Unix 域 Socket
在 Anbox 中,使用了 anbox::network::PublishedSocketConnector
来监听 Unix 域 Socket 的组件包括如下这些:
组件 | 所用 ConnectionCreator |
---|---|
anbox::audio::Server | anbox::network::DelegateConnectionCreator |
anbox::cmds::SessionManager | anbox::rpc::ConnectionCreator |
anbox::container::Service | anbox::network::DelegateConnectionCreator |
anbox::input::Device | anbox::network::DelegateConnectionCreator |
在 Anbox 中监听 Unix 域 Socket 的组件有多个,对于新到来的连接的处理方式有着一定的差异,但这种处理又都依赖于相同的底层 Unix 域 Socket 处理框架。
anbox::rpc::ConnectionCreator
清晰地为我们展示了,Anbox 中处理新接受的 Unix 域 Socket 的基本方式,来看这个类的实现。anbox::rpc::ConnectionCreator
的定义(位于 anbox/src/anbox/rpc/connection_creator.h
)如下:
anbox::rpc::ConnectionCreator
实现用于处理新到来的连接的 create_connection_for()
函数。anbox::rpc::ConnectionCreator
的实现(位于 anbox/src/anbox/rpc/connection_creator.cpp
)如下:
从 anbox::rpc::ConnectionCreator::create_connection_for()
的实现,可以看到 Anbox 处理新接受的 Unix 域 Socket 的基本流程:
- 为新的
boost::asio::local::stream_protocol::socket
对象创建anbox::network::LocalSocketMessenger
,用于将 Socket 添加进底层的 I/O 多路复用器,Socket 的读写等操作。 - 创建消息处理回调函数。当检测到 Socket 上有消息到来时,消息会被读取,并传递给这个回调函数来处理。
- 根据前面创建的
anbox::network::LocalSocketMessenger
和消息处理回调创建anbox::network::SocketConnection
。anbox::network::SocketConnection
是底层 Socket(即anbox::network::LocalSocketMessenger
) 与其用户之间的桥梁,anbox::network::SocketConnection
用于直接将 Socket 提交给底层 I/O 多路复用器以异步读取数据,帮助发送数据,以及拷贝底层数据读取缓冲区的数据并调用用户的消息处理回调函数。 - 为
anbox::network::SocketConnection
设置名称。 - 将
anbox::network::SocketConnection
保存于一个容器,以便于做一些整体的控制。anbox::network::SocketConnection
持有指向该容器的智能指针,以便于在连接断开时,将自身从容器中移除。 - 调用
anbox::network::SocketConnection
的read_next_message()
函数,将 Socket 提交给底层的 I/O 多路复用器,以启动接收数据。这个函数十分类似于前面看到的anbox::network::PublishedSocketConnector
和anbox::network::TcpSocketConnector
的start_accept()
函数。
anbox::network::SocketConnection
类的定义(位于 anbox/src/anbox/network/socket_connection.h
)如下:
anbox::network::SocketConnection
类的实现(位于 anbox/src/anbox/network/socket_connection.cpp
)如下:
在 anbox::network::SocketConnection::read_next_message()
中,用于存放读取的数据的缓冲区由 anbox::network::SocketConnection
提供,这与 anbox::network::PublishedSocketConnector
和 anbox::network::TcpSocketConnector
的 start_accept()
函数中,事先创建好保存下次到来的新连接的状态的 Socket 非常类似。
另外一个 anbox::network::ConnectionCreator
实现者是 anbox::network::DelegateConnectionCreator
,它仅仅是将处理新连接的职责转出去给一个函数,因而通常实际的新连接处理者也是 anbox::network::PublishedSocketConnector
的创建者。anbox::network::DelegateConnectionCreator
的定义及实现如下:
使用了 anbox::network::DelegateConnectionCreator
的情况下,各个组件自己处理新到来的连接的方式,与 anbox::rpc::ConnectionCreator
中处理新到来的连接的方式大同小异。
在 anbox::cmds::SessionManager
中(位于 anbox/src/anbox/cmds/session_manager.cpp
)还有一个 anbox::network::ConnectionCreator
的实现:
这个实现,与 anbox::network::DelegateConnectionCreator
在 delegate
为空时的行为一致,看上去这个类的定义似乎有些多余。
处理从监听的 TCP Scoket 接受的 TCP Socket
Anbox 中只有一处监听了 TCP Socket,即在 ADB 消息处理器 anbox::qemu::AdbMessageProcessor
中。anbox::qemu::AdbMessageProcessor
中处理与 ADB 守护进程的交互的函数主要包括如下这些:
anbox::qemu::AdbMessageProcessor
是在容器中的 Android 内的 adbd
守护进程通过 Unix 域 Socket /dev/qemu_pipe
与 SessionManager 建立连接时创建的,它作为容器中的 Android 内的 adbd
守护进程与宿主机的 ADB 守护进程之间的桥梁而存在。在 anbox::qemu::AdbMessageProcessor::wait_for_host_connection()
函数中,会启动对一个 TCP 端口的监听。
anbox::qemu::AdbMessageProcessor
为 anbox::network::TcpSocketConnector
传入的 anbox::network::ConnectionCreator
是 anbox::network::DelegateConnectionCreator
,为 anbox::network::DelegateConnectionCreator
绑定的 delegate 函数是 anbox::qemu::AdbMessageProcessor::on_host_connection()
,这也就是说,对新接受的连接的处理是由 anbox::qemu::AdbMessageProcessor::on_host_connection()
完成的。
anbox::qemu::AdbMessageProcessor::on_host_connection()
中会根据 <boost::asio::basic_stream_socket<boost::asio::ip::tcp>
创建一个 anbox::network::TcpSocketMessenger
用于将新 Socket 提交给底层的 I/O 多路复用器,读写数据等。messenger_
表示与容器中的 Android 内的 adbd 之间的连接,anbox::qemu::AdbMessageProcessor::on_host_connection()
在接收到 ADB 守护进程发起的连接之后,还会向容器中的 Android 内的 adbd 发送一条 ok
命令。随后容器中的 Android 内的 adbd 会回复一条消息,以通过 anbox::qemu::AdbMessageProcessor::read_next_host_message()
启动对新连接上的数据的接收。
anbox::qemu::AdbMessageProcessor::read_next_host_message()
启动对新连接上的数据的接收,类似于 anbox::network::SocketConnection
的 read_next_message()
,连接上的数据接收到之后,由 anbox::qemu::AdbMessageProcessor::on_host_read_size()
处理,这个函数与在 anbox::rpc::ConnectionCreator
中看到的传递给 anbox::network::SocketConnection
的 anbox::network::MessageProcessor
一样。
anbox::qemu::AdbMessageProcessor::on_host_read_size()
对收到的数据的处理方式就是,把收到的数据转发给容器中的 Android 内的 adbd 进程。
Anbox 中与监听的 Socket 上新接受的连接的处理有关的组件如下图所示:
Connector
接受新连接之后,将为新连接创建的 Socket 传给 ConnectionCreator
。Anbox 中的几个 ConnectionCreator
实现对新连接的处理方式类似:为新连接创建 SocketMessenger
,用于将新连接加入底层 I/O 多路复用器,从新连接读取数据,或者向连接中写入数据。使用者可以在启动对 Socket 上的读事件进行监听时,传入 AnboxReadHandler
回调,在数据到达时得到通知并对数据做一些处理。SocketConnection
为连接管理提供了一些方便,它封装
SocketMessenger
,并可以在收到数据时,通过另外的一个回调 MessageProcessor
将接收到的数据通知出去。更上层通过实现 AnboxReadHandler
或 MessageProcessor
来拿到接收到的数据并做处理。
发起一个到 Unix 域 Socket 服务的连接
Anbox 的 SessionManager 会通过 anbox::container::Client
发起与 ContainerManager 的连接,这是一个 Unix 域 Socket 连接。在 anbox::container::Client
中可以看到 Anbox 中发起到 Unix 域 Socket 服务的连接的过程。
anbox::container::Client
的定义(位于 anbox/src/anbox/container/client.h
)如下:
这个类的各成员变量中,messenger_
表示与目标服务的连接;buffer_
是数据接收所需的缓冲区;pending_calls_
、rpc_channel_
、management_api_
和 processor_
都用于处理与目标服务间的通信,它们与 SessionManager 自身的逻辑紧密相关;terminate_callback_
用于连接结束。
anbox::container::Client
的实现(位于 anbox/src/anbox/container/client.cpp
)如下:
在 anbox::container::Client
的构造函数中,通过传入目标域 Socket 的地址及 std::shared_ptr<Runtime>
来创建 anbox::network::LocalSocketMessenger
。在 anbox::network::LocalSocketMessenger
的构造函数中会自动与目标服务建立连接。
anbox::network::LocalSocketMessenger
的定义(位于 anbox/src/anbox/network/local_socket_messenger.h
)如下:
anbox::network::LocalSocketMessenger
的实现(位于 anbox/src/anbox/network/local_socket_messenger.cpp
)如下:
anbox::network::LocalSocketMessenger
对象创建完成后,将 Socket 提交给底层的 I/O 多路复用器,数据的收发及 Socket 的管理等,都与前面看到的为接受的新连接而创建的 anbox::network::LocalSocketMessenger
类似。
发起一个到 TCP 服务器的连接
在 Anbox 中,仅有一个地方发起了到 TCP 服务器的连接,即在我们前面已经看到过的 anbox::qemu::AdbMessageProcessor::wait_for_host_connection()
函数中:
在 Anbox 中,发起到 TCP 服务器的连接即是以目标 IP 地址和端口创建 anbox::network::TcpSocketMessenger
对象。anbox::network::TcpSocketMessenger
在其构造函数中会完成与目标服务的连接建立。anbox::network::TcpSocketMessenger
定义(位于 anbox/src/anbox/network/tcp_socket_messenger.h
)如下:
anbox::network::TcpSocketMessenger
实现(位于 anbox/src/anbox/network/tcp_socket_messenger.cpp
)如下:
anbox::network::TcpSocketMessenger
对象创建完成之后,将 Socket 提交给底层的 I/O 多路复用器,数据的收发及 Socket 的管理,都与前面看到的为接受的新连接而创建的 anbox::network::TcpSocketMessenger
类似。
发起一个到 Unix 域 Socket 服务的连接,与发起一个到 TCP 服务器的连接的过程极为相似,都是通过提供一个具体的地址创建一个 SocketMessenger
对象,具体 SocketMessenger
类的构造函数会自动建立连接。新创建的 SocketMessenger
对象与从接受的 Socket 创建的 SocketMessenger
对象无异。
打赏
Done。