zoukankan      html  css  js  c++  java
  • Android系统启动:2-Init篇

    Android系统启动:Init篇

    原文:http://gityuan.com/2016/02/05/android-init/

    概述

    init进程是Linux系统中用户空间的第一个进程,进程号固定为1。Kernel启动后,在用户空间启动init进程,并调用init中的main()方法执行init进程的职责。

    对于init进程的功能分为4部分:

    • 解析并运行所有的init.rc相关文件
    • 根据rc文件,生成相应的设备驱动节点
    • 处理子进程的终止(signal方式)
    • 提供属性服务的功能

    接下来从main()方法说起。

    main

    路径:system/core/init/init.cpp

    static int epoll_fd = -1;
    
    int main(int argc, char** argv) {
        //...
        //设置文件属性0777
        umask(0);
        //初始化内核log,位于节点/dev/kmsg【见小节1.2】
        klog_init();
        //设置输出的log级别
        klog_set_level(KLOG_NOTICE_LEVEL);
    
        //创建一块共享的内存空间,用于属性服务【见小节5.1】
        property_init();
        //初始化epoll功能
        epoll_fd = epoll_create1(EPOLL_CLOEXEC);
        //初始化子进程退出的信号处理函数,并调用epoll_ctl设置signal fd可读的回调函数【见小节2.1】
        signal_handler_init();  
    
        //加载default.prop文件
        property_load_boot_defaults();
        //启动属性服务器,此处会调用epoll_ctl设置property fd可读的回调函数【见小节5.2】
        start_property_service();   
        //解析init.rc文件
        init_parse_config_file("/init.rc");
    
        //执行rc文件中触发器为on early-init的语句
        action_for_each_trigger("early-init", action_add_queue_tail);
        //等冷插拔设备初始化完成
        queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");
        queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
        //设备组合键的初始化操作,此处会调用epoll_ctl设置keychord fd可读的回调函数
        queue_builtin_action(keychord_init_action, "keychord_init");
    
        // 屏幕上显示Android静态Logo 【见小节1.3】
        queue_builtin_action(console_init_action, "console_init");
    
        //执行rc文件中触发器为on init的语句
        action_for_each_trigger("init", action_add_queue_tail);
        queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
    
        char bootmode[PROP_VALUE_MAX];
        //当处于充电模式,则charger加入执行队列;否则late-init加入队列。
        if (property_get("ro.bootmode", bootmode) > 0 && strcmp(bootmode, "charger") == 0)
        {
           action_for_each_trigger("charger", action_add_queue_tail);
        } else {
           action_for_each_trigger("late-init", action_add_queue_tail);
        }
        //触发器为属性是否设置
        queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");
    
        while (true) {
            if (!waiting_for_exec) {
                execute_one_command();
                 //根据需要重启服务【见小节1.4】
                restart_processes();
            }
            int timeout = -1;
            if (process_needs_restart) {
                timeout = (process_needs_restart - gettime()) * 1000;
                if (timeout < 0)
                    timeout = 0;
            }
            if (!action_queue_empty() || cur_action) {
                timeout = 0;
            }
    
            epoll_event ev;
            //循环等待事件发生
            int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout));
            if (nr == -1) {
                ERROR("epoll_wait failed: %s
    ", strerror(errno));
            } else if (nr == 1) {
                ((void (*)()) ev.data.ptr)();
            }
        }
        return 0;
    }
    

    init进程执行完成后进入循环等待epoll_wait的状态。

    log系统

    此时android的log系统还没有启动,采用kernel的log系统,打开的设备节点/dev/kmsg, 那么可通过cat /dev/kmsg来获取内核log。

    接下来,设置log的输出级别为KLOG_NOTICE_LEVEL(5),当log级别小于5时则会输出到kernel log, 默认值为3

    #define KLOG_ERROR_LEVEL   3
    #define KLOG_WARNING_LEVEL 4
    #define KLOG_NOTICE_LEVEL  5
    #define KLOG_INFO_LEVEL    6
    #define KLOG_DEBUG_LEVEL   7
    #define KLOG_DEFAULT_LEVEL  3 //默认为3
    

    console_init_action

    static int console_init_action(int nargs, char **args)
    {
        char console[PROP_VALUE_MAX];
        if (property_get("ro.boot.console", console) > 0) {
            snprintf(console_name, sizeof(console_name), "/dev/%s", console);
        }
    
        int fd = open(console_name, O_RDWR | O_CLOEXEC);
        if (fd >= 0)
            have_console = 1;
        close(fd);
    
        fd = open("/dev/tty0", O_WRONLY | O_CLOEXEC);
        if (fd >= 0) {
            const char *msg;
                msg = "
    "
            "
    "
            "
    "
            "
    "
            "
    "
            "
    "
            "
    "  // console is 40 cols x 30 lines
            "
    "
            "
    "
            "
    "
            "
    "
            "
    "
            "
    "
            "
    "
            "             A N D R O I D ";
            write(fd, msg, strlen(msg));
            close(fd);
        }
    
        return 0;
    }
    

    这便是开机显示的底部带ANDROID字样的画面。

    restart_processes

    static void restart_processes()
    {
        process_needs_restart = 0;
        service_for_each_flags(SVC_RESTARTING,
                               restart_service_if_needed);
    }
    

    检查service_list中的所有服务,对于带有SVC_RESTARTING标志的服务,则都会调用其相应的restart_service_if_needed

    static void restart_service_if_needed(struct service *svc)
    {
        time_t next_start_time = svc->time_started + 5;
    
        if (next_start_time <= gettime()) {
            svc->flags &= (~SVC_RESTARTING);
            service_start(svc, NULL);
            return;
        }
    
        if ((next_start_time < process_needs_restart) ||
            (process_needs_restart == 0)) {
            process_needs_restart = next_start_time;
        }
    }
    

    之后再调用service_start来启动服务。

    接下来,解读init的main方法中的4大块核心知识点:信号处理、rc文件语法、启动服务以及属性服务。

    信号处理

    刚刚提到,init.cpp的main()方法中通过signal_handler_init()来初始化信号处理过程。

    主要工作:

    • 初始化signal句柄;
    • 循环处理子进程;
    • 注册epoll句柄;
    • 处理子进程的终止;

    signal_handler_init

    路径:system/core/init/signal_handler.cpp

    void signal_handler_init() {
        int s[2];
        // 创建socket pair
        if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == -1) {
            exit(1);
        }
        signal_write_fd = s[0];
        signal_read_fd = s[1];
        //当捕获信号SIGCHLD,则写入signal_write_fd
        struct sigaction act;
        memset(&act, 0, sizeof(act));
        act.sa_handler = SIGCHLD_handler;
        //SA_NOCLDSTOP使init进程只有在其子进程终止时才会受到SIGCHLD信号
        act.sa_flags = SA_NOCLDSTOP;
        sigaction(SIGCHLD, &act, 0);
    
        //进入waitpid来处理子进程是否退出的情况【见小节2.2】
        reap_any_outstanding_children();
        //调用epoll_ctl方法来注册epoll的回调函数【见小节2.3】
        register_epoll_handler(signal_read_fd, handle_signal);
    }
    

    每个进程在处理其他进程发送的signal信号时都需要先注册,当进程的运行状态改变或终止时会产生某种signal信号,init进程是所有用户空间进程的父进程,当其子进程终止时产生SIGCHLD信号,init进程调用信号安装函数sigaction(),传递参数给sigaction结构体,便完成信号处理的过程。

    这里有两个重要的函数:SIGCHLD_handler和handle_signal,如下:

    //写入数据
    static void SIGCHLD_handler(int) {
        //向signal_write_fd写入1,直到成功为止
        if (TEMP_FAILURE_RETRY(write(signal_write_fd, "1", 1)) == -1) {
            ERROR("write(signal_write_fd) failed: %s
    ", strerror(errno));
        }
    }
    
    //读取数据
    static void handle_signal() {
        char buf[32];
        //读取signal_read_fd中的数据,并放入buf
        read(signal_read_fd, buf, sizeof(buf));
        reap_any_outstanding_children(); 【见小节2.2】
    }
    

    reap_any_outstanding_children

    static void reap_any_outstanding_children() {
        while (wait_for_one_process()) { }
    }
    
    static bool wait_for_one_process() {
        int status;
        //等待任意子进程,如果子进程没有退出则返回0,否则则返回该子进程pid。
        pid_t pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, WNOHANG));
        if (pid == 0) {
            return false;
        } else if (pid == -1) {
            return false;
        }
        //根据pid查找到相应的service
        service* svc = service_find_by_pid(pid);
        std::string name;
    
        if (!svc) {
            return true;
        }
    
        //当flags为RESTART,且不是ONESHOT时,先kill进程组内所有的子进程或子线程
        if (!(svc->flags & SVC_ONESHOT) || (svc->flags & SVC_RESTART)) {
            kill(-pid, SIGKILL);
        }
    
        //移除当前服务svc中的所有创建过的socket
        for (socketinfo* si = svc->sockets; si; si = si->next) {
            char tmp[128];
            snprintf(tmp, sizeof(tmp), ANDROID_SOCKET_DIR"/%s", si->name);
            unlink(tmp);
        }
    
        //当flags为EXEC时,释放相应的服务
        if (svc->flags & SVC_EXEC) {
            waiting_for_exec = false;
            list_remove(&svc->slist);
            free(svc->name);
            free(svc);
            return true;
        }
        svc->pid = 0;
        svc->flags &= (~SVC_RUNNING);
    
        //对于ONESHOT服务,使其进入disabled状态
        if ((svc->flags & SVC_ONESHOT) && !(svc->flags & SVC_RESTART)) {
            svc->flags |= SVC_DISABLED;
        }
        //禁用和重置的服务,都不再自动重启
        if (svc->flags & (SVC_DISABLED | SVC_RESET))  {
            svc->NotifyStateChange("stopped"); //设置相应的service状态为stopped
            return true;
        }
    
        //服务在4分钟内重启次数超过4次,则重启手机进入recovery模式
        time_t now = gettime();
        if ((svc->flags & SVC_CRITICAL) && !(svc->flags & SVC_RESTART)) {
            if (svc->time_crashed + CRITICAL_CRASH_WINDOW >= now) {
                if (++svc->nr_crashed > CRITICAL_CRASH_THRESHOLD) {
                    android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
                    return true;
                }
            } else {
                svc->time_crashed = now;
                svc->nr_crashed = 1;
            }
        }
        svc->flags &= (~SVC_RESTART);
        svc->flags |= SVC_RESTARTING;
    
        //执行当前service中所有onrestart命令
        struct listnode* node;
        list_for_each(node, &svc->onrestart.commands) {
            command* cmd = node_to_item(node, struct command, clist);
            cmd->func(cmd->nargs, cmd->args);
        }
        //设置相应的service状态为restarting
        svc->NotifyStateChange("restarting");
        return true;
    }
    

    另外:通过getprop | grep init.svc 可查看所有的service运行状态。状态总共分为:running, stopped, restarting

    register_epoll_handler

    void register_epoll_handler(int fd, void (*fn)()) {
        epoll_event ev;
        ev.events = EPOLLIN;
        ev.data.ptr = reinterpret_cast<void*>(fn);
        //将fd的可读事件加入到epoll_fd的监听队列中
        if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {
            ERROR("epoll_ctl failed: %s
    ", strerror(errno));
        }
    }
    

    当fd可读,则会触发调用(*fn)函数。

    rc文件语法

    rc文件语法是以行尾单位,以空格间隔的语法,以#开始代表注释行。

    rc文件主要包含Action、Service、Command、Options,其中对于Action和Service的名称都是唯一的,对于重复的命名视为无效。

    Action

    Action: 通过触发器trigger,即以on开头的语句来决定执行相应的service的时机,具体有如下时机:

    • on early-init:在初始化早期阶段触发;
    • on init:在初始化阶段触发;
    • on late-init:在初始化晚期阶段触发;
    • on boot/charger:`当系统启动/充电时触发,还包含其他情况,此处不一一列举;
    • on property:<key>=<value>: 当属性值满足条件时触发;

    Service

    服务Service,以 service开头,由init进程启动,一般运行在init的一个子进程,所以启动service前需要判断对应的可执行文件是否存在。

    init生成的子进程,定义在rc文件,其中每一个service在启动时会通过fork方式生成子进程。

    例如: service servicemanager /system/bin/servicemanager代表的是

    • 服务名为servicemanager
    • 执行的路径为/system/bin/servicemanager

    Command

    下面列举常用的命令

    • class_start <service_class_name>: 启动属于同一个class的所有服务;
    • start <service_name>: 启动指定的服务,若已启动则跳过;
    • stop <service_name>: 停止正在运行的服务
    • setprop <name> <value>:设置属性值
    • mkdir <path>:创建指定目录
    • symlink <target> <sym_link>: 创建连接到<target><sym_link>符号链接;
    • write <path> <string>: 向文件path中写入字符串;
    • exec: fork并执行,会阻塞init进程直到程序完毕;
    • exprot <name> <name>:设定环境变量;
    • loglevel <level>:设置log级别

    Options

    Options是Service的可选项,与service配合使用

    • disabled: 不随class自动启动,只有根据service名才启动;
    • oneshot: service退出后不再重启;
    • user/group: 设置执行服务的用户/用户组,默认都是root;
    • class:设置所属的类名,当所属类启动/退出时,服务也启动/停止,默认为default;
    • onrestart:当服务重启时执行相应命令;
    • socket: 创建名为/dev/socket/<name>的socket
    • critical: 在规定时间内该service不断重启,则系统会重启并进入恢复模式

    default: 意味着disabled=false,oneshot=false,critical=false。

    启动服务

    启动顺序

    on early-init
    on init
    on late-init
        trigger post-fs      
        trigger load_system_props_action
        trigger post-fs-data  
        trigger load_persist_props_action
        trigger firmware_mounts_complete
        trigger boot   
    
    on post-fs      //挂载文件系统
        start logd
        mount rootfs rootfs / ro remount
        mount rootfs rootfs / shared rec
        mount none /mnt/runtime/default /storage slave bind rec
        //...
    
    on post-fs-data  //挂载data
        start logd
        start vold   //启动vold
        //...
    
    on boot      //启动核心服务
        //...
        class_start core //启动core class
    

    触发器的执行顺序为on early-init -> init -> late-init

    从上面的代码可知,在late-init触发器中会触发文件系统挂载以及on boot。再on boot过程会触发启动core class。

    至于main class的启动是由vold.decrypt的以下4个值的设置所决定的, 该过程位于system/vold/cryptfs.c文件。

    on nonencrypted
        class_start main
        class_start late_start
    
    on property:vold.decrypt=trigger_restart_min_framework
        class_start main
    
    on property:vold.decrypt=trigger_restart_framework
        class_start main
        class_start late_start
    
    on property:vold.decrypt=trigger_reset_main
        class_reset main
    
    on property:vold.decrypt=trigger_shutdown_framework
        class_reset late_start
        class_reset main
    

    服务启动(Zygote)

    在init.zygote.rc文件中,zygote服务定义如下:

    service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
        class main
        socket zygote stream 660 root system
        onrestart write /sys/android_power/request_state wake
        onrestart write /sys/power/state on
        onrestart restart media
        onrestart restart netd
    

    通过init_parser.cpp完成整个service解析工作,此处就不详细展开讲解析过程,该过程主要工作是:

    • 创建一个名叫”zygote”的service结构体;
    • 创建一个用于socket通信的socketinfo结构体;
    • 创建一个包含4个onrestart的action结构体。

    Zygote服务会随着main class的启动而启动,退出后会由init重启zygote,即使多次重启也不会进入recovery模式。

    zygote所对应的可执行文件是/system/bin/app_process,通过调用pid =fork()创建子进程,通过execve(svc->args[0], (char**)svc->args, (char**) ENV),进入App_main.cpp的main()函数。

    故zygote是通过fork和execv共同创建的。

    流程如下:

    zygote_init

    而关于Zygote重启在前面的信号处理过程中讲过,是处理SIGCHLD信号,init进程重启zygote进程,更多关于Zygote内容见Zygote篇

    服务重启

    init_oneshot

    当init子进程退出时,会产生SIGCHLD信号,并发送给init进程,通过socket套接字传递数据,调用到wait_for_one_process()方法,根据是否是oneshot,来决定是重启子进程,还是放弃启动。

    所有的Service里面只有servicemanager ,zygote ,surfaceflinger这3个服务有onrestart关键字来触发其他service启动过程。

    //zygote可触发media、netd重启
    service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
        class main
        socket zygote stream 660 root system
        onrestart write /sys/android_power/request_state wake
        onrestart write /sys/power/state on
        onrestart restart media
        onrestart restart netd
    
    //servicemanager可触发healthd、zygote、media、surfaceflinger、drm重启
    service servicemanager /system/bin/servicemanager
        class core
        user system
        group system
        critical
        onrestart restart healthd
        onrestart restart zygote
        onrestart restart media
        onrestart restart surfaceflinger
        onrestart restart drm
    
    //surfaceflinger可触发zygote重启
    service surfaceflinger /system/bin/surfaceflinger
        class core
        user system
        group graphics drmrpc
        onrestart restart zygote
    

    由上可知:

    • zygote:触发media、netd以及子进程(包括system_server进程)重启;
    • system_server: 触发zygote重启;
    • surfaceflinger:触发zygote重启;
    • servicemanager: 触发zygote、healthd、media、surfaceflinger、drm重启

    所以,surfaceflinger,servicemanager,zygote自身以及system_server进程被杀都会触发Zygote重启。

    属性服务

    当某个进程A,通过property_set()修改属性值后,init进程会检查访问权限,当权限满足要求后,则更改相应的属性值。

    属性值一旦改变则会触发相应的触发器(即rc文件中的on开头的语句),在Android Shared Memmory(共享内存区域)中有一个_system_property_area_区域,里面记录着所有的属性值。

    对于进程A通过property_get()方法,获取的也是该共享内存区域的属性值。

    property_init

    路径:system//core/init/property_service.cpp

    void property_init() {
        //用于保证只初始化_system_property_area_区域一次
        if (property_area_initialized) {
            return;
        }
        property_area_initialized = true;
        //创建共享内存
        if (__system_property_area_init()) {
            return;
        }
        pa_workspace.size = 0;
        pa_workspace.fd = open(PROP_FILENAME, O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
    }
    

    该方法核心功能在执行__system_property_area_init()方法,创建用于跨进程的共享内存。主要工作如下:

    • 执行open(),打开名为”/dev/properties“的共享内存文件,并设置大小为128KB;
    • 执行mmap(),将该内存映射到init进程;
    • 将该内存的首地址保存在全局变量__system_property_area__,后续的增加或者修改属性都基于该变量来计算位置。

    关于加载的prop文件

    通过load_all_load_all_propsprops()方法,加载以下:

    1. /system/build.prop;
    2. /vendor/build.prop;
    3. /factory/factory.prop;
    4. /data/local.prop;
    5. /data/property路径下的persist属性

    start_property_service

    void start_property_service() {
        property_set("ro.property_service.version", "2");
    
        property_set_fd = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
                                       false, 0666, 0, 0, nullptr, sehandle);
        listen(property_set_fd, 8);
        //设置property文件描述符可读的回调函数【见小节2.3】
        register_epoll_handler(property_set_fd, handle_property_set_fd);
    }
    

    创建并监听名叫“property_service”的socket,再利用epoll_ctl设置property文件描述符触发可读时的回调函数为handle_property_set_fd,接下来看看该函数的实现。

    handle_property_set_fd

    static void handle_property_set_fd() {
        static constexpr uint32_t kDefaultSocketTimeout = 2000; /* ms */
    
        int s = accept4(property_set_fd, nullptr, nullptr, SOCK_CLOEXEC);
    
        struct ucred cr;
        socklen_t cr_size = sizeof(cr);
        getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0);
    
        SocketConnection socket(s, cr);
        uint32_t timeout_ms = kDefaultSocketTimeout; //设置2秒超时
    
        uint32_t cmd = 0;
        if (!socket.RecvUint32(&cmd, &timeout_ms)) {
            socket.SendUint32(PROP_ERROR_READ_CMD);
            return;
        }
    
        switch (cmd) {
        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)) {
              return;
            }
    
            prop_name[PROP_NAME_MAX-1] = 0;
            prop_value[PROP_VALUE_MAX-1] = 0;
            //设置property
            handle_property_set(socket, prop_value, prop_value, true);
            break;
          }
    
        case PROP_MSG_SETPROP2: {
            std::string name;
            std::string value;
            if (!socket.RecvString(&name, &timeout_ms) ||
                !socket.RecvString(&value, &timeout_ms)) {
              socket.SendUint32(PROP_ERROR_READ_DATA);
              return;
            }
            //设置property
            handle_property_set(socket, name, value, false);
            break;
          }
    
        default:
            socket.SendUint32(PROP_ERROR_INVALID_CMD);
            break;
        }
    }
    

    这里针对socket接收事件设置2秒超时,也就是说property的设置过程有可能耗时。

    handle_property_set

    static void handle_property_set(SocketConnection& socket,
                                    const std::string& name,
                                    const std::string& value,
                                    bool legacy_protocol) {
      const char* cmd_name = legacy_protocol ? "PROP_MSG_SETPROP" : "PROP_MSG_SETPROP2";
      if (!is_legal_property_name(name)) { //检查属性名是否合规
        socket.SendUint32(PROP_ERROR_INVALID_NAME);
        return;
      }
    
      struct ucred cr = socket.cred();
      char* source_ctx = nullptr;
      getpeercon(socket.socket(), &source_ctx);
    
      if (android::base::StartsWith(name, "ctl.")) {
        if (check_control_mac_perms(value.c_str(), source_ctx, &cr)) {
          //处理以ctl.开头的属性
          handle_control_message(name.c_str() + 4, value.c_str());
        }
        //...
      } else {
        if (check_perms(name, source_ctx, &cr)) {
          //设置属性名和属性值
          uint32_t result = property_set(name, value);
        }
        //...
      }
      freecon(source_ctx);
    }
    

    这里会检测属性名是否合规,具体检查规范如下:

    bool is_legal_property_name(const std::string& name) {
        size_t namelen = name.size();
    
        if (namelen < 1) return false;
        if (name[0] == '.') return false;
        if (name[namelen - 1] == '.') return false;
    
        /* Only allow alphanumeric, plus '.', '-', '@', ':', or '_' */
        /* Don't allow ".." to appear in a property name */
        for (size_t i = 0; i < namelen; i++) {
            if (name[i] == '.') {
                // i=0 is guaranteed to never have a dot. See above.
                if (name[i-1] == '.') return false;
                continue;
            }
            if (name[i] == '_' || name[i] == '-' || name[i] == '@' || name[i] == ':') continue;
            if (name[i] >= 'a' && name[i] <= 'z') continue;
            if (name[i] >= 'A' && name[i] <= 'Z') continue;
            if (name[i] >= '0' && name[i] <= '9') continue;
            return false;
        }
    
        return true;
    }
    uint32_t property_set(const std::string& name, const std::string& value) {
        return PropertySetImpl(name, value);
    }
    
    static uint32_t PropertySetImpl(const std::string& name, const std::string& value) {
        size_t valuelen = value.size();
    
        if (!is_legal_property_name(name)) {
            return PROP_ERROR_INVALID_NAME;
        }
    
        if (valuelen >= PROP_VALUE_MAX) { //属性名不可过长
            return PROP_ERROR_INVALID_VALUE;
        }
    
        prop_info* pi = (prop_info*) __system_property_find(name.c_str());
        if (pi != nullptr) {
            // 以ro.开头的属性不可更改
            if (android::base::StartsWith(name, "ro.")
                && strcmp(name.c_str(),"ro.build.software.version")) {
                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);
        }
    
        //以persist.开头的属性需要持久化
        if (persistent_properties_loaded && android::base::StartsWith(name, "persist.")) {
            write_persistent_property(name.c_str(), value.c_str());
        }
        //属性值改变的通知过程
        property_changed(name, value);
        return PROP_SUCCESS;
    }
    

    不同属性执行逻辑有所不同,主要区分如下:

    • 属性名以ctl.开头,则表示是控制消息,控制消息用来执行一些命令。例如:

      • setprop ctl.start bootanim 查看开机动画;
      • setprop ctl.stop bootanim 关闭开机动画;
      • setprop ctl.start pre-recovey 进入recovery模式;
    • 属性名以ro.开头,则表示是只读的,不能设置,所以直接返回;

    • 属性名以persist.开头,则需要把这些值写到对应文件;需要注意的是,persist用于持久化保存某些属性值,当同时也带来了额外的IO操作。

    总结

    init进程(pid=1)是Linux系统中用户空间的第一个进程,主要工作如下:

    • 创建一块共享的内存空间,用于属性服务器;
    • 解析各个rc文件,并启动相应属性服务进程;
    • 初始化epoll,依次设置signal、property、keychord这3个fd可读时相对应的回调函数;
    • 进入无限循环状态,执行如下流程:
      • 检查action_queue列表是否为空,若不为空则执行相应的action;
      • 检查是否需要重启的进程,若有则将其重新启动;
      • 进入epoll_wait等待状态,直到系统属性变化事件(property_set改变属性值),或者收到子进程的信号SIGCHLD,再或者keychord 键盘输入事件,则会退出等待状态,执行相应的回调函数。

    可见init进程在开机之后的核心工作就是响应property变化事件和回收僵尸进程。

    当某个进程调用property_set来改变一个系统属性值时,系统会通过socket向init进程发送一个property变化的事件通知,那么property fd会变成可读,init进程采用epoll机制监听该fd则会 触发回调handle_property_set_fd()方法。

    回收僵尸进程,在Linux内核中,如父进程不等待子进程的结束直接退出,会导致子进程在结束后变成僵尸进程,占用系统资源。

    为此,init进程专门安装了SIGCHLD信号接收器,当某些子进程退出时发现其父进程已经退出,则会向init进程发送SIGCHLD信号,init进程调用回调方法handle_signal()来回收僵尸子进程。

    如果说我的文章对你有用,只不过是我站在巨人的肩膀上再继续努力罢了。
    若在页首无特别声明,本篇文章由 Schips 经过整理后发布。
    博客地址:https://www.cnblogs.com/schips/
  • 相关阅读:
    Linnia学习记录
    漫漫考研路
    ENS的学习记录
    KnockoutJS 3.X API 第四章 数据绑定(4) 控制流with绑定
    KnockoutJS 3.X API 第四章 数据绑定(3) 控制流if绑定和ifnot绑定
    KnockoutJS 3.X API 第四章 数据绑定(2) 控制流foreach绑定
    KnockoutJS 3.X API 第四章 数据绑定(1) 文本及样式绑定
    KnockoutJS 3.X API 第三章 计算监控属性(5) 参考手册
    KnockoutJS 3.X API 第三章 计算监控属性(4)Pure computed observables
    KnockoutJS 3.X API 第三章 计算监控属性(3) KO如何实现依赖追踪
  • 原文地址:https://www.cnblogs.com/schips/p/android-2-init.html
Copyright © 2011-2022 走看看