首页 > 其他分享 >Android系统启动-2—Init篇-1

Android系统启动-2—Init篇-1

时间:2023-05-07 15:56:48浏览次数:43  
标签:系统启动 name service Init init fd 进程 Android property

基于 Android 6.0 的源码剖析, 分析Android启动过程进程号为1的init进程的工作内容。主要相关文件:

system/core/init/
  - init.cpp
  - init_parser.cpp
  - signal_handler.cpp

 

一、概述

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

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

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

1.1 main

[-> 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\n", strerror(errno));
        } else if (nr == 1) {
            ((void (*)()) ev.data.ptr)();
        }
    }
    return 0;
}

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

1.2 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


1.3 console_init_action

[-> init.cpp]

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 = "\n"
        "\n"
        "\n"
        "\n"
        "\n"
        "\n"
        "\n"  // console is 40 cols x 30 lines,这里要显示在中间
        "\n"
        "\n"
        "\n"
        "\n"
        "\n"
        "\n"
        "\n"
        " A N D R O I D ";
        write(fd, msg, strlen(msg));
        close(fd);
    }

    return 0;
}

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

1.4 restart_processes

[-> init.cpp]

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文件语法、启动服务、属性服务。


二、信号处理

在小节[1.1]的 init.cpp 的 main() 方法中通过 signal_handler_init() 来初始化信号处理过程。

主要工作:

(1) 初始化signal句柄;
(2) 循环处理子进程;
(3) 注册epoll句柄;
(4) 处理子进程的终止;

2.1 signal_handler_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\n", 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】
}


2.2 reap_any_outstanding_children

[-> signal_handler.cpp]

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

2.3 register_epoll_handler

[-> signal_handler.cpp]

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\n", strerror(errno));
    }
}

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


三、rc文件语法

rc文件语法是以行为单位,以空格间隔的语法,以#开始代表注释行。rc文件主要包含 Action、Service、Command、Options,其中对于 Action 和 Service 的名称都是唯一的,对于重复的命名视为无效。#######

3.1 Action

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

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


3.2 Service

服务Service,以 service 开头,由 init 进程启动,一般运行在 init 的一个子进程,所以启动 service 前需要判断对应的可执行文件是否存在。init 生成的子进程,定义在 rc 文件,其中每一个 service 在启动时会通过 fork 方式生成子进程。

例如:service servicemanager /system/bin/servicemanager 代表的是服务名为 servicemanager,服务执行文件的路径为 /system/bin/servicemanager。

3.3 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级别


3.4 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。

 

四、启动服务

4.1 启动顺序

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


4.2 服务启动(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 解析工作,此处就不详细展开讲解析过程,该过程主要工作是:

(1) 创建一个名叫 ”zygote” 的 service 结构体;
(2) 创建一个用于 socket 通信的 socketinfo 结构体;
(3) 创建一个包含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 重启在前面的信号处理过程中讲过,是处理 SIGCHLD 信号,init 进程重启 zygote 进程,更多关于 Zygote 内容见之后的 Zygote 篇。

4.3 服务重启

当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() 方法,获取的也是该共享内存区域的属性值。

5.1 property_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() 方法,创建用于跨进程的共享内存。主要工作如下:

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

关于加载的prop文件:

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

/system/build.prop;
/vendor/build.prop;
/factory/factory.prop;
/data/local.prop;
/data/property路径下的persist属性


5.2 start_property_service

[-> property_service.cpp]

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,接下来看看该函数的实现。

5.3 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【见小节5.4】
        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【见小节5.4】
        handle_property_set(socket, name, value, false);
        break;
      }

    default:
        socket.SendUint32(PROP_ERROR_INVALID_CMD);
        break;
    }
}

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

5.4 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;
}

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

(1) 属性名以 ctl. 开头,则表示是控制消息,控制消息用来执行一些命令。例如:
setprop ctl.start bootanim 查看开机动画;
setprop ctl.stop bootanim 关闭开机动画;
setprop ctl.start pre-recovey 进入 recovery 模式;

(2) 属性名以 ro. 开头,则表示是只读的,不能设置,所以直接返回;

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


六、总结

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

(1) 创建一块共享的内存空间,用于属性服务器;
(2) 解析各个rc文件,并启动相应属性服务进程;
(3) 初始化epoll,依次设置 signal、property、keychord 这3个fd可读时相对应的回调函数;
(4) 进入无限循环状态,执行如下流程:
    a. 检查 action_queue 列表是否为空,若不为空则执行相应的 action;
    b. 检查是否需要重启的进程,若有则将其重新启动;
    c. 进入 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() 来回收僵尸子进程。

 

参考:
http://gityuan.com/2016/02/05/android-init/

 

标签:系统启动,name,service,Init,init,fd,进程,Android,property
From: https://www.cnblogs.com/hellokitty2/p/17379430.html

相关文章

  • Android NDK 内存泄露检测
     Andorid中Java层代码内存泄露可以借助leakcanary进行检测;C++上的内存泄露检测库LeakTracer,于是再找了下,找到了Android上的移植版。首先建立一个项目,在根目录下建立thirdparty目录,进入该目录,clone相关库代码:[email protected]:lizhangqu/LeakTracer.git在项目src/main/cpp......
  • [docker]mysql的docker镜像中docker-entrypoint-initdb.d目录的妙用
    docker-entrypoint-initdb.d是Docker官方MySQL镜像中的一个目录,用于初始化数据库。在该目录下,可以放置一些SQL脚本文件,MySQL会在容器启动时自动执行这些脚本文件,用于创建用户、创建数据库、创建表等操作。具体来说,当MySQL镜像启动时,其entrypoint.sh脚本会检查是否存在......
  • ocidll forced to load library,initialization error could not initialize make sure
    是因为你使用pl/sql是64位的,但是软件只找到了32位的oracle客户端 多次尝试,我的pl/sql是64位,但是这里需要对应的使用64位,我的方案是下载https://www.allroundautomations.com/registered-plsqldev/ 这个版本的软件 这样配置,32位的使用,这里需要手动配置的,如果是64位的pl/......
  • Android设置语言
    应用语言的切换单纯的切换自身应用的语言。Resourcesresources=getResources();DisplayMetricsdm=resources.getDisplayMetrics();Configurationconfig=resources.getConfiguration();config.setLocale(locale);resources.updateConfiguration(config,dm);......
  • 一统天下 flutter - 存储: shared_preferences - 用于操作 android 的 SharedPreferen
    源码https://github.com/webabcd/flutter_demo作者webabcd一统天下flutter-存储:shared_preferences-用于操作android的SharedPreferences,ios的NSUserDefaults,web的LocalStorage示例如下:lib\storage\shared_preferences.dart/**shared_preferences......
  • AndroidStudio插件GsonFormat快速实现JavaBean
    安装方法一:1.AndroidstudioFile->Settings..->Plugins–>Browserepositores..搜索GsonFormat2.安装插件,重启androidstudio方法二:1.下载GsonFormat.jar;2.AndroidstudioFile->Settings..->Plugins–>installpluginfromdisk..导入下载GsonFormat.jar3.重启android......
  • Android开发:使用Glide动态加载圆形图片和圆角图片
    最新消息,鼎鼎大名的Yelp应用也转投Glide的阵营了,而且Glide在跟Listview的配合起来非常的顺畅,Glide除了配置简单,还可以本地缓存图片,也可以实现Listview图片的提前预加载,使得listview的更加的顺滑,具体可以查看Yelp的那篇博文。但是如果碰到要把加载下来的图片转成圆角或者圆形的图......
  • Android开发中的一个小功能 清空搜索框的文字
    需求:项目中的有关搜索的地方,加上清空文字的功能,目的是为了增加用户体验,使用户删除文本更加快捷解决过程:开始的时候感觉这个东西不太好实现,主要就是布局的问题,可能是开始顾虑的太多了,再加上当时产品催的不太紧,而且这个功能也不是必须实现的。但是今天不一样了,这个是老大让加上的,说别......
  • android 获取屏幕高度和宽度 的方法
    Android 获取屏幕高度和宽度的方法  我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸   下面的代码即可获取屏幕的尺寸。    在一个Activity的onCreate方法中,写入如下代码:[java] vi......
  • Android 布局设计新思路
    [YouTube](AndroidSpeechRecognition-Part1-YouTube---Android语音识别-第1部分-YouTube)......