• ANDROID init进程


    init简要

    init是Android上启动的第一个用户态进程。

    执行序列是:

    start_kernel() -> rest_init() -> kernel_init() -> init_post() -> run_init_process()

    if (ramdisk_execute_command) {    //rdinit, default "/init"
        run_init_process(ramdisk_execute_command);
        printk(KERN_WARNING "Failed to execute %s
    ", ramdisk_execute_command);
    }
    

    ramdisk_execute_command是内核启动参数rdinit指定的值,默认为“/init”。

    static void run_init_process(const char *init_filename)
    {
        argv_init[0] = init_filename;
        kernel_execve(init_filename, argv_init, envp_init);
    }
    

    至此,内核初始化完毕,用户init进程开始启动。

    init功能

    Android的init进程源码位于aosp的system/core/init目录,主要完成了4项工作:

    1. 解析处理init.rc文件

    2. 生成设备驱动节点

    3. 处理子进程终止

    4. 属性读取、设置服务

    1)处理子进程终止

    init子进程终止处理的初始化在signal_init函数完成。

    /* signal_handler.c */
    void signal_init(void)
    {
        int s[2];
    
        struct sigaction act;
        memset(&act, 0, sizeof(act));
        act.sa_handler = sigchld_handler;
        act.sa_flags = SA_NOCLDSTOP;
        sigaction(SIGCHLD, &act, 0);    //@1
    
        /* create a signalling mechanism for the sigchld handler */
        if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) == 0) { @2
            signal_fd = s[0]; @3
            signal_recv_fd = s[1];
            fcntl(s[0], F_SETFD, FD_CLOEXEC);
            fcntl(s[0], F_SETFL, O_NONBLOCK);
            fcntl(s[1], F_SETFD, FD_CLOEXEC);
            fcntl(s[1], F_SETFL, O_NONBLOCK);
        }
    
        handle_signal();
    }
    

    首先@1注册了SIGCHLD信号处理函数sigchld_handler,用于设置全局signal_fd。

    /* signal_handler.c */
    static void sigchld_handler(int s) //s是SIGCHLD信号编号
    {
        write(signal_fd, &s, 1);
    }
    

    接着@2创建UNIX域套接字,关联signal_fd、signal_recv_fd。

    当子进程终止,SIGCHLD信号编号被写入socketpair的signal_fd @3,信号编号将传递到接收端的socket_recv_fd套接字。socket_recv_fd已经注册到POLL,因此SIGCHLD信号将触发其POLLIN事件,poll事件监听返回,执行wait_for_one_proces。

    /* init.c */
    /* init的事件循环 */
    for(;;) {
        nr = poll(ufds, fd_count, timeout); //接收SIGCHLD信号后,监听返回
        if (nr <= 0)
            continue;
    
        for (i = 0; i < fd_count; i++) {
            if (ufds[i].revents == POLLIN) {
                if (ufds[i].fd == get_property_set_fd())
                    handle_property_set_fd();
                else if (ufds[i].fd == get_keychord_fd())
                    handle_keychord();
                else if (ufds[i].fd == get_signal_fd()) //socket_recv_fd事件
                    handle_signal();
            }
        }
    }
    

    在wait_for_one_process函数中,将完成进程资源回收或进程重启工作。

    void handle_signal(void)
    {
        char tmp[32];
    
        /* we got a SIGCHLD - reap and restart as needed */
        read(signal_recv_fd, tmp, sizeof(tmp));
        while (!wait_for_one_process(0))
            ;
    }
    

    2)生成设备驱动节点

    init会创建并挂载系统启动所需的文件目录,编译Android系统源码时,并不存在/dev、/proc、/sys等目录,它们是系统运行时由init进行生成的。在Android上,init扮演着Linux系统上udev守护进程的角色。

    /* init.c */
        mkdir("/dev", 0755);
        mkdir("/proc", 0755);
        mkdir("/sys", 0755);
    
        mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
        mkdir("/dev/pts", 0755);
        mkdir("/dev/socket", 0755);
        mount("devpts", "/dev/pts", "devpts", 0, NULL);
        mount("proc", "/proc", "proc", 0, NULL);
        mount("sysfs", "/sys", "sysfs", 0, NULL);
    

    3)init.rc解析

    init会解析两个配置文件,分别是init.rc和init.{hardware}.rc,两者语法是一样的,init.{hardware}.rc中是与硬件相关的一些配置信息。

    init.rc文件大致分为两大部分,一部分是以on关键字开头的动作列表(Action List),一部分是以service关键字开头的服务列表(Service List)。

    动作列表主要进行一些属性,环境配置,示例:


    on init sysclktz 0 loglevel 3 # Backward compatibility symlink /system/etc /etc symlink /sys/kernel/debug /d # Right now vendor lives on the same filesystem as system, # but someday that may change. symlink /system/vendor /vendor # Create cgroup mount point for cpu accounting mkdir /acct mount cgroup none /acct cpuacct mkdir /acct/uid on post-fs # once everything is setup, no need to modify / mount rootfs rootfs / ro remount # mount shared so changes propagate into child namespaces mount rootfs rootfs / shared rec on boot # basic network init ifup lo hostname localhost domainname localdomain # set RLIMIT_NICE to allow priorities from 19 to -20 setrlimit 13 40 40

    服务列表用来记录init进程启动的进程,包括运行一次的程序,Daemon进程等,示例:

    ## Daemon processes to be run by init.
    ##
    service ueventd /sbin/ueventd
        class core
        critical
        seclabel u:r:ueventd:s0
    
    service healthd /sbin/healthd
        class core
        critical
        seclabel u:r:healthd:s0
    
    service healthd-charger /sbin/healthd -n
        class charger
        critical
        seclabel u:r:healthd:s0
    
    on property:selinux.reload_policy=1
        restart ueventd
        restart installd
    

    init.rc文件解析是在main函数完成的。

    init_parse_config_file("/init.rc");
    

    4)属性读取、设置服务

    属性变更请求是init事件处理循环中的另一个事件。

    /* init.c */
        if (!property_set_fd_init && get_property_set_fd() > 0) {
            ufds[fd_count].fd = get_property_set_fd();
            ufds[fd_count].events = POLLIN;
            ufds[fd_count].revents = 0;
            fd_count++;
            property_set_fd_init = 1;
        }
    

    在Android平台中,为了让运行中的所有进程共享系统运行时所需要的各种环境变量,系统开辟了一块称作属性的共享内存区域,并提供了访问该区域的API。

    Android平台的属性是K-V的键值对。系统中所有进程都可以读取属性值,但只有init进程才能修改它。其它进程修改属性值,需向init进程提出请求,并由init进程进行权限检查后,决定是否修改,这之间是通过UNIX域套接字进行通信。

    当属性值更改后,若定义在init.rc中的某个特定条件得到满足,则与此条件相匹配的动作就会执行。每个动作都有一个触发器(Trigger),记录在on property关键字后。

    /* init.rc */
    on property:selinux.reload_policy=1
        restart ueventd
        restart installd    //selinux策略改变时,重启installd进程
    

    这块内存区域初始化是在property_init函数中完成的:

    /* property_service.c */
    void property_init(void)
    {
        init_property_area();
    }
    

    接着读入并解析default.prop配置文件,设置初始化属性值:

    /* property_service.c */
    // #define PROP_PATH_RAMDISK_DEFAULT "/default.prop"
    void property_load_boot_defaults(void)
    {
        load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT);
    }
    
    

    系统中进程与init的通信使用unix域套接字,套接字在start_property_service中完成创建。接下来就可以通过property_get和property_set访问这些属性值了。

    具体unix域套接字处理请求的实现,就不再赘述。

    ro.debuggable 是只读属性(**R**ead**O**nly),这种属性初始化完,不允许修改。

  • 相关阅读:
    memcache安装 基于Red Hat 7.4
    LNMP源码编译
    LAMP源码编译
    Red Hat 7.4 安装laravel框架 基于xampp集成环境
    PHP 扩展开发检测清单(扩展开发必读)
    20 个 Laravel Eloquent 必备的实用技巧
    [项目推荐] Corcel 让你在 WordPress 中使用 Laravel
    Tumblr:我们是如何从 PHP 5 升级到 PHP 7 的
    PHP / Laravel 月刊 #23
    十个你需要在 PHP 7 中避免的坑
  • 原文地址:https://www.cnblogs.com/gm-201705/p/9863953.html
Copyright © 2020-2023  润新知