精华内容
下载资源
问答
  • init进程

    2016-10-25 20:49:37
    前言众所周知的是,Linux中的所有进程都是由init进程创建并运行的。首先Linux内核启动,然后在用户空间启动init进程,再依次启动系统运行的其他进程。在系统启动完成后,init进程会作为守护进程监视其他进程。若是...

    前言

    众所周知的是,Linux中的所有进程都是由init进程创建并运行的。首先Linux内核启动,然后在用户空间启动init进程,再依次启动系统运行的其他进程。在系统启动完成后,init进程会作为守护进程监视其他进程。若是某个监视中的进程一旦终结,进入到僵死状态,它就会释放进程所占用的系统资源。其实,在Android平台中也存在init进程,除了提供以上常见的功能外,还提供几种额外的功能。与Linux类似的是,init进程是在Android启动后,由内核启动的第一个用户级别的进程。

    Android的init进程主要提供四大功能,如下所述。


    init.rc脚本文件分析与运行

    init.rc文件是在init启动后执行的启动脚本文件,文件中记录着init进程执行的功能。在Linux系统中,它被定义在根文件系统中的“/etc/rc.d”目录下,是启动时的可执行文件,在“/etc”目录下保存着设置环境变量的脚本。但在Android系统中,仅使用init.rc与init.{hardware}.rc两个文件,用来定义与执行文件与环境变量。init.rc文件在Android系统运行过程中用于通用的环境及与进程相关的定义,init.{hardware}.rc用于定义Android在不同平台下的特定进程和环境设置等。init.rc文件大致分为两大部分,一部分是以“on”关键字开头的动作列表(action list),另一部分是以“sevice”关键字开头的服务列表。借助系统环境变量或者Linux命令,动作列表用于创建所需目录,以及某些特定文件指定权限,并挂载与系统运行相关的目录。而服务列表用来记录初始化程序需要启动的一些服务。


    创建设备结点文件

    与Linux相同,Android中的应用程序通过设备驱动来访问硬件设备。设备结点文件是设备驱动的逻辑文件,应用程序使用设备结点文件来访问设备驱动程序。init进程通过两种方式创建设备节点文件。第一种,以预先在系统中定义的设备信息为基础,当init进程被启动运行时,统一创建设备结点文件;第二种,在系统运行的时候,当有设备插入到USB端口时,init进程就会感知并接收这一事件,为插入的设备动态创建设备结点文件。

    第一种方法是连接已定义的设备的方法,称之为“冷插拔”,而第二种方法是在系统运行的状态下连接设备,称为“热插拔”。在系统运行过程中,若某个设备被插入,内核就会加载与该设备相关的驱动程序。而后驱动程序就会调用启动函数,将主次设备号、设备类型保存到“/sys”文件系统中,然后发出uevent,并传递给udev守护进程。在这里解释一下,uevent是内核向用户空间进程传递信息的信号系统,即在添加删除设备时,内核使用uevent将设备信息传递到用户空间。udev守护进程在用户空间监听、接收和分析uevent,查看在/sys目录下的设备信息而后在/dev目录相应位置创建设备节点文件。其实上面说道的明显是热插拔机制,那么什么情况是冷插拔呢?刚提到,udev进程运行在用户空间内,它无法处理内核启动过程中发生的uevent。虽然内核空间内的设备驱动程序可以正常运行,但是由于未创建访问设备驱动所需要的设备结点文件,将会出现应用程序无法使用相关设备的问题。那么这个时候冷插拔机制就应运而生了,冷插拔机制就是当内核启动后就会启动udev守护进程,从/sys目录中读取事先注册好的设备信息,而后引发与各设备相对应的uevent,创建设备结点文件。其实,总结一下就是:冷插拔机制就是会在系统启动的时候就会把设备信息事先写在/sys目录下,而后udev守护进程启动后读取/sys目录下的设备信息在/dev目录下创建设备结点文件供应用程序使用;而热插拔机制就是系统在运行过程中,有设备被插入到USB端口中,这个时候udev收到内核传来的uevent才会创建设备结点文件。上述一直说道udev进程是在Linux中的说法,在Android中其实就是init进程。


    进程的终止与再启动

    init进程读取并分析init.rc文件,获得服务列表,而后从列表中依次启动服务子进程,例如:sh、adbd、servicemanager、vold、playmp3。除了以上这些进程外,init进程还启动其他多种进程。若init启动的某个进程终止,则会对系统的运行产生影响。因此,在init启动的进程中,除了一小部分外,其他大部分进程出现意外终止时,init进程要重新启动它们。其实,当init的子进程意外终止时,会向父进程init进程传递SIGCHLD信号,init进程接收该信号,检查进程选项是否设置为oneshot【只启动一次】,若是设置为oneshot,init进程将会放弃重启该进程;否则重启进程。


    属性服务

    属性变更请求是init事件处理循环处理的另一个事件。在Android平台中,为了让运行中的所有进程共享系统运行时所需要的各种设置值,系统开辟了属性存储区域,并提供了访问该区域的API。属性由该key与value构成,其表现形式为“key=value”。在Linux系统中,属性服务主要用来设置环境变量,提供各进程访问设定的环境变量值。在Android平台中,属性服务得到系统地应用,在访问属性值时,添加了访问控制权限,增强了访问的安全性。系统中所有的运行中的进程都可以访问属性值,但是仅有init进程才能修改属性值。其他进程修改属性值时,必须向init进程提出请求,最终由init进程负责修改属性值。

    重要的是。以上四种功能其实还基于一点:Android同Linux系统一样是利用消息传递机制在进程间通信的。所以肯定需要有消息处理器来接收和处理各种消息的。init进程设置处理循环的监听事件,并且注册在POLL中的文件描述符会在poll函数中等待事件,若事件发生,则从poll函数中跳出并处理事件。注册在POLL中的文件描述符:设备结点生成事件处理文件描述符、属性服务申请事件处理文件描述符、进程信号处理文件描述符。

    展开全文
  • Init进程

    2014-11-05 14:12:03
    init进程,它是内核启动的第一个用户级进程。建立该进程是以调用kernel_thread(init ,NULL,0)这个函数的形式进行的。 init总是第一个进程(它的进程号总是1)。原因:当内核启动了自己之后(已被装入内存、已经...

    init进程,它是内核
    启动的第一个用户级进程。
    建立该进程是以调用kernel_thread(init ,NULL,0)这个函数的形式进行的
    init总是第一个进程(它的进程号总是1)。原因:内核启动了自己之后(已被装入内存、已经开始运行、已经初始化了所有的设备驱动程序和数据结构等等),通过启动用户级程序init来完成引导进程的内核部分。
    内核在几个位置上来查寻init,这几个位置以前常用来放置init,但是init的最适当的位置(在Linux系统上)是/sbin/init。如果内核没有找到init,它就会试着运行/bin/sh,如果还是失败了,那么系统的启动就宣告失败了 。



    解释:

    一、GETTY
    GETTY:linux终端的设置

    getty指令是UNIX之类操作系统启动时所必须的3个步骤之一。

    二、/etc/profile 和  .profile

    总:在UNIX/Xenix系统中有两个对用户而言必不可少的文件——etc目录下的profile文件和
    用户主目录($HOME)下的.profile文件。前者是系统文件,对系统下全体用户起作用,后者是
    用户自己的"私人"文件
    功能区别:
    /etc/profile文件的主要功能包括:显示UNIX/Xenix版本信息或者系统专用应用程序的
    提示信息,设置掩码(umask),对终端和邮箱(mail box)进行处理,对非root用户禁止使用new
    s命令等。
    .profile是隐含文件,普通的列目录命令(l、ls、lf,ls-l等)显示不出该文件。UNIX/Xenix系统所有隐文件前           都有"."符。
    显示UNIX/Xenix系统中的隐含文件属性用以下命令:
    $l - a
    原始的.profile文件是系统管理员使用Sysadmsh命令或mkdev user命令创建用户时,系
    统为该用户自动生成的,用户可使用vi编辑该文件为自己所用。.profile文件产生的效用直
    至用户退出系统后才消失,而当用户重新登录时,又开始发挥作用。



    展开全文
  • init进程源码分析

    千次阅读 2017-10-23 23:42:45
    init进程

    Android N平台


    涉及源码位置:
    aosp/system/core/init/init.cpp
    aosp/system/core/rootdir/init.rc
    aosp/system/core/init/property_service.cpp


    0 init进程的主要职责

    • init如何创建zygote。
    • init的属性服务是如何工作的。

    1 init.cpp分析

    1.1从init进程的入口函数main()开始分析

    init进程的main()函数会执行两次,分别是第一阶段和第二阶段,main函数会进入两次,只是两次进去执行的代码不一样

    int main(int argc, char** argv) {
        //由于ueventd watchdogd是公用代码,所以启动的时候根据文件名来判断是哪个进程
        //和ueventd守护进程相关
        if (!strcmp(basename(argv[0]), "ueventd")) {
            return ueventd_main(argc, argv);
        }
        //和watchdogd守护进程相关
        if (!strcmp(basename(argv[0]), "watchdogd")) {
            return watchdogd_main(argc, argv);
        }
    
        // Clear the umask.
        umask(0);
        //添加环境变量 
        add_environment("PATH", _PATH_DEFPATH);
    
        bool is_first_stage = (argc == 1) || (strcmp(argv[1], "--second-stage") != 0);
        //创建文件夹,挂载设备,和linux相关
        // Get the basic filesystem setup we need put together in the initramdisk
        // on / and then we'll let the rc file figure out the rest.
        if (is_first_stage) {
            mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
            mkdir("/dev/pts", 0755);
            mkdir("/dev/socket", 0755);
            mount("devpts", "/dev/pts", "devpts", 0, NULL);
            #define MAKE_STR(x) __STRING(x)
            mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));
            mount("sysfs", "/sys", "sysfs", 0, NULL);
        }
    
        // We must have some place other than / to create the device nodes for
        // kmsg and null, otherwise we won't be able to remount / read-only
        // later on. Now that tmpfs is mounted on /dev, we can actually talk
        // to the outside world.
        //重定向标准输入/输出/错误输出到/dev/_null_ 
        open_devnull_stdio();
        //对klog进行初始化,设置klog level为NOTICE,所以可以将NOTICE级别的log输出,而INFO级别的log就打印不出来
        //<http://blog.csdn.net/fu_kevin0606/article/details/53339001>
        //初始化klog
        klog_init();
        //设置klog的级别为NOTICE
        klog_set_level(KLOG_NOTICE_LEVEL);
    
        NOTICE("init %s started!\n", is_first_stage ? "first stage" : "second stage");
    
        if (!is_first_stage) {//第二阶段执行该代码
            // Indicate that booting is in progress to background fw loaders, etc.
            close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
            //属性服务初始化,接下来会分析
            property_init();
    
            // If arguments are passed both on the command line and in DT,
            // properties set in DT always have priority over the command-line ones.
            process_kernel_dt();
            process_kernel_cmdline();
    
            // Propagate the kernel variables to internal variables
            // used by init as well as the current required properties.
            export_kernel_boot_props();
        }
        //初始化SELinux,加载策略文件
        // Set up SELinux, including loading the SELinux policy if we're in the kernel domain.
        selinux_initialize(is_first_stage);
    
        // If we're in the kernel domain, re-exec init to transition to the init domain now
        // that the SELinux policy has been loaded.
        if (is_first_stage) {
            if (restorecon("/init") == -1) {
                ERROR("restorecon failed: %s\n", strerror(errno));
                security_failure();
            }
            char* path = argv[0];
            //设置第二阶段的参数
            char* args[] = { path, const_cast<char*>("--second-stage"), nullptr };
            //当init是第一阶段,要通过execv重启init进程,进入init的第二阶段
            if (execv(path, args) == -1) {
                ERROR("execv(\"%s\") failed: %s\n", path, strerror(errno));
                security_failure();
            }
        }
    
        // These directories were necessarily created before initial policy load
        // and therefore need their security context restored to the proper value.
        // This must happen before /dev is populated by ueventd.
        NOTICE("Running restorecon...\n");
        restorecon("/dev");
        restorecon("/dev/socket");
        restorecon("/dev/__properties__");
        restorecon("/property_contexts");
        restorecon_recursive("/sys");
    
        epoll_fd = epoll_create1(EPOLL_CLOEXEC);
        if (epoll_fd == -1) {
            ERROR("epoll_create1 failed: %s\n", strerror(errno));
            exit(1);
        }
    
        signal_handler_init();
    
        property_load_boot_defaults();
        export_oem_lock_status();
        //启动属性服务
        start_property_service();
        const BuiltinFunctionMap function_map;
        Action::set_function_map(&function_map);
        //将`service`,`on`,`import`分为3个section
        Parser& parser = Parser::GetInstance();
        parser.AddSectionParser("service",std::make_unique<ServiceParser>());
        parser.AddSectionParser("on", std::make_unique<ActionParser>());
        parser.AddSectionParser("import", std::make_unique<ImportParser>());
        //解析init.rc配置文件入口
        parser.ParseConfig("/init.rc");
    
        ActionManager& am = ActionManager::GetInstance();
    
        am.QueueEventTrigger("early-init");
    
        // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
        am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
        // ... so that we can start queuing up actions that require stuff from /dev.
        am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
        am.QueueBuiltinAction(set_mmap_rnd_bits_action, "set_mmap_rnd_bits");
        am.QueueBuiltinAction(keychord_init_action, "keychord_init");
        am.QueueBuiltinAction(console_init_action, "console_init");
    
        // Trigger all the boot actions to get us started.
        am.QueueEventTrigger("init");
    
        // Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
        // wasn't ready immediately after wait_for_coldboot_done
        am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
    
        // Don't mount filesystems or start core system services in charger mode.
        std::string bootmode = property_get("ro.bootmode");
        if (bootmode == "charger") {
            am.QueueEventTrigger("charger");
        } else {
            am.QueueEventTrigger("late-init");
        }
    
        // Run all property triggers based on current state of the properties.
        am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");
    
        while (true) {
            if (!waiting_for_exec) {
                am.ExecuteOneCommand();
                restart_processes();
            }
    
            int timeout = -1;
            if (process_needs_restart) {
                timeout = (process_needs_restart - gettime()) * 1000;
                if (timeout < 0)
                    timeout = 0;
            }
    
            if (am.HasMoreCommands()) {
                timeout = 0;
            }
    
            bootchart_sample(&timeout);
    
            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;
    }

    main函数里涉及不少东西,只是把当前知道的注释了一下,以后补充,这里关注一下,属性服务的启动,以及对init.rc文件的解析.

    1.2 属性服务

    Android中有很多属性,是通过属性服务(property service)来管理它们的.接着来分析属性服务的代码,从上面的init.cpp的main函数中涉及属性服务的代码有

        property_init();
        start_property_service();

    从property_init()开始分析,该方法的主要工作是初始化属性服务配置.位置在aosp/system/core/init/property_service.cpp

    void property_init() {
        if (property_area_initialized) {
            return;
        }
    
        property_area_initialized = true;
       //__system_property_area_init()函数是用来初始化属性内存区域
        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;
        }
    }

    接下来查看start_property_service函数的具体代码:

    void start_property_service() {
        //创建一个非阻塞的socket,
        property_set_fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
                                        0666, 0, 0, NULL);
        if (property_set_fd == -1) {
            ERROR("start_property_service socket creation failed: %s\n", strerror(errno));
            exit(1);
        }
        //使用listen函数对之前创建的socket进行监听
        listen(property_set_fd, 8);
    
        register_epoll_handler(property_set_fd, handle_property_set_fd);
    }

    listen(property_set_fd, 8);中的8指属性服务最多可以同时为8个试图设置属性的用户提供服务.property_set_fd代表监听
    的端口(socket),这样属性服务就建立了.register_epoll_handler(property_set_fd, handle_property_set_fd)property_set_fd
    放入了epoll句柄中,用epoll来监听property_set_fd:当property_set_fd中有数据到来时,init进程将用handle_property_set_fd
    函数进行处理。(网上资料说:在linux新的内核中,epoll用来替换select,epoll最大的好处在于它不会随着监听fd数目的增长而降低效率。
    因为内核中的select实现是采用轮询来处理的,轮询的fd数目越多,自然耗时越多,epoll还没有研究过,抽时间学习一下).
    当有property_set_fd这个socket有数据来时,就会产生调用到handle_property_set_fd方法,接着分析该方法:

    static void handle_property_set_fd()
    {
        prop_msg msg;
        int s;
        int r;
        struct ucred cr;
        struct sockaddr_un addr;
        socklen_t addr_size = sizeof(addr);
        socklen_t cr_size = sizeof(cr);
        char * source_ctx = NULL;
        struct pollfd ufds[1];
        const int timeout_ms = 2 * 1000;  /* Default 2 sec timeout for caller to send property. */
        int nr;
    
        if ((s = accept(property_set_fd, (struct sockaddr *) &addr, &addr_size)) < 0) {
            return;
        }
    
        /* Check socket options here */
        if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) {
            close(s);
            ERROR("Unable to receive socket options\n");
            return;
        }
    
        ufds[0].fd = s;
        ufds[0].events = POLLIN;
        ufds[0].revents = 0;
        nr = TEMP_FAILURE_RETRY(poll(ufds, 1, timeout_ms));
        if (nr == 0) {
            ERROR("sys_prop: timeout waiting for uid=%d to send property message.\n", cr.uid);
            close(s);
            return;
        } else if (nr < 0) {
            ERROR("sys_prop: error waiting for uid=%d to send property message: %s\n", cr.uid, strerror(errno));
            close(s);
            return;
        }
    
        r = TEMP_FAILURE_RETRY(recv(s, &msg, sizeof(msg), MSG_DONTWAIT));
        if(r != sizeof(prop_msg)) {
            ERROR("sys_prop: mis-match msg size received: %d expected: %zu: %s\n",
                  r, sizeof(prop_msg), strerror(errno));
            close(s);
            return;
        }
    
        switch(msg.cmd) {
        case PROP_MSG_SETPROP:
            msg.name[PROP_NAME_MAX-1] = 0;
            msg.value[PROP_VALUE_MAX-1] = 0;
    
            if (!is_legal_property_name(msg.name, strlen(msg.name))) {
                ERROR("sys_prop: illegal property name. Got: \"%s\"\n", msg.name);
                close(s);
                return;
            }
    
            getpeercon(s, &source_ctx);
    
            if(memcmp(msg.name,"ctl.",4) == 0) {
                // Keep the old close-socket-early behavior when handling
                // ctl.* properties.
                close(s);
                if (check_control_mac_perms(msg.value, source_ctx)) {
    #ifdef MTK_INIT
                    //INFO("[PropSet]: pid:%u uid:%u gid:%u %s %s\n", cr.pid, cr.uid, cr.gid, msg.name, msg.value);
    #endif
                    handle_control_message((char*) msg.name + 4, (char*) msg.value);
                } else {
                    ERROR("sys_prop: Unable to %s service ctl [%s] uid:%d gid:%d pid:%d\n",
                            msg.name + 4, msg.value, cr.uid, cr.gid, cr.pid);
                }
            } else {
                //check_perms:检测设置系统属性的权限,允许返回1,否则返回0
                if (check_perms(msg.name, source_ctx)) {
    #ifdef MTK_INIT
                    //INFO("[PropSet]: pid:%u uid:%u gid:%u set %s=%s\n", cr.pid, cr.uid, cr.gid, msg.name, msg.value);
                    if(strcmp(msg.name, ANDROID_RB_PROPERTY) == 0) {
                        INFO("pid %d set %s=%s\n", cr.pid, msg.name, msg.value);
                        reboot_pid(cr.pid);
                    }
    #endif
                    //设置系统属性
                    property_set((char*) msg.name, (char*) msg.value);
                } else {
                    ERROR("sys_prop: permission denied uid:%d  name:%s\n",
                          cr.uid, msg.name);
                }
    
                // Note: bionic's property client code assumes that the
                // property server will not close the socket until *AFTER*
                // the property is written to memory.
                close(s);
            }
            freecon(source_ctx);
            break;
    
        default:
            close(s);
            break;
        }
    }
    

    接着看property_set((char*) msg.name, (char*) msg.value)的具体实现:

    int property_set(const char* name, const char* value) {
        int rc = property_set_impl(name, value);
        if (rc == -1) {
            ERROR("property_set(\"%s\", \"%s\") failed\n", name, value);
        }
        return rc;
    }

    看来实现设置的活交给了property_set_impl(name, value):

    static int property_set_impl(const char* name, const char* value) {
        size_t namelen = strlen(name);
        size_t valuelen = strlen(value);
        //判断属性名的合法性
        if (!is_legal_property_name(name, namelen)) return -1;
        if (valuelen >= PROP_VALUE_MAX) return -1;
        //如果属性的名称等于“selinux.reload_policy”,并且前面给它设置的值等于1,那么就表示要重新加载SEAndroid策略
        if (strcmp("selinux.reload_policy", name) == 0 && strcmp("1", value) == 0) {
            //加载SEAndroid策略
            if (selinux_reload_policy() != 0) {
                ERROR("Failed to reload policy\n");
            }
        } else if (strcmp("selinux.restorecon_recursive", name) == 0 && valuelen > 0) {
            if (restorecon_recursive(value) != 0) {
                ERROR("Failed to restorecon_recursive %s\n", value);
            }
        }
        //查找名称为name的属性,如果存在的话,那么就会得到一个类型为prop_info的结构体pi,否则返回Null
        prop_info* pi = (prop_info*) __system_property_find(name);
    
        if(pi != 0) {//属性如果存在
            /* ro.* properties may NEVER be modified once set */
            //如果属性是ro.开头,不能修改,直接返回.
            if(!strncmp(name, "ro.", 3)) {
               return -1;
            }
        //属性可以修改,进行修改
            __system_property_update(pi, value, valuelen);
        } else {//属性不存在
            //属性不存在,添加该属性,在属性内存区域的属性值列表pa_info_array的最后增加一项
            int rc = __system_property_add(name, namelen, value, valuelen);
            if (rc < 0) {
                return rc;
            }
        }
        /* If name starts with "net." treat as a DNS property. */
        //接着处理net.开头的属性,
        //如果属性的名称是以“net.”开头,但是又不等于“net.change”(net.change是一个特殊的属性,记录网络属性是否发生变化),那么就将名称为“net.change”的属性设置为name,表示网络属性发生了变化
        if (strncmp("net.", name, strlen("net.")) == 0)  {
            if (strcmp("net.change", name) == 0) {
               return 0;
            }
           /*
            * The 'net.change' property is a special property used track when any
            * 'net.*' property name is updated. It is _ONLY_ updated here. Its value
            * contains the last updated 'net.*' property.
            */
            //设置`net.change`属性
            property_set("net.change", name);
        } else if (persistent_properties_loaded &&
                strncmp("persist.", name, strlen("persist.")) == 0) {//对`persist.`属性进行操作,该属性应该是持久化储存到文件
            /*
             * Don't write properties to disk until after we have read all default properties
             * to prevent them from being overwritten by default values.
             */
        //调用函数write_persistent_property执行持久化操作,以便系统下次启动后,可以将该属性的初始值设置为系统上次关闭时的值
            write_persistent_property(name, value);
        }
        //发送一个属性改变的通知,以便init进程可以执行在启动脚本init.rc中配置的操作
        property_changed(name, value);
        return 0;
    }

    property_set_impl对以ro、net和persist开头的属性进行不同的处理,给张来自罗升阳blog的一张图,帮助对android属性服务有个整体上的认识(Android属性的实现框架):
    Android属性的实现框架

    1.3 读取init.rc文件


    init.rc简单介绍
    init.rc是一个配置文件,内部由Android初始化语言编写(Android Init Language)编写的脚本,它主要包含五种类型语句:
    Action、Commands、Services、Options和Import.在init.rc文件中一条语句通常占用一行,单词之间是用空格符来相隔的。
    如果一行写不下,可以在行尾加上反斜杠,来连接下一行。也就是说,可以用反斜杠将多行代码连接成一行代码。并且使用#
    来进行注释。在init.rc中分成三个部分(Section),而每一部分的开头需要指定on(Actions)、service(Services)或
    import。也就是说,每一个Actions, import或 Services确定一个Section。而所有的Commands和Options只能属于最近定义的
    Section。如果Commands和 Options在第一个Section之前被定义,它们将被忽略。Actions和Services的名称必须唯一。如果
    有两个或多个Actions或Services拥有同样的名称,那么init在执行它们时将抛出错误,并忽略这些Action和Service。
    完整的init文件比较长,这里重点分析Zygote的启动,后续要分析该进程.
    下面简单的用init.rc中的例子对Action、Commands、Services、Options和Import进行说明。

    # Copyright (C) 2012 The Android Open Source Project
    #
    # IMPORTANT: Do not create world writable files or directories.
    # This is a common source of Android security bugs.
    #
    #导入相关的初始化配置文件
    import /init.environ.rc
    import /init.usb.rc
    #平台相关的如:高通、MTK
    import /init.${ro.hardware}.rc
    import /init.usb.configfs.rc
    #导入初始化zygote进程的配置文件
    import /init.${ro.zygote}.rc
    #on 对应action,是启动,early-init市条件 write、mkdir、start是命令(commands)
    on early-init
        # Set init and its forked children's oom_adj.
        write /proc/1/oom_score_adj -1000
    
        # Disable sysrq from keyboard
        write /proc/sys/kernel/sysrq 0
    
        # Set the security context of /adb_keys if present.
        restorecon /adb_keys
    
        # Shouldn't be necessary, but sdcard won't start without it. http://b/22568628.
        mkdir /mnt 0775 root system
    
        # Set the security context of /postinstall if present.
        restorecon /postinstall
    
        start ueventd
    #每一个service对应一个新的进程,ueventd进程名,/sbin/ueventd进程的位置(程序执行的路径)也就是options,后面还可以跟参数,
    #class、critical、seclabel都是命令
    service ueventd /sbin/ueventd
        //core 是服务的组,同样名字的会在一起被启动
        class core
        critical
        seclabel u:r:ueventd:s0

    对于这些commands在Android源码中有文档说明,在aosp/system/core/init/readme.txt,每个命令都有对于的代码实现,接下来就会分析到.


    有了对init.rc文件的简单认识,回到init.cpp中,解析init.rc代码的位置,解析init.rc主要任务由aosp/system/core/init/init_parser.cpp实现.
    开始分析:

        Parser& parser = Parser::GetInstance();
        parser.AddSectionParser("service",std::make_unique<ServiceParser>());
        parser.AddSectionParser("on", std::make_unique<ActionParser>());
        parser.AddSectionParser("import", std::make_unique<ImportParser>());

    Parser::GetInstance()的实现在aosp/system/core/init/init_parser.cpp:

    Parser& Parser::GetInstance() {
        static Parser instance;
        return instance;
    }

    parser.AddSectionParser同样在aosp/system/core/init/init_parser.cpp:

    void Parser::AddSectionParser(const std::string& name,
                                  std::unique_ptr<SectionParser> parser) {
        section_parsers_[name] = std::move(parser);
    }

    这就是将service,on,import设置为了3个Section.

    parser.ParseConfig("/init.rc");

    这就是解析init.rc函数的入口,在init_parser.cpp里面:

    bool Parser::ParseConfig(const std::string& path) {
        if (is_dir(path.c_str())) {//路径是文件夹,调用解析文件夹的函数处理
            return ParseConfigDir(path);
        }
        //解析init.rc
        return ParseConfigFile(path);
    }

    调用了ParseConfigFile(path):

    bool Parser::ParseConfigFile(const std::string& path) {
        INFO("Parsing file %s...\n", path.c_str());
        //用于记录解析init.rc的耗时
        Timer t;
        std::string data;
        if (!read_file(path.c_str(), &data)) {
            return false;
        }
    
        data.push_back('\n'); // TODO: fix parse_config.
        //解析rc文件内容
        ParseData(path, data);
        for (const auto& sp : section_parsers_) {
            //EndFile在Import_parse.cpp
            sp.second->EndFile(path);
        }
    
        // Turning this on and letting the INFO logging be discarded adds 0.2s to
        // Nexus 9 boot time, so it's disabled by default.
        if (false) DumpState();
        //打印出解析文件的耗时,用来查找耗时操作
        NOTICE("(Parsing %s took %.2fs.)\n", path.c_str(), t.duration());
        return true;
    }

    在该方法中调用的主要的方法有ParseData,EndFile接下来分别对这两部分进行分析,ParseData:

    void Parser::ParseData(const std::string& filename, const std::string& data) {
        //TODO: Use a parser with const input and remove this copy
        //copy数据
        std::vector<char> data_copy(data.begin(), data.end());
        data_copy.push_back('\0');
    
        parse_state state;
        state.filename = filename.c_str();
        state.line = 0;
        state.ptr = &data_copy[0];
        state.nexttoken = 0;
    
        SectionParser* section_parser = nullptr;
        std::vector<std::string> args;
    
        for (;;) {//循环遍历解析init.rc文件内容
            //next_token在system/core/init/parse.cpp
            switch (next_token(&state)) {
            case T_EOF:
                if (section_parser) {
                    section_parser->EndSection();
                }
                return;
            case T_NEWLINE:
                state.line++;
                if (args.empty()) {
                    break;
                }
                if (section_parsers_.count(args[0])) {
                    if (section_parser) {
                        //Section解析完成
                        section_parser->EndSection();
                    }
                    section_parser = section_parsers_[args[0]].get();
                    std::string ret_err;
                    //解析Action,Service, Import 三个Section
                    if (!section_parser->ParseSection(args, &ret_err)) {
                        parse_error(&state, "%s\n", ret_err.c_str());
                        section_parser = nullptr;
                    }
                } else if (section_parser) {
                    std::string ret_err;
                    //解析section的内容
                    if (!section_parser->ParseLineSection(args, state.filename,
                                                          state.line, &ret_err)) {
                        parse_error(&state, "%s\n", ret_err.c_str());
                    }
                }
                args.clear();
                break;
            case T_TEXT:
                args.emplace_back(state.text);
                break;
            }
        }
    }

    重点分析!section_parser->ParseSection(args, &ret_err),section_parser->ParseLineSection,ParseSection方法在action,service,
    import三个不同的section调用的位置不同:
    action–>aosp/system/core/init/action.cpp:
    service–>aosp/system/core/init/service.cpp
    import–>aosp/system/core/init/import_parser.cpp
    section_parser->ParseLineSection方法在action,service中嵌套在里面分析
    依次分析这对应的三个ParseSection方法:


    action ParseSection解析
    ParseSection:

    bool ActionParser::ParseSection(const std::vector<std::string>& args,
                                    std::string* err) {
        //将on后面的trigger触发执行条件保存在triggers中
        std::vector<std::string> triggers(args.begin() + 1, args.end());
        //如果一个on后面没有trigger将会报错,必须要有一个
        if (triggers.size() < 1) {
            *err = "actions must have a trigger";
            return false;
        }
    
        auto action = std::make_unique<Action>(false);
        if (!action->InitTriggers(triggers, err)) {
            return false;
        }
    
        action_ = std::move(action);
        return true;
    }

    ParseLineSection:

    bool ActionParser::ParseLineSection(const std::vector<std::string>& args,
                                        const std::string& filename, int line,
                                        std::string* err) const {
        return action_ ? action_->AddCommand(args, filename, line, err) : false;
    }

    调用了AddCommand:

    bool Action::AddCommand(const std::vector<std::string>& args,
                            const std::string& filename, int line, std::string* err) {
        if (!function_map_) {
            *err = "no function map available";
            return false;
        }
    
        if (args.empty()) {
            *err = "command needed, but not provided";
            return false;
        }
    
        auto function = function_map_->FindFunction(args[0], args.size() - 1, err);
        if (!function) {
            return false;
        }
    
        AddCommand(function, args, filename, line);
        return true;
    }

    接着调用了AddCommand(function, args, filename, line):

    void Action::AddCommand(BuiltinFunction f,
                            const std::vector<std::string>& args,
                            const std::string& filename, int line) {
        commands_.emplace_back(f, args, filename, line);
    }

    service ParseSection解析
    ParseSection:

    bool ServiceParser::ParseSection(const std::vector<std::string>& args,
                                     std::string* err) {
        //检查参数个数是否合法
        if (args.size() < 3) {
            *err = "services must have a name and a program";
            return false;
        }
    
        const std::string& name = args[1];
        //检查定义的Service名字的合法性
        if (!IsValidName(name)) {
            *err = StringPrintf("invalid service name '%s'", name.c_str());
            return false;
        }
        //获取执行文件位置和参数,也就是除了service和service名其他的参数
        std::vector<std::string> str_args(args.begin() + 2, args.end());
        //给service赋值
        service_ = std::make_unique<Service>(name, "default", str_args);
        return true;
    }

    定义的每个service都是一个新的进程,定义service还commands,这些commands和执行他们的方法对应关系定义是:

    Service::OptionHandlerMap::Map& Service::OptionHandlerMap::map() const {
        constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
        static const Map option_handlers = {
            {"class",       {1,     1,    &Service::HandleClass}},
            {"console",     {0,     0,    &Service::HandleConsole}},
            {"critical",    {0,     0,    &Service::HandleCritical}},
            {"disabled",    {0,     0,    &Service::HandleDisabled}},
            {"group",       {1,     NR_SVC_SUPP_GIDS + 1, &Service::HandleGroup}},
            {"ioprio",      {2,     2,    &Service::HandleIoprio}},
            {"keycodes",    {1,     kMax, &Service::HandleKeycodes}},
            {"oneshot",     {0,     0,    &Service::HandleOneshot}},
            {"onrestart",   {1,     kMax, &Service::HandleOnrestart}},
            {"seclabel",    {1,     1,    &Service::HandleSeclabel}},
            {"setenv",      {2,     2,    &Service::HandleSetenv}},
            {"socket",      {3,     6,    &Service::HandleSocket}},
            {"user",        {1,     1,    &Service::HandleUser}},
            {"writepid",    {1,     kMax, &Service::HandleWritepid}},
        };
        return option_handlers;
    }

    ParseLineSection:

    bool ServiceParser::ParseLineSection(const std::vector<std::string>& args,
                                         const std::string& filename, int line,
                                         std::string* err) const {
        return service_ ? service_->HandleLine(args, err) : false;  //service_为true, 调用HandleLine
    }

    接着调用了HandleLine:

    bool Service::HandleLine(const std::vector<std::string>& args, std::string* err) {
        if (args.empty()) {
            *err = "option needed, but not provided";
            return false;
        }
    
        static const OptionHandlerMap handler_map;   //获得option对应的函数表
        auto handler = handler_map.FindFunction(args[0], args.size() - 1, err); //根据option获取对应的函数名
    
        if (!handler) {
            return false;
        }
    
        return (this->*handler)(args, err);   
    }

    EndSection:

    void ServiceParser::EndSection() {
        if (service_) {
            ServiceManager::GetInstance().AddService(std::move(service_));
        }
    }
    void ServiceManager::AddService(std::unique_ptr<Service> service) {
        Service* old_service = FindServiceByName(service->name());
        if (old_service) {    //service已经被定义过了就抛弃
            ERROR("ignored duplicate definition of service '%s'",
                  service->name().c_str());
            return;
        }
        services_.emplace_back(std::move(service));  //将service添加services_列表
    }

    import ParseSection解析

    bool ImportParser::ParseSection(const std::vector<std::string>& args,
                                    std::string* err) {
        //import 命令是2参数的,如果参数个数不对就直接报错
        if (args.size() != 2) {
            *err = "single argument needed for import\n";
            return false;
        }
    
        std::string conf_file;
        //第一个参数都是import,args[1]才是要导入的配置文件conf_file
        bool ret = expand_props(args[1], &conf_file);
        if (!ret) {
            *err = "error while expanding import";
            return false;
        }
    
        INFO("Added '%s' to import list\n", conf_file.c_str());
        //将所有的conf_file添加到imports_列表
        imports_.emplace_back(std::move(conf_file));
        return true;
    }

    终于把ParseData方法粗略的过了一遍,接下来分析EndFile,该方法其实就在import_parser.cpp中:

    void ImportParser::EndFile(const std::string& filename) {
        auto current_imports = std::move(imports_);  //获取imports_
        imports_.clear();   //将imports_列表清空
        for (const auto& s : current_imports) {  //遍历列表
            if (!Parser::GetInstance().ParseConfig(s)) {   //调用ParseConfig函数,对其他配置进行解析, 流程遇上面的相同
                ERROR("could not import file '%s' from '%s': %s\n",
                      s.c_str(), filename.c_str(), strerror(errno));
            }
        }
    }

    到此,init.rc文件的解析工作完成,接下来的的工作就是执行这些配置,由于init.rc里面配置了太多,接下来以Zygote这个service为例,分析.
    init.rcimport /init.${ro.zygote}.rc,这就引入了不同的zygote配置:

    init.zygote32_64.rc  init.zygote32.rc     init.zygote64_32.rc  init.zygote64.rc

    这里以init.zygote32.rc为例:

    #zygote是进程
    service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
        #启动组名,同样名字的一起启动
        class main
        socket zygote stream 660 root system
        #onrestart表示zygote重启时需要执行的命令
        onrestart write /sys/android_power/request_state wake
        onrestart write /sys/power/state on
        onrestart restart audioserver
        onrestart restart cameraserver
        onrestart restart media
        onrestart restart netd
        writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks

    通过对这个叫zygote的service的解析之后,在init.rc配置文件中配置了怎么去启动zygote:

    on nonencrypted
        # A/B update verifier that marks a successful boot.
        exec - root -- /system/bin/update_verifier nonencrypted
        #通过class_start方法启动了main(这就是zygote的中配置的)
        class_start main
        class_start late_start

    找到class_start对应执行的函数就可以接着分析了,对应关系就在aosp/system/core/init/builtins.cpp:

    ltinFunctionMap::Map& BuiltinFunctionMap::map() const {
        constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
        static const Map builtin_functions = {
            {"bootchart_init",          {0,     0,    do_bootchart_init}},
            {"chmod",                   {2,     2,    do_chmod}},
            {"chown",                   {2,     3,    do_chown}},
            {"class_reset",             {1,     1,    do_class_reset}},
            {"class_start",             {1,     1,    do_class_start}},
            {"class_stop",              {1,     1,    do_class_stop}},
            {"copy",                    {2,     2,    do_copy}},
            {"domainname",              {1,     1,    do_domainname}},
            {"enable",                  {1,     1,    do_enable}},
            {"exec",                    {1,     kMax, do_exec}},
            {"export",                  {2,     2,    do_export}},
            {"hostname",                {1,     1,    do_hostname}},
            {"ifup",                    {1,     1,    do_ifup}},
            {"init_user0",              {0,     0,    do_init_user0}},
            {"insmod",                  {1,     kMax, do_insmod}},
            {"installkey",              {1,     1,    do_installkey}},
            {"load_persist_props",      {0,     0,    do_load_persist_props}},
            {"load_system_props",       {0,     0,    do_load_system_props}},
            {"loglevel",                {1,     1,    do_loglevel}},
            {"mkdir",                   {1,     4,    do_mkdir}},
            {"mount_all",               {1,     kMax, do_mount_all}},
            {"mount",                   {3,     kMax, do_mount}},
            {"powerctl",                {1,     1,    do_powerctl}},
            {"restart",                 {1,     1,    do_restart}},
            {"restorecon",              {1,     kMax, do_restorecon}},
            {"restorecon_recursive",    {1,     kMax, do_restorecon_recursive}},
            {"rm",                      {1,     1,    do_rm}},
            {"rmdir",                   {1,     1,    do_rmdir}},
            {"setprop",                 {2,     2,    do_setprop}},
            {"setrlimit",               {3,     3,    do_setrlimit}},
            {"start",                   {1,     1,    do_start}},
            {"stop",                    {1,     1,    do_stop}},
            {"swapon_all",              {1,     1,    do_swapon_all}},
            {"symlink",                 {2,     2,    do_symlink}},
            {"sysclktz",                {1,     1,    do_sysclktz}},
            {"trigger",                 {1,     1,    do_trigger}},
            {"verity_load_state",       {0,     0,    do_verity_load_state}},
            {"verity_update_state",     {0,     0,    do_verity_update_state}},
            {"wait",                    {1,     2,    do_wait}},
            {"write",                   {2,     2,    do_write}},
        };
        return builtin_functions;
    }

    对于在rc配置文件中的commands都对应一个方法函数,可以通过grep -nr "<command>" .aosp/system/core/init/中搜索.
    找到需要的对应关系:

            {"class_start",             {1,     1,    do_class_start}},

    进入do_class_start方法:

    static int do_class_start(const std::vector<std::string>& args) {
            /* Starting a class does not start services
             * which are explicitly disabled.  They must
             * be started individually.
             */
        ServiceManager::GetInstance().
            ForEachServiceInClass(args[1], [] (Service* s) { s->StartIfNotDisabled(); });
        return 0;
    }

    接着看StartIfNotDisabled(),位置aosp/system/core/init/service.cpp:

    bool Service::StartIfNotDisabled() {
        if (!(flags_ & SVC_DISABLED)) {
            return Start();
        } else {
            flags_ |= SVC_DISABLED_START;
        }
        return true;
    }

    还调了Start(),接着看吧:
    “cpp
    bool Service::Start() {
    ……
    //判断需要启动的Service的对应的执行文件是否存在,不存在则不启动该Service
    struct stat sb;
    if (stat(args_[0].c_str(), &sb) == -1) {
    ERROR(“cannot find ‘%s’ (%s), disabling ‘%s’\n”,
    args_[0].c_str(), strerror(errno), name_.c_str());
    flags_ |= SVC_DISABLED;
    return false;
    }
    ……
    //每一个service都是一个新进程,必然需要fork
    pid_t pid = fork();
    if (pid == 0) {
    umask(077);

        for (const auto& ei : envvars_) {
            add_environment(ei.name.c_str(), ei.value.c_str());
        }
    
        for (const auto& si : sockets_) {
            int socket_type = ((si.type == "stream" ? SOCK_STREAM :
                                (si.type == "dgram" ? SOCK_DGRAM :
                                 SOCK_SEQPACKET)));
            const char* socketcon =
                !si.socketcon.empty() ? si.socketcon.c_str() : scon.c_str();
    
            int s = create_socket(si.name.c_str(), socket_type, si.perm,
                                  si.uid, si.gid, socketcon);
            if (s >= 0) {
                PublishSocket(si.name, s);
            }
        }
        ......
        //execve执行程序,在`init.zygote32.rc`里写了zygote的进程程序的位置以及参数
       if (execve(args_[0].c_str(), (char**) &strs[0], (char**) ENV) < 0) {
            ERROR("cannot execve('%s'): %s\n", args_[0].c_str(), strerror(errno));
        }
    
        _exit(127);
    }
    ......
    NotifyStateChange("running");
    return true;
    

    }

    在fork出来的新的子进程里就会进入java层面,`aosp/frameworks/base/cmds/app_process/app_main.cpp`的main()函数:  
    ```C++
    int main(int argc, char* const argv[])
    {
        if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) {
            // Older kernels don't understand PR_SET_NO_NEW_PRIVS and return
            // EINVAL. Don't die on such kernels.
            if (errno != EINVAL) {
                LOG_ALWAYS_FATAL("PR_SET_NO_NEW_PRIVS failed: %s", strerror(errno));
                return 12;
            }
        }
        AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
        // Process command line arguments
        // ignore argv[0]
        argc--;
        argv++;
        ......
        if (!niceName.isEmpty()) {
            runtime.setArgv0(niceName.string());
            //设置进程名为zygote
            set_process_name(niceName.string());
        }
    
        if (zygote) {//经过一系列的初始化和参数判断,会调用到这里
            runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
        } else if (className) {//与上面的启动方式对比,两者在native层实现相同,区别在java层,ZygoteInit比RuntimeInit多做不少事情
            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.");
            return 10;
        }
    }
    
    
    
    
    <div class="se-preview-section-delimiter"></div>
    

    最终使用runtime.start执行”com.android.internal.os.ZygoteInit”,接着分析runtime.start的具体实现.runtime是AppRuntime类,可是AppRuntime
    类没有start方法,于是找到AppRuntime的父类AndroidRuntime的start方法:

    void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
    {
        ALOGD(">>>>>> START %s uid %d <<<<<<\n",
                className != NULL ? className : "(unknown)", getuid());
    
    
        static const String8 startSystemServer("start-system-server");
    
        /*
         * 'startSystemServer == true' means runtime is obsolete and not run from
         * init.rc anymore, so we print out the boot start event here.
         */
        for (size_t i = 0; i < options.size(); ++i) {
            if (options[i] == startSystemServer) {
               /* track our progress through the boot sequence */
               const int LOG_BOOT_PROGRESS_START = 3000;
               LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START,  ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
            }
        }
    
        const char* rootDir = getenv("ANDROID_ROOT");
        if (rootDir == NULL) {
            rootDir = "/system";
            if (!hasDir("/system")) {
                LOG_FATAL("No root directory specified, and /android does not exist.");
                return;
            }
            setenv("ANDROID_ROOT", rootDir, 1);
        }
    
        //const char* kernelHack = getenv("LD_ASSUME_KERNEL");
        //ALOGD("Found LD_ASSUME_KERNEL='%s'\n", kernelHack);
    
        /* start the virtual machine */
        JniInvocation jni_invocation;
        jni_invocation.Init(NULL);
        JNIEnv* env;
        //启动虚拟机
        if (startVm(&mJavaVM, &env, zygote) != 0) {
            return;
        }
        onVmCreated(env);
        //注册JNI方法到虚拟机
        /*
         * Register android functions.
         */
        if (startReg(env) < 0) {
            ALOGE("Unable to register all android natives\n");
            return;
        }
    
        /*
         * We want to call main() with a String array with arguments in it.
         * At present we have two arguments, the class name and an option string.
         * Create an array to hold them.
         */
        jclass stringClass;
        jobjectArray strArray;
        jstring classNameStr;
    
        stringClass = env->FindClass("java/lang/String");
        assert(stringClass != NULL);
        strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);
        assert(strArray != NULL);
        classNameStr = env->NewStringUTF(className);
        assert(classNameStr != NULL);
        env->SetObjectArrayElement(strArray, 0, classNameStr);
    
        for (size_t i = 0; i < options.size(); ++i) {
            jstring optionsStr = env->NewStringUTF(options.itemAt(i).string());
            assert(optionsStr != NULL);
            env->SetObjectArrayElement(strArray, i + 1, optionsStr);
        }
    
        /*
         * Start VM.  This thread becomes the main thread of the VM, and will
         * not return until the VM exits.
         */
        char* slashClassName = toSlashClassName(className);
        jclass startClass = env->FindClass(slashClassName);
        if (startClass == NULL) {
            ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
            /* keep going */
        } else {
            jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
                "([Ljava/lang/String;)V");
            if (startMeth == NULL) {
                ALOGE("JavaVM unable to find main() in '%s'\n", className);
                /* keep going */
            } else {//启动com.android.internal.os.ZygoteInit
                env->CallStaticVoidMethod(startClass, startMeth, strArray);
    
    
    
    
    
    <div class="se-preview-section-delimiter"></div>
    
    #if 0
                if (env->ExceptionCheck())
                    threadExitUncaughtException(env);
    
    
    
    
    <div class="se-preview-section-delimiter"></div>
    
    #endif
            }
        }
        free(slashClassName);
    
        ALOGD("Shutting down VM\n");
        if (mJavaVM->DetachCurrentThread() != JNI_OK)
            ALOGW("Warning: unable to detach main thread\n");
        mVMShutdown = true;
        if (mJavaVM->DestroyJavaVM() != 0)
            ALOGW("Warning: VM did not shut down cleanly\n");
    }

    该方法做了一下几件事:

    • 启动java虚拟机
    • 将JNI方法注册到java虚拟机
    • 进入到ZygoteInit.java的main()方法

    进入到ZygoteInit.java也就是进入到java层,在分析Zygote的启动过程中再接着分析,这里告一段落.需要注意的是Zygote进程的启动是在解析init.Zygote32.rc开始的,
    到这里还没有完成,只是到这,C++层的执行完了.在另一篇介绍Zygote启动的文章中,再接着ZygoteInit.java的main()分析,从java层分析.在C++层只是讲Zygote进程创建,
    但是什么活也没干,干活是在java层面,因此文章将zygote进程的分析从此处分成两个部分,同事也是为了让文章内容是以init进程分析为主.

    总结

    本文主要分析了,init进程的启动,主要分析了一下内容:

    • init进程启动属性服务的过程,分析了属性服务建立过程
    • init进程对rc配置文件的解析,分为对import,action,service,commands的的解析
    • 以zygote进程为例子,分析了作为service被解析之后的执行过程,一直到调用到java层的过程

    参考blog

    http://blog.csdn.net/fu_kevin0606/article/details/53339001
    http://blog.csdn.net/innost/article/details/47204675
    http://blog.csdn.net/luoshengyang/article/details/38102011
    http://blog.csdn.net/itachi85/article/details/54783506
    http://blog.csdn.net/kc58236582/article/details/52247547
    http://blog.csdn.net/fu_kevin0606/article/details/53320515

    展开全文
  • init进程笔记

    2019-07-02 17:41:19
    在Linux内核启动到最后时,内核会调用三个进程,PID分别为0,1,2,其中就包含了init进程(PID=1)。init进程可以说是之后所有用户进程的父进程了,它自从产生便一直存在在内存空间中直到关机。 在系统启动后,使用...

    在Linux内核启动到最后时,内核会调用三个进程,PID分别为0,1,2,其中就包含了init进程(PID=1)。init进程可以说是之后所有用户进程的父进程了,它自从产生便一直存在在内存空间中直到关机。
    在系统启动后,使用ps命令查看进程列表可以在最开始找到init进程,ps命令打印的是所有用户态下的进程,这是不是可以说明init进程就是一个用户进程呢?答案是否定的,init进程实际上最初是内核进程,在经过一段时间后才变为用户进程的。尽管这样,实际上init进程完成的绝大多数工作是在用户态下进行的。
    init进程在内核态时,其本身实质上就是一个函数,内容如下:

    static int __init kernel_init(void * unused)
    {
    	/*
    	 * Wait until kthreadd is all set-up.
    	 */
    	wait_for_completion(&kthreadd_done);
    	lock_kernel();
    
    	/*
    	 * init can allocate pages on any node
    	 */
    	set_mems_allowed(node_states[N_HIGH_MEMORY]);
    	/*
    	 * init can run on any cpu.
    	 */
    	set_cpus_allowed_ptr(current, cpu_all_mask);
    	/*
    	 * Tell the world that we're going to be the grim
    	 * reaper of innocent orphaned children.
    	 *
    	 * We don't want people to have to make incorrect
    	 * assumptions about where in the task array this
    	 * can be found.
    	 */
    	init_pid_ns.child_reaper = current;
    
    	cad_pid = task_pid(current);
    
    	smp_prepare_cpus(setup_max_cpus);
    
    	do_pre_smp_initcalls();
    	start_boot_trace();
    
    	smp_init();
    	sched_init_smp();
    
    	do_basic_setup();
    
    	/* Open the /dev/console on the rootfs, this should never fail */
    	if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
    		printk(KERN_WARNING "Warning: unable to open an initial console.\n");
    
    	(void) sys_dup(0);
    	(void) sys_dup(0);
    	/*
    	 * check if there is an early userspace init.  If yes, let it do all
    	 * the work
    	 */
    
    	if (!ramdisk_execute_command)
    		ramdisk_execute_command = "/init";
    
    	if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {
    		ramdisk_execute_command = NULL;
    		prepare_namespace();
    	}
    
    	/*
    	 * Ok, we have completed the initial bootup, and
    	 * we're essentially up and running. Get rid of the
    	 * initmem segments and start the user-mode stuff..
    	 */
    
    	init_post();
    	return 0;
    }
    

    分析这个函数,init进程在内核态主要完成了三件事情,其一是调用sys_open打开了控制台,同时也得到了内核的第一个文件描述符,然后进程又连续调用了两次sys_dup函数再次得到了两个文件描述符,这样系统就得到了三个文件描述符,意义分别是标准输入,标准输出,标准错误文件。之后所有的用户进程由于继承了init进程,所以最初就具有了这三个文件描述符。其二是调用prepare_namespace函数挂载根文件系统,关于根文件系统的描述(根文件系统存放位置,根文件格式)可以通过设置uboot的bootargs参数的方式给出,比如bootargs=console=ttySAC2,115200 root=/dev/mmcblk0p2 rw init=/linuxrc rootfstype=ext3,根文件系统存放位置为第0个MMC设备的第二个分区中,根文件格式是ext3。如果挂载失败,内核会自动在5秒后进行重启。第三,内核寻找根文件系统中的init进程程序,并且执行,这个工作是在init_post函数中完成的。在init_post函数中,程序会首先尝试使用bootargs中的给出的init程序位置(/linuxrc),若发现根文件系统并未在此给出init程序,其会尝试另外四个位置寻找init程序,顺序为/sbin/init -> /etc/init -> /bin/init -> /bin/sh,如果还是未能找到init程序,系统默认启动失败。除此之外,init_post函数可以看做是init进程由内核态到用户态的过渡。
    init进程过渡到用户态后,其便开始调度其他进程直到建立起我们最后看到的终端界面,调度的进程包括了login进程,命令行进程,shell进程等等。

    展开全文
  • init进程与Init.rc init进程的进程号为1,这个进程是系统起来的第一个进程,init进程掌握了整个系统的启动流程。 Android可能运行在不同的平台上,不同的设备上,启动的逻辑是不尽相同的,为了适应各平台和设备的...
  • Init 进程详解

    千次阅读 2017-11-13 11:16:13
    Android 内核加载完成后,就会启动init进程,init进程是Android系统用户空间的第一个进程。init程序放在系统根目录下,init进程代码位于源码的目录“system/core/init”下面。下面我们来分析init进程的启动过程1. ...
  • idle进程是init进程和kthreadd进程(内核线程)的父进程; init进程init进程是Linux中第一个用户空间的进程,PID为1; init进程是其他用户空间进程的直接或间接父进程; kthreadd(内核线程): kthre.....
  • Android init 进程

    2017-08-16 20:41:34
    Linux 内核启动之后第一个用户进程就是init进程,之后init进程启动zygote进程,zygote 进程fork出systemserver,systemserver负责向zygote进程请求fork应用进程 Android init进程的入口文件在system/core/init/init....
  • android init进程

    2018-08-13 09:41:59
    android init进程 Init进程在Android系统中非常重要,他是Linux系统中用户空间的第一个进程。它的任务主要做2件事情: 1.解析配置文件init.rc,然后启动系统各种native进程,例如非常重要的进程Nygote进程,...
  • linux init进程

    千次阅读 2016-09-11 12:36:08
    linux init进程
  • init进程与zygote进程

    2017-11-06 11:33:41
    如图,通过ps命令看到的init进程和守护进程之间的关系。 zygote进程生成系统服务器端和各种java应用程序进程。 init进程在所有系统中通常具有固定的pid值(pid=1),但启动其他进程时,不同系统赋予的pid值也有所...
  • Android init进程

    2016-12-14 14:13:07
     linux中的所有进程都是由Init进程创建并运行的。首先linux内核启动,然后在用户空间中,启动init进程,再依次启动系统运行所需的其他进程。在系统启动完成后,init进程会作为守护进程监视其他进程。若某个监视中的...
  • 僵尸进程与孤儿进程的区别以及什么是init进程僵尸进程init进程孤儿进程 僵尸进程 首先我们要明白什么是父子进程:初始启动一个程序,在程序内使用fork函数创建出一个新的进程。这个fork函数创建出来的进程就是初始...
  • init进程是Linux启动之后,用户空间的第一个进程,进程号为1。 引入init进程 步骤如下 启动电源,加载引导程序BootLoader到ROM中 BootLoader把系统OS拉起来并运行 Linux内核启动,首先在系统文件中寻找init.rc文件...
  • Android Init进程

    2015-08-18 19:39:55
    Bootloader,Kernel完成系统设置以后,首先会在系统文件中寻找init文件,然后启动系统的第一个进程:init进程   init进程 /system/core/init/init.c 1. init进程主要完成两个事情,一是创建系统目录,挂载文件系统...
  • Android系统init进程启动及init.rc全解析

    万次阅读 多人点赞 2017-11-23 16:14:40
    system/core/init/init.c文件main函数中parse_config_file(init.rc)读取并解析init.rc文件内容。将service信息放置到system/core/init/init_parser.cpp的service_list中 system/core/init/init.c文
  • init进程简析

    2015-01-30 22:44:45
    由于执行init()函数的内核线程和init进程的进程标识符都是1,它们又都叫init,因此init()函数和init进程容易造成概念上的模糊不清。 主要区别: 1、init()函数是内核代码的一部分,在内核态运行,是独立...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 14,273
精华内容 5,709
关键字:

init进程