Android系统的启动流程

简介: Android系统的启动流程

1.用户看到的 Android 系统的启动过程:

  1. 按下电源键
  2. 进入开机动画
  3. 经过漫长的等待(大概几分钟)
  4. 开机动画结束,正式开机
  5. 进入系统桌面(Launcher)

2.系统真正的开机过程

  1. 按下电源键---引导芯片代码开始从预定义的地方(固化在ROM中)开始执行。加载引导程序Bootloader到RAM,然后执行,初始化硬件参数等功能。
  2. 硬件参数初始化完成后,进入到kernel层,kernel层主要加载一些硬件设备驱动,初始化进程管理等操作,再kernerl中首先启动swapper进程(pid=0),用于初始化进程管理,内存管理,加载Driver等操作,再启动kthread进程(pid=2),这些linux系统的内核进程,kthread是所有内核进程的鼻祖。
  3. Kernel加载完毕后,硬件设备驱动与hal层交互,初始化进程管理等操作会启动init进程,这些在native中,init进程pid=1,第一个启动,启动后,会启动adbd,logd等用户守护进程,并且会启动servicemanager(binder服务管家)等重要服务,
  4. 同时孵化出zygote进程,c++ framework,zygote进程是有init进程解析init.rc文件后fork生成,他会加载虚拟机,启动system server,(zygote 孵化的第一个进程)
  5. System server负责启动和管理整个java framework,包含AMS、PMS、WMS等服务
  6. zygote同时会启动app进程,启动的第一个进程是Launcher,然后启动email,sms等进程,所有的app都是zygote fork生成。

总结:BootLoader > Kernel > Native > Framework > Application

3.启动过程

3.1init进程的启动

system/core/init/main.cpp

main函数的执行顺序:

  • (1)ueventd_main: init进程创建子进程ueventd,创建设备节点文件的工作托付给ueventd,ueventd通过两种方式创建设备节点文件
  • (2)FirstStageMain 启动第一阶段 创建文件并挂载
  • (3)SetupSelinux 加载selinux规则,并设置selinux日志,完成SELinux相关工作
  • (4)SecondStageMain 启动第二阶段
  • 具体方法的调用均在system/core/init/路径下,这里不再贴代码。
int main(int argc, char** argv) {
#if __has_feature(address_sanitizer)
    __asan_set_error_report_callback(AsanReportCallback);
#endif
 
    if (!strcmp(basename(argv[0]), "ueventd")) {
        return ueventd_main(argc, argv); //  ueventd.cpp
    }
 
    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);  //subcontext.cpp
        }
 
        if (!strcmp(argv[1], "selinux_setup")) {
            return SetupSelinux(argv);  //selinux.cpp
        }
 
        if (!strcmp(argv[1], "second_stage")) {
            return SecondStageMain(argc, argv); //文件为 init.cpp
        }
    }
 
    return FirstStageMain(argc, argv);  //文件为first_stage_init.cpp
}

system\core\init\first_stage_init.cpp

int FirstStageMain(int argc, char** argv) {
    boot_clock::time_point start_time = boot_clock::now();
#define CHECKCALL(x) \
    if (x != 0) errors.emplace_back(#x " failed", errno);
    // Clear the umask.
    umask(0);
    //初始化系统环境变量
    CHECKCALL(clearenv());
    CHECKCALL(setenv("PATH", _PATH_DEFPATH, 1));
    // 挂载及创建基本的文件系统,并设置合适的访问权限
    CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"));
    CHECKCALL(mkdir("/dev/pts", 0755));
    CHECKCALL(mkdir("/dev/socket", 0755));
    CHECKCALL(mount("devpts", "/dev/pts", "devpts", 0, NULL));
#define MAKE_STR(x) __STRING(x)
    CHECKCALL(mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC)));
#undef MAKE_STR
    // 不要将原始命令行公开给非特权进程
    CHECKCALL(chmod("/proc/cmdline", 0440));
    gid_t groups[] = {AID_READPROC};
    CHECKCALL(setgroups(arraysize(groups), groups));
    CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL));
    CHECKCALL(mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL));
    CHECKCALL(mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11)));
    if constexpr (WORLD_WRITABLE_KMSG) {
        CHECKCALL(mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11)));
    }
    //创建linux随机伪设备文件
    CHECKCALL(mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8)));
    CHECKCALL(mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9)));
    //log wrapper所必须的,需要在ueventd运行之前被调用
    CHECKCALL(mknod("/dev/ptmx", S_IFCHR | 0666, makedev(5, 2)));
    CHECKCALL(mknod("/dev/null", S_IFCHR | 0666, makedev(1, 3)));
    ...
    //将内核的stdin/stdout/stderr 全都重定向/dev/null,关闭默认控制台输出
    SetStdioToDevNull(argv);
    // tmpfs已经挂载到/dev上,同时我们也挂载了/dev/kmsg,我们能够与外界开始沟通了
    //初始化内核log
    InitKernelLogging(argv);
    //检测上面的操作是否发生了错误
    if (!errors.empty()) {
        for (const auto& [error_string, error_errno] : errors) {
            LOG(ERROR) << error_string << " " << strerror(error_errno);
        }
        LOG(FATAL) << "Init encountered errors starting first stage, aborting";
    }
    LOG(INFO) << "init first stage started!";
    auto old_root_dir = std::unique_ptr<DIR, decltype(&closedir)>{opendir("/"), closedir};
    if (!old_root_dir) {
        PLOG(ERROR) << "Could not opendir("/"), not freeing ramdisk";
    }
    struct stat old_root_info;
    ...
    //挂载 system、cache、data 等系统分区
    if (!DoFirstStageMount()) {
        LOG(FATAL) << "Failed to mount required partitions early ...";
    }
    ...
    //进入下一步,SetupSelinux
    const char* path = "/system/bin/init";
    const char* args[] = {path, "selinux_setup", nullptr};
    execv(path, const_cast<char**>(args));
    return 1;
}

FirstStageMain到底做了哪些重要的事情:

  • 挂载及创建基本的文件系统,并设置合适的访问权限
  • 关闭默认控制台输出,并初始化内核级log。
  • 挂载 system、cache、data 等系统分区

system\core\init\selinux.cpp

int SetupSelinux(char** argv) {
    SetStdioToDevNull(argv);
    InitKernelLogging(argv);
 
    if (REBOOT_BOOTLOADER_ON_PANIC) {
        InstallRebootSignalHandlers();
    }
 
    boot_clock::time_point start_time = boot_clock::now();
 
    MountMissingSystemPartitions();
 
    SelinuxSetupKernelLogging();
 
    LOG(INFO) << "Opening SELinux policy";
 
    // Read the policy before potentially killing snapuserd.
    std::string policy;
    ReadPolicy(&policy);
 
    auto snapuserd_helper = SnapuserdSelinuxHelper::CreateIfNeeded();
    if (snapuserd_helper) {
        // Kill the old snapused to avoid audit messages. After this we cannot
        // read from /system (or other dynamic partitions) until we call
        // FinishTransition().
        snapuserd_helper->StartTransition();
    }
 
    LoadSelinuxPolicy(policy);
 
    if (snapuserd_helper) {
        // Before enforcing, finish the pending snapuserd transition.
        snapuserd_helper->FinishTransition();
        snapuserd_helper = nullptr;
    }
 
    SelinuxSetEnforcement();
 
    // We're in the kernel domain and want to transition to the init domain.  File systems that
    // store SELabels in their xattrs, such as ext4 do not need an explicit restorecon here,
    // but other file systems do.  In particular, this is needed for ramdisks such as the
    // recovery image for A/B devices.
    if (selinux_android_restorecon("/system/bin/init", 0) == -1) {
        PLOG(FATAL) << "restorecon failed of /system/bin/init failed";
    }
 
    setenv(kEnvSelinuxStartedAt, std::to_string(start_time.time_since_epoch().count()).c_str(), 1);
 
    const char* path = "/system/bin/init";
    const char* args[] = {path, "second_stage", nullptr};
    execv(path, const_cast<char**>(args));
 
    // execv() only returns if an error happened, in which case we
    // panic and never return from this function.
    PLOG(FATAL) << "execv(\"" << path << "\") failed";
 
    return 1;
}

SecondStageMain的关键代码为:

PropertyInit();对属性进行初始化
StartPropertyService(&property_fd);启动属性服务
LoadBootScripts(am, sm);解析init.rc
init.rc的文件为 system/core/rootdir/init.zygote64.rc

总结一下,第二阶段做了以下这些比较重要的事情:

  • 初始化属性的服务,并从指定文件读取属性
  • 初始化epoll,并注册signalfd和property_set_fd,建立和init的子进程以及部分服务的通讯桥梁
  • 初始化设备组合键,使系统能够对组合键信号做出响应
  • 解析init.rc文件,并按rc里的定义去启动服务
  • 开启死循环,用于接收epoll的事件

init.zygote64.rc的代码为:

其中service用于通知init进程创建名zygote的进程,这个zygote进程执行程序的路径为/system/bin/app_process64,后面的则是要传给app_process64的参数。

service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
    class main
    priority -20
    user root
    group root readproc reserved_disk
    socket zygote stream 660 root system
    socket usap_pool_primary stream 660 root system
    onrestart exec_background - system system -- /system/bin/vdc volume abort_fuse
    onrestart write /sys/power/state on
    onrestart restart audioserver
    onrestart restart cameraserver
    onrestart restart media
    onrestart restart netd
    onrestart restart wificond
    writepid /dev/cpuset/foreground/tasks

system\core\rootdir\init.rc 启动zygote进程

on nonencrypted
    class_start main  启动zygote进程
    class_start late_start

 

class_start是一个COMMAND,对应的函数为system\core\init\builtins.cpp下的do_class_start

static Result<void> do_class_start(const BuiltinArguments& args) {
    // Do not start a class if it has a property persist.dont_start_class.CLASS set to 1.
    if (android::base::GetBoolProperty("persist.init.dont_start_class." + args[1], false))
        return {};
    // Starting a class does not start services which are explicitly disabled.
    // They must  be started individually.
    for (const auto& service : ServiceList::GetInstance()) {
        if (service->classnames().count(args[1])) {
            if (auto result = service->StartIfNotDisabled(); !result.ok()) {
                LOG(ERROR) << "Could not start service '" << service->name()
                           << "' as part of class '" << args[1] << "': " << result.error();
            }
        }
    }
    return {};
}

StartIfNotDisabled()方法位于system\core\init\service.cpp下

Result<void> Service::StartIfNotDisabled() {

   if (!(flags_ & SVC_DISABLED)) {

       return Start();

   } else {

       flags_ |= SVC_DISABLED_START;

   }

   return {};

}

Start方法的关键代码为:

 if (flags_ & SVC_RUNNING) {//如果Service已经运行,则不启动
        if ((flags_ & SVC_ONESHOT) && disabled) {
            flags_ |= SVC_RESTART;
        }
        // It is not an error to try to start a service that is already running.
        reboot_on_failure.Disable();
        return {};
    }
 
 
//判断需要启动的Service的对应的执行文件是否存在,不存在则不启动该Service
struct stat sb;
    if (stat(args_[0].c_str(), &sb) == -1) {
        flags_ |= SVC_DISABLED;
        return ErrnoError() << "Cannot find '" << args_[0] << "'";
    }
 
 pid_t pid = -1;
    if (namespaces_.flags) {
        pid = clone(nullptr, nullptr, namespaces_.flags | SIGCHLD, nullptr);
    } else {
        pid = fork();/1.fork函数创建子进程
    }
 
    if (pid == 0) {//运行在子进程中
        umask(077);
}
 
 
ExpandArgsAndExecv(args_, sigstop_)  //通过execve执行system/bin/app_process

这样就会进入framework/base/cmds/app_process/app_main.cpp的main函数

 if (zygote) {
        runtime.start("com.android.internal.os.ZygoteInit", args, zygote); 启动zygote进程
    } else if (className) {
        runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
    } else {
        fprintf(stderr, "Error: no class name or --zygote supplied.\n");
        app_usage();
        LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
    }

属性的初始化

文件位于 system\core\init\property_service.cpp

void PropertyInit() {
    selinux_callback cb;
    cb.func_audit = PropertyAuditCallback;
    selinux_set_callback(SELINUX_CB_AUDIT, cb);
 
    mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH);
    CreateSerializedPropertyInfo();
    if (__system_property_area_init()) {
        LOG(FATAL) << "Failed to initialize property area";
    }
    if (!property_info_area.LoadDefaultPath()) {
        LOG(FATAL) << "Failed to load serialized property info file";
    }
 
    // If arguments are passed both on the command line and in DT,
    // properties set in DT always have priority over the command-line ones.
    ProcessKernelDt();
    ProcessKernelCmdline();
 
    // Propagate the kernel variables to internal variables
    // used by init as well as the current required properties.
    ExportKernelBootProps();
 
    PropertyLoadBootDefaults();
}

启动属性服务

void StartPropertyService(int* epoll_socket) {
    InitPropertySet("ro.property_service.version", "2");
 
    int sockets[2];
    if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sockets) != 0) {
        PLOG(FATAL) << "Failed to socketpair() between property_service and init";
    }
    *epoll_socket = from_init_socket = sockets[0];
    init_socket = sockets[1];
    StartSendingMessages();
 
    //创建非阻塞的socket
    if (auto result = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,false, 0666, 0, 0, {});
        result.ok()) {
        property_set_fd = *result;
    } else {
        LOG(FATAL) << "start_property_service socket creation failed: " << result.error();
    }
    //调用listen函数对property_set_fd进行监听,这样创建的socket就成为了server,也就是属性服务;listen函数的第二个参数设置8意味着属性服务最多可以同时为8个试图设置属性的用户提供服务
    listen(property_set_fd, 8);
 
    //创建PropertyServiceThread
    auto new_thread = std::thread{PropertyServiceThread};
    property_service_thread.swap(new_thread);
}

PropertyServiceThread关键代码为:

property_set_fd放入了epoll句柄中,用epoll来监听property_set_fd:当property_set_fd中有数据到来时,init进程将用handle_property_set_fd函数进行处理。

在linux新的内核中,epoll用来替换select,epoll最大的好处在于它不会随着监听fd数目的增长而降低效率。因为内核中的select实现是采用轮询来处理的,轮询的fd数目越多,自然耗时越多。

if (auto result = epoll.RegisterHandler(property_set_fd, handle_property_set_fd);
        !result.ok()) {
        LOG(FATAL) << result.error();
    }

handle_property_set_fd的关键代码为

 case PROP_MSG_SETPROP: {
        char prop_name[PROP_NAME_MAX];
        char prop_value[PROP_VALUE_MAX];
 
        if (!socket.RecvChars(prop_name, PROP_NAME_MAX, &timeout_ms) ||
            !socket.RecvChars(prop_value, PROP_VALUE_MAX, &timeout_ms)) {
          PLOG(ERROR) << "sys_prop(PROP_MSG_SETPROP): error while reading name/value from the socket";
          return;
        }
 
        prop_name[PROP_NAME_MAX-1] = 0;
        prop_value[PROP_VALUE_MAX-1] = 0;
 
        std::string source_context;
        if (!socket.GetSourceContext(&source_context)) {
            PLOG(ERROR) << "Unable to set property '" << prop_name << "': getpeercon() failed";
            return;
        }
 
        const auto& cr = socket.cred();
        std::string error;
//设置属性,会对权限进行判断
        uint32_t result =
                HandlePropertySet(prop_name, prop_value, source_context, cr, nullptr, &error);
        if (result != PROP_SUCCESS) {
            LOG(ERROR) << "Unable to set property '" << prop_name << "' from uid:" << cr.uid
                       << " gid:" << cr.gid << " pid:" << cr.pid << ": " << error;
        }
 
        break;
      }

HandlePropertySet 代码如下:

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.")) {
        return SendControlMessage(name.c_str() + 4, value, cr.pid, socket, error);
    }
 
    // sys.powerctl is a special property that is used to make the device reboot.  We want to log
    // any process that sets this property to be able to accurately blame the cause of a shutdown.
    if (name == "sys.powerctl") {
        std::string cmdline_path = StringPrintf("proc/%d/cmdline", cr.pid);
        std::string process_cmdline;
        std::string process_log_string;
        if (ReadFileToString(cmdline_path, &process_cmdline)) {
            // Since cmdline is null deliminated, .c_str() conveniently gives us just the process
            // path.
            process_log_string = StringPrintf(" (%s)", process_cmdline.c_str());
        }
        LOG(INFO) << "Received sys.powerctl='" << value << "' from pid: " << cr.pid
                  << process_log_string;
        if (value == "reboot,userspace" && !is_userspace_reboot_supported().value_or(false)) {
            *error = "Userspace reboot is not supported by this device";
            return PROP_ERROR_INVALID_VALUE;
        }
    }
 
    // If a process other than init is writing a non-empty value, it means that process is
    // requesting that init performs a restorecon operation on the path specified by 'value'.
    // We use a thread to do this restorecon operation to prevent holding up init, as it may take
    // a long time to complete.
    if (name == kRestoreconProperty && cr.pid != 1 && !value.empty()) {
        static AsyncRestorecon async_restorecon;
        async_restorecon.TriggerRestorecon(value);
        return PROP_SUCCESS;
    }
//设置属性
    return PropertySet(name, value, error);
}

PropertySet关键代码为:

static uint32_t PropertySet(const std::string& name, const std::string& value, std::string* error) {
    size_t valuelen = value.size();
 
    if (!IsLegalPropertyName(name)) {
        *error = "Illegal property name";
        return PROP_ERROR_INVALID_NAME;
    }
 
    if (auto result = IsLegalPropertyValue(name, value); !result.ok()) {
        *error = result.error().message();
        return PROP_ERROR_INVALID_VALUE;
    }
    //从属性存储空间查找该属性
    prop_info* pi = (prop_info*) __system_property_find(name.c_str());
    //如果属性存在
    if (pi != nullptr) {
        // ro.* properties are actually "write-once".
    //如果属性以"ro."开头,则表示是只读,不能修改,直接返回
        if (StartsWith(name, "ro.")) {
            *error = "Read-only property was already set";
            return PROP_ERROR_READ_ONLY_PROPERTY;
        }
    //更新属性值
        __system_property_update(pi, value.c_str(), valuelen);
    } else {
    //如果属性不存在则添加该属性
        int rc = __system_property_add(name.c_str(), name.size(), value.c_str(), valuelen);
        if (rc < 0) {
            *error = "__system_property_add failed";
            return PROP_ERROR_SET_FAILED;
        }
    }
 
    // Don't write properties to disk until after we have read all default
    // properties to prevent them from being overwritten by default values.
    if (persistent_properties_loaded && StartsWith(name, "persist.")) {
        WritePersistentProperty(name, value);
    }
    // If init hasn't started its main loop, then it won't be handling property changed messages
    // anyway, so there's no need to try to send them.
    auto lock = std::lock_guard{accept_messages_lock};
    if (accept_messages) {
        PropertyChanged(name, value);
    }
    return PROP_SUCCESS;
}

总结起来init进程主要做了三件事:

1.创建一些文件夹并挂载设备

2.初始化和启动属性服务

3.解析init.rc配置文件并启动zygote进程


目录
相关文章
|
4天前
|
Android开发
Android 如何将定制的Launcher成为系统中唯一的Launcher
Android 如何将定制的Launcher成为系统中唯一的Launcher
19 2
|
4天前
|
机器学习/深度学习 Java Shell
[RK3568][Android12.0]--- 系统自带预置第三方APK方法
[RK3568][Android12.0]--- 系统自带预置第三方APK方法
43 0
|
2天前
|
设计模式 算法 Android开发
2024年Android网络编程总结篇,androidview绘制流程面试
2024年Android网络编程总结篇,androidview绘制流程面试
2024年Android网络编程总结篇,androidview绘制流程面试
|
2天前
|
Android开发
Android获取当前系统日期和时间的三种方法
Android获取当前系统日期和时间的三种方法
16 4
|
3天前
|
存储 缓存 Android开发
Android系统分区与升级
Android系统分区与升级
21 4
|
3天前
|
安全 搜索推荐 物联网
构建未来:基于Android的智能物联网家居系统
【5月更文挑战第15天】 在快速发展的数字化时代,智能物联网(IoT)技术与移动操作系统的结合正在塑造未来家居的生活方式。特别是Android平台,以其开放性、灵活性和广泛的用户基础,成为智能家居创新的理想选择。本文将探讨如何利用Android系统构建一个高效、安全且易于扩展的智能家居控制系统,涵盖系统设计、关键技术实现以及可能面临的挑战。通过分析具体案例,我们旨在为开发者和企业提供一套可行的解决方案蓝图,以促进智能家居领域的进一步发展。
|
4天前
|
Java Android开发
Android OTG U盘无法显示在系统文件管理的修改
Android OTG U盘无法显示在系统文件管理的修改
11 0
|
4天前
|
安全 Android开发
修改Android系统的签名
修改Android系统的签名
21 0
|
4天前
|
安全 Android开发
Android 系统签名
Android 系统签名
18 0
|
4天前
|
Java Android开发
Android 切换壁纸代码流程追踪
Android 切换壁纸代码流程追踪
15 0