Framework篇 - Init 进程分析

本文将分析 pid = 1 的 Init 进程。Init 是 Linux 系统中用户空间的第一个进程,进程号为1。Kernel 启动后,在用户空间,启动 Init 进程,并调用 Init 中的 main() 方法执行 Init 进程的职责。Init 进程从功能上来说,分成下面四个部分:

  • 分析和运行所有的 init.rc 文件。
  • 生成设备驱动节点 (通过 rc 文件创建)。
  • 处理子进程的终止 (signal 方式)。
  • 提供属性服务。

 

目录:

  1. main() 分析
  2. 信号处理
  3. rc 文件语法
  4. 服务启动
  5. 属性服务

 

1. main() 分析

/system/core/init/init.cpp

int main(int argc, char** argv) {
    ...
    umask(0); //设置文件属性0777
    
    klog_init();  //初始化kernel log,位于设备节点/dev/kmsg【见小节1.2】
    klog_set_level(KLOG_NOTICE_LEVEL); //设置输出的log级别
    // 输出init启动阶段的log
    NOTICE("init%s started!\n", is_first_stage ? "" : " second stage");
    
    property_init(); //创建一块共享的内存空间,用于属性服务
    signal_handler_init();  //初始化子进程退出的信号处理过程【见小节2.1】

    property_load_boot_defaults(); //加载default.prop文件
    start_property_service();   //启动属性服务器(通过socket通信)【5.1】
    
    init_parse_config_file("/init.rc"); //解析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");
    
    //设备组合键的初始化操作
    queue_builtin_action(keychord_init_action, "keychord_init");
    
    // 屏幕上显示Android静态Logo
    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();
            restart_processes(); //【见小节1.4】
        }
        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.cpp 的 main() 做的工作包括:

  • 启动 log 系统。
  • 开辟共享内存空间,用于属性服务。
  • 初始化信号处理相关服务。
  • 解析 init.rc。
  • 启动循环,监听事件。

 

 

2. 信号处理

在 init.cpp 的 main() 方法中,通过 signal_handler_init() 来初始化信号处理过程。主要工作:

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

/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);
    reap_any_outstanding_children(); 【见小节2.2】
    register_epoll_handler(signal_read_fd, handle_signal); 
}

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

 

 

3. 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 是 Services 的可选项,与 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. 服务启动

 

  • 4.1 启动顺序
on early-init
on init
on late-init //挂载文件系统,启动核心服务
    trigger post-fs
    trigger load_system_props_action 
    trigger post-fs-data //挂载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
    start logd
    start vold //启动vold
    ...
    
on boot
    ...
    class_start core //启动core class
由on early-init -> init -> late-init -> 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 解析工作,此处就不详细展开讲解析过程,该过程主要是创建一个名 "zygote" 的 service结构体,一个 socketinfo 结构体 (用于 socket 通信),以及一个包含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 共同创建的。

流程如下:


 

  • 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 重启。

 

 

5. 属性服务

当某个进程 A,通过 property_set() 修改属性值后,init 进程会检查访问权限,当权限满足要求后,则更改相应的属性值,属性值一旦改变则会触发相应的触发器 (即 rc 文件中的 on 开头的语句),在 Android Shared Memmory (共享内存区域) 中有一个_system_property_area_ 区域,里面记录着所有的属性值。对于进程 A 通过 property_get() 方法,获取的也是该共享内存区域的属性值。

 

  • 5.1 初始化

/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);
    if (pa_workspace.fd == -1) {
        ERROR("Failed to open %s: %s\n", PROP_FILENAME, strerror(errno));
        return;
    }
}


在 properyty_init 函数中,先调用 init_property_area 函数,创建一块用于存储属性的共享内存,而共享内存是可以跨进程的。

 

 

  • 5.2 属性类别

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

  • setprop ctl.start bootanim 查看开机动画。
  • setprop ctl.stop bootanim 关闭开机动画。
  • setprop ctl.start pre-recovery 进入 recovery 模式。

属性名以 ro. 开头,则表示是只读的,不能设置,所以直接返回。
属性名以 persist. 开头,则需要把这些值写到对应文件中去。

比如上一篇文章的开机动画:

/**
 * 启动开机动画
 *
 * 通过控制ctl.start属性,设置成bootanim值,则触发init进程来创建开机动画进程bootanim,
 * 到此,则开始显示开机过程的动画。 
 */
void SurfaceFlinger::startBootAnim() {
    // start boot animation
    property_set("service.bootanim.exit", "0");
    property_set("ctl.start", "bootanim");
}

 

 

 

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值