Android系统的启动流程

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 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进程


目录
相关文章
|
21天前
|
缓存 Java Shell
Android 系统缓存扫描与清理方法分析
Android 系统缓存从原理探索到实现。
46 15
Android 系统缓存扫描与清理方法分析
|
13天前
|
算法 JavaScript Android开发
|
15天前
|
安全 搜索推荐 Android开发
揭秘安卓与iOS系统的差异:技术深度对比
【10月更文挑战第27天】 本文深入探讨了安卓(Android)与iOS两大移动操作系统的技术特点和用户体验差异。通过对比两者的系统架构、应用生态、用户界面、安全性等方面,揭示了为何这两种系统能够在市场中各占一席之地,并为用户提供不同的选择。文章旨在为读者提供一个全面的视角,理解两种系统的优势与局限,从而更好地根据自己的需求做出选择。
39 2
|
24天前
|
Android开发
Android面试之Activity启动流程简述
Android面试之Activity启动流程简述
70 6
|
24天前
|
安全 搜索推荐 Android开发
深入探索安卓与iOS系统的差异及其对用户体验的影响
在当今的智能手机市场中,安卓和iOS是两大主流操作系统。它们各自拥有独特的特性和优势,为用户提供了不同的使用体验。本文将深入探讨安卓与iOS系统之间的主要差异,包括它们的设计理念、用户界面、应用生态以及安全性等方面,并分析这些差异如何影响用户的使用体验。
|
23天前
|
安全 搜索推荐 Android开发
揭秘iOS与Android系统的差异:一场技术与哲学的较量
在当今数字化时代,智能手机操作系统的选择成为了用户个性化表达和技术偏好的重要标志。iOS和Android,作为市场上两大主流操作系统,它们之间的竞争不仅仅是技术的比拼,更是设计理念、用户体验和生态系统构建的全面较量。本文将深入探讨iOS与Android在系统架构、应用生态、用户界面及安全性等方面的本质区别,揭示这两种系统背后的哲学思想和市场策略,帮助读者更全面地理解两者的优劣,从而做出更适合自己的选择。
|
23天前
|
XML 前端开发 Android开发
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
|
26天前
|
消息中间件 Android开发 索引
Android面试高频知识点(4) 详解Activity的启动流程
Android面试高频知识点(4) 详解Activity的启动流程
25 3
|
14天前
|
安全 搜索推荐 程序员
深入探索Android系统的碎片化问题及其解决方案
在移动操作系统的世界中,Android以其开放性和灵活性赢得了广泛的市场份额。然而,这种开放性也带来了一个众所周知的问题——系统碎片化。本文旨在探讨Android系统碎片化的现状、成因以及可能的解决方案,为开发者和用户提供一种全新的视角来理解这一现象。通过分析不同版本的Android系统分布、硬件多样性以及更新机制的影响,我们提出了一系列针对性的策略,旨在减少碎片化带来的影响,提升用户体验。
|
14天前
|
安全 Android开发 iOS开发
深入探索iOS与Android系统的差异性及优化策略
在当今数字化时代,移动操作系统的竞争尤为激烈,其中iOS和Android作为市场上的两大巨头,各自拥有庞大的用户基础和独特的技术特点。本文旨在通过对比分析iOS与Android的核心差异,探讨各自的优势与局限,并提出针对性的优化策略,以期为用户提供更优质的使用体验和为开发者提供有价值的参考。