(懒人最近想起我还有csdn好久没打理了,这个android init躺在我的草稿箱中快5年了,稍微改改发出来吧)

android设备上电,引导程序引导进入boot(通常是uboot),加载initramfs、kernel镜像,启动kernel后,进入用户态程序。第一个用户空间程序是init, PID固定是1.在android系统上,init的代码位于/system/core/init下,基本功能有:

  • 管理设备
  • 解析并处理启动脚本init.rc
  • 实时维护这个init.rc中的服务

init进程的系统初始化和服务流程,可以简单的看下init.c中的main函数。这里简要分析一下这个函数的主要作用,细节不展开或后继再展开。

简要说下main函数的各项操作吧:

    if (!strcmp(basename(argv[0]), "ueventd"))        return ueventd_main(argc, argv);    if (!strcmp(basename(argv[0]), "watchdogd"))        return watchdogd_main(argc, argv);

以上这段,是main函数入口的第一句,这是判断是跑那个程序。

/system/core/init 这里面的一份代码,编出的二进制可执行程序init,实际是分为三个程序运行的,指示大家共享一份代码而已。三份分别是:
/init: 实际init可执行程序
/sbin/ueventd:ueventd daemon程序,是init的软链接
/sbin/watchdogd: watchdogd daemon程序,也是init的软链接。

我们都知道,main函数的参数argv的第一个,argv[0]为自身运行目录路径和程序名,这里就根据这个条件来判断代码走的路径。为何这样做?其实完全可以再另外写一个ueventd或watchdogd的一套程序,定义main函数啊。其实这里原因也是很简单,他们共享了太多东西,直接写到一起多简单!就像busybox或toolbox集成那么多命令工具一样的道理。

    /* clear the umask */    umask(0);

清理umask,这个主要是文件权限的问题,设了umask,可以全局mask掉一些文件权限。

        /* 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.         */    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);

Linux需要的dev,proc, sys等文件系统的加载。

        /* indicate that booting is in progress to background fw loaders, etc */    close(open("/dev/.booting", O_WRONLY | O_CREAT, 0000));

创建一个/dev/.booting文件。 /dev是内存文件系统,不会保存的,每次开机都要重新创建。这个是指示,目前在booting过程中,具体干什么用的,介绍ueventd的时候就清楚了。就是加载设备的fimware用的。

    open_devnull_stdio();    klog_init();    property_init();

这三句,分别是将 stdin,stdout和stderr先初始化到/dev/__null__,这样用printf或其他打印的,都输出不了,也不会引起其它异常(这个阶段,其实不能用,会出错的)。

注意这个/dev/__null__是临时起的名字,创建node后删了,不影响系统真正的/dev/null,这里只需要fd即可,有了fd后,文件名就无用了,有了还会有干扰。

klog_init,是将init进程中的log,打印到内核printk的log buffer中的方法。这对调试init很有帮助,毕竟此时没有shell,通用的log输出方法,如printf等还不能工作,借助底层已有的内核调试功能当然是最好的了。

property的初始化也是在这里,property读取可以在各个用户进程中做,但设置的入口必须是到init进程中来。在4.4上,property这块修改了好多,现在是通过字典树的方式存储,可以支持更多的property属性。

 get_hardware_name(hardware, &revision);

从 /proc/cpuinfo中读到hardware信息,设置到ro.hardware属性中,便于后面解析 init.${ro.hardware}.rc使用

    process_kernel_cmdline();

kernel的command参数,解析后也放到property中,供以后的子进程或其他服务等使用。

    union selinux_callback cb;    cb.func_log = klog_write;    selinux_set_callback(SELINUX_CB_LOG, cb);    cb.func_audit = audit_callback;    selinux_set_callback(SELINUX_CB_AUDIT, cb);    selinux_initialize();    /* 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.     */    restorecon("/dev");    restorecon("/dev/socket");    restorecon("/dev/__properties__");    restorecon_recursive("/sys");

selinux的初始化和检查,没仔细研究selinux,则个检查过程有时候还挺耗费时间的。

    INFO("property init\n");    if (!is_charger)        property_load_boot_defaults();

这个是加载并设置properties。这些是预置的property,通常是/system/build.prop和default.prop文件中预置的那些property。

init_parse_config_file("/init.rc");

这是开始解析init.rc文件了。细节和init.rc的格式、写法不说了,网上到处都是。主要说一下常见问题:
1. 下载代码的服务器,umask有时候可能会有影响,init.rc文件other和group用户是不能有写权限的,编译的PC的umask最好设置成0022。
2. init.rc文件严格来说只是配置文件,不算脚本,循环、条件判断等等都不支持的,不要想这里能干太多事情。

    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");    queue_builtin_action(console_init_action, "console_init");    /* execute all the boot actions to get us started */    action_for_each_trigger("init", action_add_queue_tail);    ...

这是开始处理init.rc中parser好的各个命令了。

action_for_each_trigger是对此类trigger所在呃所有命令,都加入到actions的list中去。对实际代码或项目上要全局的看一下,所有的*.rc的同一个trigger都一起处理的。

queue_builtin_action这是内建的actions,也是将actions动作加入到actions list中。

这里需要注意的是,各个trigger的加载顺序,先加入的先执行,后加入的后执行,要特别注意,尤其是要修改init.rc文件的时候,不了解这个容易因为前后依赖关系造成问题。


都准备好了的话,就到了服务处理阶段了,这是一个死循环,主要工作就是:
1. 将init.rc及内建的actions命令,一条一条执行
2.负责对service的管理。
3. 对signal及进程退出的处理
4.响应property设置的请求(设置都在init中统一设置,读取进程可以自己读共享内存)

    for(;;) {        int nr, i, timeout = -1;        execute_one_command();        restart_processes();...

有时候需要优化开机的话,可以测量一下execute_on_command中的命令执行时间,把较长的(比如大于50ms)的打印出来,再想办法优化一下。

每次循环,执行一条命令,检查是否有需要重启的服务..

        nr = poll(ufds, fd_count, timeout);        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())                    handle_signal();            }        }

多路polling,当polling到东西,就执行相应的动作。

timeout时间到,执行下轮循环。init.rc中的command没处理完的话,timeout是0,这样之前的actions list可以一顺下来都执行掉。
注意,init.rc中定义的服务,是在class_start这个command中做的。





更多相关文章

  1. Android中的服务(service)详解(一)
  2. 初学Android,使用Drawable资源之使用StateListDrawable资源(十三
  3. 图片布局在android中资源文件夹中添加一个新的图片资源
  4. Android(安卓)studio 配置Git (第一次提交代码)
  5. android通过反射代替R来获取控件对象
  6. 关于Android下的JNI编程、SO库以及NDK的一些问题
  7. 解决URl中文路径乱码问题
  8. Android(安卓)Binder 机制学习总结(一)
  9. android中修改默认语言

随机推荐

  1. 中华万年历官方V2.0.0 Android最好用的日
  2. android高亮引导页
  3. android 开发规范好工具----Android(安卓
  4. Android实现仿360手机卫士悬浮窗效果
  5. 基于Android的闹钟的软件
  6. Android(安卓)实现水波纹
  7. Android笔记(一):Android工程目录介绍
  8. Android使用Ant多渠道打包
  9. Android(安卓)程序开发的插件化 模块化方
  10. Android(安卓)学习资料收集汇总