Android 12 init start&stop命令流程分析

Posted pecuyu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 12 init start&stop命令流程分析相关的知识,希望对你有一定的参考价值。

文章托管在gitee上 Android Notes , 同步csdn
本文基于android12 分析

概述

通常,在开发过程中,需要push一些修改到系统分区,之后需要重启系统使修改生效。如果修改的是系统框架相关的,通常可以只重启系统框架,一般在shell下面执行如下命令即可:

pecuyu-PC:~$ adb shell
emulator64_x86_64_arm64:/ $ stop
Must be root
emulator64_x86_64_arm64:/ $ su
emulator64_x86_64_arm64:/ # stop
emulator64_x86_64_arm64:/ # start

不过,执行stop、start命令需要root权限,这点需要注意。接下来分析这两个命令的执行流程。

stop、start程序的实现

在shell里面查看两个命令,发现它们都是软连接,指向的都是 toolbox

$ ls -l /system/bin/stop                                                                                                       
lrwxr-xr-x 1 root shell 7 2021-12-20 15:52 /system/bin/stop -> toolbox
$ ls -l /system/bin/start                                                                                                      
lrwxr-xr-x 1 root shell 7 2021-12-20 15:52 /system/bin/start -> toolbox
$ ls -lZ /system/bin/toolbox
-rwxr-xr-x 1 root shell u:object_r:toolbox_exec:s0  128984 2021-12-20 15:30 /system/bin/toolbox

toolbox 实现

toolbox是一个可执行程序,它的实现在 system/core/toolbox/

// 声明一系列函数
// 定义TOOL, 在tools.h使用此宏,声明所有工具函数
#define TOOL(name) int name##_main(int, char**); // 如 name##_main -> start_main
#include "tools.h"
#undef TOOL

static struct 
    const char* name; // 函数名与函数指针
    int (*func)(int, char**);
 tools[] =  // 定义工具列表
#define TOOL(name)  #name, name##_main ,  // 初始化一个结构体, 如  start,start_main ,
#include "tools.h"  // 展开并初始化结构体数组
#undef TOOL
     0, 0 ,
;

上面导入tools.h头文件,其中调用TOOL来实现功能,可以知道定义了如下工具

/// @system/core/toolbox/tools.h
TOOL(getevent)
TOOL(getprop)
TOOL(modprobe)
TOOL(setprop)
TOOL(start)
TOOL(stop)
TOOL(toolbox)

在shell中执行toolbox,就可以知道有哪些命令,即是上面声明的命令。

$ toolbox                                                                                                                      
getprop modprobe setprop start stop toolbox

直接执行toolbox调用的是toolbox_main函数

int toolbox_main(int argc, char** argv) 
    // "toolbox foo ..." is equivalent to "foo ..."
    if (argc > 1)  // 如果指定参数,调用main继续处理命令
        return main(argc - 1, argv + 1);
    

    // Plain "toolbox" lists the tools.
    for (size_t i = 1; tools[i].name; i++)  // 打印所有命令名
        printf("%s%c", tools[i].name, tools[i+1].name ? ' ' : '\\n');
    
    return 0;

toolbox#main

看看toolbox的main函数实现,toolbox_main函数实际上也是从这里进入的。执行 stop/start命令也是从这个main进入,argv[0] 对应是stop/start,根据函数名调用具体的实现函数(stop_main/start_main)。

/// @system/core/toolbox/toolbox.c
int main(int argc, char** argv) 
    // Let's assume that none of this code handles broken pipes. At least ls,
    // ps, and top were broken (though I'd previously added this fix locally
    // to top). We exit rather than use SIG_IGN because tools like top will
    // just keep on writing to nowhere forever if we don't stop them.
    signal(SIGPIPE, SIGPIPE_handler); // 处理SIGPIPE,默认动作exit

    char* cmd = strrchr(argv[0], '/'); // 寻找最后一个 /
    char* name = cmd ? (cmd + 1) : argv[0]; // 如果存在/ ,则后面是函数名, 否则整体是函数名

    for (size_t i = 0; tools[i].name; i++)  // 遍历所有的tool,查找名字匹配的
        if (!strcmp(tools[i].name, name)) 
            return tools[i].func(argc, argv);  // 当name是 start,则调用 start_main
        
    

    printf("%s: no such tool\\n", argv[0]);
    return 127;

stop_main/start_main 实现

从上面

/// @system/core/toolbox/start.cpp
extern "C" int start_main(int argc, char** argv) 
    return StartStop(argc, argv, true);


extern "C" int stop_main(int argc, char** argv) 
    return StartStop(argc, argv, false);

StartStop

  • 必须在root下执行
  • 没有参数, 则去执行 stop、start 所有默认服务,即"netd"、“surfaceflinger”、“audioserver”、“zygote”,
  • 指定一个参数为–help,则打印帮助,如 start --help
  • 指定了一些服务,则去启动相关服务,比如 start zygote
static int StartStop(int argc, char** argv, bool start) 
    if (getuid()) // uid 为0 即 root 才能执行
        std::cerr << "Must be root" << std::endl;
        return EXIT_FAILURE;
    

    if (argc == 1)  // 没有参数, 则去执行 stop、start
        ControlDefaultServices(start);
    

    if (argc == 2 && argv[1] == "--help"s)  // 打印help,如 start --help
        std::cout << "usage: " << (start ? "start" : "stop")
                  << " [SERVICE...]\\n"
                     "\\n"
                  << (start ? "Starts" : "Stops")
                  << " the given system service, or netd/surfaceflinger/zygotes." << std::endl;
        return EXIT_SUCCESS;
    

    for (int i = 1; i < argc; ++i)  // 指定了某个服务名
        ControlService(start, argv[i]);
    
    return EXIT_SUCCESS;

ControlDefaultServices

static void ControlDefaultServices(bool start) 
    std::vector<std::string> services =  // 默认 停止、启动 的服务
        "netd",
        "surfaceflinger",
        "audioserver",
        "zygote",
    ;

    // Only start zygote_secondary if not single arch.
    std::string zygote_configuration = GetProperty("ro.zygote", ""); // 读取zygote配置
    if (zygote_configuration != "zygote32" && zygote_configuration != "zygote64")  // 没有指定zygote32或zygote64
        services.emplace_back("zygote_secondary");
    

    if (start)  // 启动默认服务
        for (const auto& service : services) 
            ControlService(true, service);
        
     else  // 停止默认服务
        for (auto it = services.crbegin(); it != services.crend(); ++it) 
            ControlService(false, *it);
        
    

ControlService

通过设置控制属性 ctl.start、ctl.stop 来启动、停止服务

static void ControlService(bool start, const std::string& service) 
    if (!android::base::SetProperty(start ? "ctl.start" : "ctl.stop", service)) 
        std::cerr << "Unable to " << (start ? "start" : "stop") << " service '" << service
                  << "'\\nSee dmesg for error reason." << std::endl;
        exit(EXIT_FAILURE);
    

通过init(3) 属性服务 分析可知, init的属性服务将处理该请求。

init 处理属性设置请求

在init的属性服务中处理设置属性请求,该实现在HandlePropertySet函数处理。

/// @system/core/init/property_service.cpp
// This returns one of the enum of PROP_SUCCESS or PROP_ERROR*.
uint32_t HandlePropertySet(const std::string& name, const std::string& value,
                           const std::string& source_context, const ucred& cr,
                           SocketConnection* socket, std::string* error) 
    if (auto ret = CheckPermissions(name, value, source_context, cr, error); ret != PROP_SUCCESS) 
        return ret;
    

    if (StartsWith(name, "ctl."))  // 处理控制属性
        // +4 跳过 ctl.  , value 表示服务名
        return SendControlMessage(name.c_str() + 4, value, cr.pid, socket, error);
    
    ...

SendControlMessage

/// @system/core/init/property_service.cpp
static uint32_t SendControlMessage(const std::string& msg, const std::string& name, pid_t pid,
                                   SocketConnection* socket, std::string* error) 
    auto lock = std::lock_guardaccept_messages_lock;
    if (!accept_messages)  // 关机过程不再处理
        *error = "Received control message after shutdown, ignoring";
        return PROP_ERROR_HANDLE_CONTROL_MESSAGE;
    

    // We must release the fd before sending it to init, otherwise there will be a race with init.
    // If init calls close() before Release(), then fdsan will see the wrong tag and abort().
    int fd = -1;
    if (socket != nullptr && SelinuxGetVendorAndroidVersion() > __ANDROID_API_Q__) 
        fd = socket->Release();
    

    bool queue_success = QueueControlMessage(msg, name, pid, fd); // 控制消息入队等待处理
    if (!queue_success && fd != -1)  // 回写错误信息
        uint32_t response = PROP_ERROR_HANDLE_CONTROL_MESSAGE;
        TEMP_FAILURE_RETRY(send(fd, &response, sizeof(response), 0));
        close(fd);
    

    return PROP_SUCCESS;

QueueControlMessage

/// @system/core/init/init.cpp
bool QueueControlMessage(const std::string& message, const std::string& name, pid_t pid, int fd) 
    auto lock = std::lock_guardpending_control_messages_lock;
    if (pending_control_messages.size() > 100)  // 队列待处理任务大于100,则丢弃此事件
        LOG(ERROR) << "Too many pending control messages, dropped '" << message << "' for '" << name
                   << "' from pid: " << pid;
        return false;
    
    pending_control_messages.push(message, name, pid, fd); // 添加到待处理队列
    WakeMainInitThread(); // 唤醒主线程处理
    return true;

SecondStageMain 循环处理控制事件

如下是 init 主循环,负责处理相关事件。

/// @system/core/init/main.cpp
int SecondStageMain(int argc, char** argv) 
...
while (true) 
    // By default, sleep until something happens. 计算epool超时
    auto epoll_timeout = std::optional<std::chrono::milliseconds>;
    ...
    auto pending_functions = epoll.Wait(epoll_timeout); // WakeMainInitThread导致Wait收到新事件返回
    ...
    if (!IsShuttingDown())  // 不是正在关机,
        HandleControlMessages(); // 处理控制消息
        SetUsbController();
    
    ...

return 0;

HandleControlMessages

处理控制消息。 一次只处理一个事件,防止占用过多时间,导致其他事件得不得处理

static void HandleControlMessages() 
    auto lock = std::unique_lockpending_control_messages_lock; // 持锁,防止其他地方并发修改
    // Init historically would only execute handle one property message, including control messages
    // in each iteration of its main loop.  We retain this behavior here to prevent starvation of
    // other actions in the main loop.
    if (!pending_control_messages.empty())  // 队列不为空
        auto control_message = pending_control_messages.front();
        pending_control_messages.pop(); // 取出队列头元素并移除队列
        lock.unlock();
        // 处理控制消息
        bool success = HandleControlMessage(control_message.message, control_message.name,
                                            control_message.pid);

        uint32_t response = success ? PROP_SUCCESS : PROP_ERROR_HANDLE_CONTROL_MESSAGE;
        if (control_message.fd != -1)  // 回写响应信息
            TEMP_FAILURE_RETRY(send(control_message.fd, &response, sizeof(response), 0));
            close(control_message.fd);
        
        lock.lock();
    
    // If we still have items to process, make sure we wake back up to do so.
    if (!pending_control_messages.empty())  // 队列不为空,扔需要唤醒
        WakeMainInitThread();
    

HandleControlMessage

处理一条控制消息

/// @system/core/init/init.cpp
static bool HandleControlMessage(std::string_view message, const std::string& name,
                                 pid_t from_pid) 
    // 获取发起进程的cmdline                               
    std::string cmdline_path = StringPrintf("proc/%d/cmdline", from_pid);
    std::string process_cmdline;
    if (ReadFileToString(cmdline_path, &process_cmdline)) 
        std::replace(process_cmdline.begin(), process_cmdline.end(), '\\0', ' ');
        process_cmdline = Trim(process_cmdline);
     else 
        process_cmdline = "unknown process";
    

    Service* service = nullptr;
    auto action = message; // action 是 ctl. 后面的部分,比如 ctl.start 则 action 是 start
    if (ConsumePrefix(&action, "interface_"))  // 比如interface_start、interface_stop 启动aidl型服务
        service = ServiceList::GetInstance().FindInterface(name);
     else  // 启动某个native服务,比如 surfaceflinger
        service = ServiceList::GetInstance().FindService(name);
    

    if (service == nullptr) 
        LOG(ERROR) << "Control message: Could not find '" << name << "' for ctl." << message
                   << " from pid: " << from_pid << " (" << process_cmdline << ")";
        return false;
    

    const auto& map = GetControlMessageMap();
    const auto it = map.find(action); // 寻找action其对应的函数
    if (it == map.end()) 
        LOG(ERROR) << "Unknown control msg '" << message << "'";
        return false;
    
    const auto& function = it->second;

    if (auto result = function(service); !result.ok())  // 调用对应的函数,start 对应的函数是 do_start
        LOG(ERROR) << "Control message: Could not ctl." << message << " for '" << name
                   << "' from pid: " << from_pid << " (" << process_cmdline
                   << "): " << result.error();
        return false;
    

    LOG(INFO以上是关于Android 12 init start&stop命令流程分析的主要内容,如果未能解决你的问题,请参考以下文章

android init.rc中service console option的含义作用

ANDROID init进程

Android启动init.rc中service的权限问题

Android 12 init 属性服务

Android 12 init 属性服务

Caused by: java.lang.NoSuchMethodException: &lt;init&gt; [class android.content.Context, int