文章托管在gitee上 Android Notes , 同步csdn 本文基于Android12 分析
概述
在init启动过程中,会启动一个subcontext进程,通常与init有着不一样的 secontext 以及 mount namespace。该进程用来接收来自init的命令,用来执行某些操作,这些操作是在 subcontext 的secontext 和 mount namespace 下进行。通过ps命令看看init及subcontext进程信息
# ps -AZ | grep init
u:r:init:s0 root 1 0 10904472 3956 do_epoll_wait 0 S init # 这个是 init 进程
u:r:vendor_init:s0 root 166 1 10780496 1924 do_sys_poll 0 S init # 这个是 subcontext 进程
Subcontext 进程启动与初始化
Subcontext 进程是在init启动第二阶段进行启动和初始化的,在SecondStageMain函数中调用InitializeSubcontext去完成相关操作。
SecondStageMain
int SecondStageMain(int argc, char** argv) {
...
const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
Action::set_function_map(&function_map);
...
if (!SetupMountNamespaces()) {
PLOG(FATAL) << "SetupMountNamespaces failed";
}
InitializeSubcontext();
ActionManager& am = ActionManager::GetInstance();
ServiceList& sm = ServiceList::GetInstance();
LoadBootScripts(am, sm);
...
}
SetupMountNamespaces
- 如果apex可更新并且不是recovery模式,则会创建一个新的mount namespace,被记为default,而原始的则称为 bootstrap
- 否则,default 和 bootstrap 的mount namespace是同一个
bool SetupMountNamespaces() {
if (!ChangeMount("/", MS_SHARED | MS_REC)) return false;
if (!(ChangeMount("/apex", MS_PRIVATE))) return false;
if (!(ChangeMount("/linkerconfig", MS_PRIVATE))) return false;
...
bootstrap_ns_fd.reset(OpenMountNamespace());
bootstrap_ns_id = GetMountNamespaceId();
bool success = true;
if (IsApexUpdatable() && !IsRecoveryMode()) {
if (unshare(CLONE_NEWNS) == -1) {
PLOG(ERROR) << "Cannot create mount namespace";
return false;
}
default_ns_fd.reset(OpenMountNamespace());
default_ns_id = GetMountNamespaceId();
if (setns(bootstrap_ns_fd.get(), CLONE_NEWNS) == -1) {
PLOG(ERROR) << "Cannot switch back to bootstrap mount namespace";
return false;
}
} else {
default_ns_fd.reset(OpenMountNamespace());
default_ns_id = GetMountNamespaceId();
}
#ifdef ACTIVATE_FLATTENED_APEX
success &= ActivateFlattenedApexesIfPossible();
#endif
LOG(INFO) << "SetupMountNamespaces done";
return success;
}
关于 mount namespace 可参考文章 浅谈Linux Namespace机制(一)
InitializeSubcontext
创建subcontext进程,用于执行init交给其的一些任务(比如后面执行的某些Command),通过socket与init进行通信
- 在Android P 开始才创建 subcontext
- 使用的secontext是 kVendorContext, 即 u:r:vendor_init:s0
void InitializeSubcontext() {
if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_P__) {
subcontext.reset(
new Subcontext(std::vector<std::string>{"/vendor", "/odm"}, kVendorContext));
}
}
Subcontext(std::vector<std::string> path_prefixes, std::string context, bool host = false)
: path_prefixes_(std::move(path_prefixes)), context_(std::move(context)), pid_(0) {
if (!host) {
Fork();
}
}
Subcontext::Fork
- 创建Socket pair,用于subcontext与init进行通信
- fork() 创建子进程 subcontext
- subcontext通过setexeccon设置安全上下文,通过setns 设置为默认mount space
- 通过execv执行/system/bin/init 进入subcontext业务逻辑
void Subcontext::Fork() {
unique_fd subcontext_socket;
if (!Socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, &socket_, &subcontext_socket)) {
LOG(FATAL) << "Could not create socket pair to communicate to subcontext";
return;
}
auto result = fork();
if (result == -1) {
LOG(FATAL) << "Could not fork subcontext";
} else if (result == 0) {
socket_.reset();
int child_fd = dup(subcontext_socket);
if (child_fd < 0) {
PLOG(FATAL) << "Could not dup child_fd";
}
if (context_ != kTestContext) {
if (setexeccon(context_.c_str()) < 0) {
PLOG(FATAL) << "Could not set execcon for '" << context_ << "'";
}
}
#if defined(__ANDROID__)
if (auto result = SwitchToMountNamespaceIfNeeded(NS_DEFAULT); !result.ok()) {
LOG(FATAL) << "Could not switch to \"default\" mount namespace: " << result.error();
}
#endif
auto init_path = GetExecutablePath();
auto child_fd_string = std::to_string(child_fd);
const char* args[] = {init_path.c_str(), "subcontext", context_.c_str(),
child_fd_string.c_str(), nullptr};
execv(init_path.data(), const_cast<char**>(args));
PLOG(FATAL) << "Could not execv subcontext init";
} else {
subcontext_socket.reset();
pid_ = result;
LOG(INFO) << "Forked subcontext for '" << context_ << "' with pid " << pid_;
}
}
SwitchToMountNamespaceIfNeeded
Result<void> SwitchToMountNamespaceIfNeeded(MountNamespace target_mount_namespace) {
if (IsRecoveryMode() || !IsApexUpdatable()) {
return {};
}
const auto& ns_id = target_mount_namespace == NS_BOOTSTRAP ? bootstrap_ns_id : default_ns_id;
const auto& ns_fd = target_mount_namespace == NS_BOOTSTRAP ? bootstrap_ns_fd : default_ns_fd;
const auto& ns_name = target_mount_namespace == NS_BOOTSTRAP ? "bootstrap" : "default";
if (ns_id != GetMountNamespaceId() && ns_fd.get() != -1) {
if (setns(ns_fd.get(), CLONE_NEWNS) == -1) {
return ErrnoError() << "Failed to switch to " << ns_name << " mount namespace.";
}
}
return {};
}
subcontext入口在init main方法中,通过参数可以知道启动的是哪个,对于subcontext则会调用SubcontextMain函数
int main(int argc, char** argv) {
...
if (argc > 1) {
if (!strcmp(argv[1], "subcontext")) {
android::base::InitLogging(argv, &android::base::KernelLogger);
const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
return SubcontextMain(argc, argv, &function_map);
}
if (!strcmp(argv[1], "selinux_setup")) {
return SetupSelinux(argv);
}
if (!strcmp(argv[1], "second_stage")) {
return SecondStageMain(argc, argv);
}
}
return FirstStageMain(argc, argv);
}
SubcontextMain
int SubcontextMain(int argc, char** argv, const BuiltinFunctionMap* function_map) {
if (argc < 4) LOG(FATAL) << "Fewer than 4 args specified to subcontext (" << argc << ")";
auto context = std::string(argv[2]);
auto init_fd = std::atoi(argv[3]);
SelabelInitialize();
trigger_shutdown = [](const std::string& command) { shutdown_command = command; };
auto subcontext_process = SubcontextProcess(function_map, context, init_fd);
setpriority(PRIO_PROCESS, 0, 0);
subcontext_process.MainLoop();
return 0;
}
SubcontextProcess::MainLoop
进入主循环,等待init消息并处理相关请求。
- poll等待读事件的发生,通常是init发送相关命令请求
- ReadMessage(init_fd_) 读取来自 init 的消息
- subcontext_command.ParseFromString 解析init消息
- 根据Command类别执行不同的操作
- kExecuteCommand 执行指定的命令
- kExpandArgsCommand 展开给定的参数。
- SendMessage回复init执行结果
void SubcontextProcess::MainLoop() {
pollfd ufd[1];
ufd[0].events = POLLIN;
ufd[0].fd = init_fd_;
while (true) {
ufd[0].revents = 0;
int nr = TEMP_FAILURE_RETRY(poll(ufd, arraysize(ufd), -1));
if (nr == 0) continue;
if (nr < 0) {
PLOG(FATAL) << "poll() of subcontext socket failed, continuing";
}
auto init_message = ReadMessage(init_fd_);
if (!init_message.ok()) {
if (init_message.error().code() == 0) {
return;
}
LOG(FATAL) << "Could not read message from init: " << init_message.error();
}
auto subcontext_command = SubcontextCommand();
if (!subcontext_command.ParseFromString(*init_message)) {
LOG(FATAL) << "Unable to parse message from init";
}
auto reply = SubcontextReply();
switch (subcontext_command.command_case()) {
case SubcontextCommand::kExecuteCommand: {
RunCommand(subcontext_command.execute_command(), &reply);
break;
}
case SubcontextCommand::kExpandArgsCommand: {
ExpandArgs(subcontext_command.expand_args_command(), &reply);
break;
}
default:
LOG(FATAL) << "Unknown message type from init: "
<< subcontext_command.command_case();
}
if (!shutdown_command.empty()) {
reply.set_trigger_shutdown(shutdown_command);
shutdown_command.clear();
}
if (auto result = SendMessage(init_fd_, reply); !result.ok()) {
LOG(FATAL) << "Failed to send message to init: " << result.error();
}
}
}
ReadMessage
从socket 中读取消息, 存储到 string
inline Result<std::string> ReadMessage(int socket) {
char buffer[kBufferSize] = {};
auto result = TEMP_FAILURE_RETRY(recv(socket, buffer, sizeof(buffer), 0));
if (result == 0) {
return Error();
} else if (result < 0) {
return ErrnoError();
}
return std::string(buffer, result);
}
SendMessage
将消息序列化为string,然后通过socket发送
template <typename T>
Result<void> SendMessage(int socket, const T& message) {
std::string message_string;
if (!message.SerializeToString(&message_string)) {
return Error() << "Unable to serialize message";
}
if (message_string.size() > kBufferSize) {
return Error() << "Serialized message too long to send";
}
if (auto result =
TEMP_FAILURE_RETRY(send(socket, message_string.c_str(), message_string.size(), 0));
result != static_cast<long>(message_string.size())) {
return ErrnoError() << "send() failed to send message contents";
}
return {};
}
SubcontextCommand、SubcontextReply
上面两个用来接收命令,发送反馈的类,在源码的实现是通过proto2。通过SubcontextCommand定义可知,它目前支持两种命令,execute_command和expand_args_command。
syntax = "proto2";
option optimize_for = LITE_RUNTIME;
message SubcontextCommand {
message ExecuteCommand { repeated string args = 1; }
message ExpandArgsCommand { repeated string args = 1; }
oneof command {
ExecuteCommand execute_command = 1;
ExpandArgsCommand expand_args_command = 2;
}
}
message SubcontextReply {
message Failure {
optional string error_string = 1;
optional int32 error_errno = 2;
}
message ExpandArgsReply { repeated string expanded_args = 1; }
oneof reply {
bool success = 1;
Failure failure = 2;
ExpandArgsReply expand_args_reply = 3;
}
optional string trigger_shutdown = 4;
}
关于proto2 请参考 快来看看Google出品的Protocol Buffer
init发送命令
在 init(2) rc脚本解析和事件执行流程 中,分析过Command的解析与执行。通常,每一个Command都在内置函数Map有对应的一行,如下所示。第四列true/false表示是否在subcontext中执行。
static const BuiltinFunctionMap builtin_functions =
{"copy", {2, 2, {true, do_copy}}},
{"copy_per_line", {2, 2, {true, do_copy_per_line}}},
{"domainname", {1, 1, {true, do_domainname}}},
{"enable", {1, 1, {false, do_enable}}},
{"exec", {1, kMax, {false, do_exec}}},
{"exec_background", {1, kMax, {false, do_exec_background}}},
{"exec_start", {1, 1, {false, do_exec_start}}},
以copy命令为例,看看它的实现。当执行该命令时,会调用Command::InvokeFunc
Command::InvokeFunc
- 当指定了不为空的subcontext
- 若execute_in_subcontext_为true,则会直接调用subcontext->Execute
- 否则会先subcontext->ExpandArgs通过subcontext进程对args进行膨胀处理(主要是将 ${prop_name} 解析成对应的属性值),然后在调用RunBuiltinFunction
- 未指定subcontext则直接调用RunBuiltinFunction
Result<void> Command::InvokeFunc(Subcontext* subcontext) const {
if (subcontext) {
if (execute_in_subcontext_) {
return subcontext->Execute(args_);
}
auto expanded_args = subcontext->ExpandArgs(args_);
if (!expanded_args.ok()) {
return expanded_args.error();
}
return RunBuiltinFunction(func_, *expanded_args, subcontext->context());
}
return RunBuiltinFunction(func_, args_, kInitContext);
}
copy指定了在subcontext执行,因此会调用subcontext->Execute
Subcontext::Execute
Result<void> Subcontext::Execute(const std::vector<std::string>& args) {
auto subcontext_command = SubcontextCommand();
std::copy(
args.begin(), args.end(),
RepeatedPtrFieldBackInserter(subcontext_command.mutable_execute_command()->mutable_args()));
auto subcontext_reply = TransmitMessage(subcontext_command);
if (!subcontext_reply.ok()) {
return subcontext_reply.error();
}
if (subcontext_reply->reply_case() == SubcontextReply::kFailure) {
auto& failure = subcontext_reply->failure();
return ResultError(failure.error_string(), failure.error_errno());
}
if (subcontext_reply->reply_case() != SubcontextReply::kSuccess) {
return Error() << "Unexpected message type from subcontext: "
<< subcontext_reply->reply_case();
}
return {};
}
Subcontext::TransmitMessage
Result<SubcontextReply> Subcontext::TransmitMessage(const SubcontextCommand& subcontext_command) {
if (auto result = SendMessage(socket_, subcontext_command); !result.ok()) {
Restart();
return ErrnoError() << "Failed to send message to subcontext";
}
auto subcontext_message = ReadMessage(socket_);
if (!subcontext_message.ok()) {
Restart();
return Error() << "Failed to receive result from subcontext: " << subcontext_message.error();
}
auto subcontext_reply = SubcontextReply{};
if (!subcontext_reply.ParseFromString(*subcontext_message)) {
Restart();
return Error() << "Unable to parse message from subcontext";
}
if (subcontext_reply.has_trigger_shutdown()) {
trigger_shutdown(subcontext_reply.trigger_shutdown());
}
return subcontext_reply;
}
从上面分析可知,init发送了一个类型为execute_command的消息到 subcontext,在SubcontextProcess::MainLoop中收到请求后,根据类型执行相关动作,即调用RunCommand
switch (subcontext_command.command_case()) {
case SubcontextCommand::kExecuteCommand: {
RunCommand(subcontext_command.execute_command(), &reply);
break;
}
...
}
...
if (auto result = SendMessage(init_fd_, reply); !result.ok()) {
LOG(FATAL) << "Failed to send message to init: " << result.error();
}
SubcontextProcess::RunCommand
void SubcontextProcess::RunCommand(const SubcontextCommand::ExecuteCommand& execute_command,
SubcontextReply* reply) const {
auto args = std::vector<std::string>();
for (const auto& string : execute_command.args()) {
args.emplace_back(string);
}
auto map_result = function_map_->Find(args);
Result<void> result;
if (!map_result.ok()) {
result = Error() << "Cannot find command: " << map_result.error();
} else {
result = RunBuiltinFunction(map_result->function, args, context_);
}
if (result.ok()) {
reply->set_success(true);
} else {
auto* failure = reply->mutable_failure();
failure->set_error_string(result.error().message());
failure->set_error_errno(result.error().code());
}
}
RunBuiltinFunction
Result<void> RunBuiltinFunction(const BuiltinFunction& function,
const std::vector<std::string>& args, const std::string& context) {
auto builtin_arguments = BuiltinArguments(context);
builtin_arguments.args.resize(args.size());
builtin_arguments.args[0] = args[0];
for (std::size_t i = 1; i < args.size(); ++i) {
auto expanded_arg = ExpandProps(args[i]);
if (!expanded_arg.ok()) {
return expanded_arg.error();
}
builtin_arguments.args[i] = std::move(*expanded_arg);
}
return function(builtin_arguments);
}
ExpandProps
将属性参数展开为对应的值
- 属性的形式可以是 $x.y 或者 ${x.y}。但 $x.y 这种形式从R开始不再支持
- 连续两个$被解析为$。{}内部的内嵌属性不再解析,如 ${foo.${bar}}
- 可以指定默认值,当属性值为空时返回。如 ${x.y:-default} , default即默认值
Result<std::string> ExpandProps(const std::string& src) {
const char* src_ptr = src.c_str();
std::string dst;
while (*src_ptr) {
const char* c;
c = strchr(src_ptr, '$');
if (!c) {
dst.append(src_ptr);
return dst;
}
dst.append(src_ptr, c);
c++;
if (*c == '$') {
dst.push_back(*(c++));
src_ptr = c;
continue;
} else if (*c == '\0') {
return dst;
}
std::string prop_name;
std::string def_val;
if (*c == '{') {
c++;
const char* end = strchr(c, '}');
if (!end) {
return Error() << "unexpected end of string in '" << src << "', looking for }";
}
prop_name = std::string(c, end);
c = end + 1;
size_t def = prop_name.find(":-");
if (def < prop_name.size()) {
def_val = prop_name.substr(def + 2);
prop_name = prop_name.substr(0, def);
}
} else {
prop_name = c;
if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_R__) {
return Error() << "using deprecated syntax for specifying property '" << c
<< "', use ${name} instead";
} else {
LOG(ERROR) << "using deprecated syntax for specifying property '" << c
<< "', use ${name} instead";
}
c += prop_name.size();
}
if (prop_name.empty()) {
return Error() << "invalid zero-length property name in '" << src << "'";
}
std::string prop_val = android::base::GetProperty(prop_name, "");
if (prop_val.empty()) {
if (def_val.empty()) {
return Error() << "property '" << prop_name << "' doesn't exist while expanding '"
<< src << "'";
}
prop_val = def_val;
}
dst.append(prop_val);
src_ptr = c;
}
return dst;
}
Command是copy情景下,function(builtin_arguments) 调用的函数是 do_copy
do_copy
static Result<void> do_copy(const BuiltinArguments& args) {
auto file_contents = ReadFile(args[1]);
if (!file_contents.ok()) {
return Error() << "Could not read input file '" << args[1] << "': " << file_contents.error();
}
if (auto result = WriteFile(args[2], *file_contents); !result.ok()) {
return Error() << "Could not write to output file '" << args[2] << "': " << result.error();
}
return {};
}
subcontext在执行完命令后,会通过SendMessage向init发送回复信息。
总结
- init启动过程第二阶段启动subcontext进程,创建socket pair用来与subcontext进程通信
- subcontext进程初始化,设置 secontext 及 mount namespace
- subcontext在MainLoop中通过poll等待init的消息
- init执行某些命令时,通过socket向subcontext发送消息,让其执行某些操作
- subcontext收到消息后,根据消息类型执行相关操作,并回写执行结果
|