转自:http://hi.baidu.com/flydownup/blog/item/097dde90c56d301c7bf480c8.html 谢谢!


android init 进程分析

 int main(int argc, char **argv)  
 {  
     int device_fd = -1;  
     int property_set_fd = -1;  
     int signal_recv_fd = -1;  
     int keychord_fd = -1;  
     int fd_count;  
     int s[2];  
     int fd;  
     struct sigaction act;  
     char tmp[PROP_VALUE_MAX];  
     struct pollfd ufds[4];  
     char *tmpdev;  
     char* debuggable;  
   
    /** 
    安装SIGCHLD信号。如果父进程不等待子进程结束,子进程将成为僵尸进程, 
    其占用的系统资源将得不到释,必须注册此信号处理。 
    */  
     act.sa_handler = sigchld_handler;  
     act.sa_flags = SA_NOCLDSTOP;  
     sigemptyset(&act.sa_mask);  
     sigaction(SIGCHLD, &act, 0); 

     /** 
     创建文件系统需要的基本目录。mount一些必要的分区 
     */  
     /* clear the umask */  
     umask(0); /* 设置文件的默认权限,umask和chmod的权限刚好反的,umask的0000相当于chmod的0777 */


     mkdir("/dev", 0755);  
     mkdir("/proc", 0755);  
     mkdir("/sys", 0755);  
   
     mount("tmpfs", "/dev", "tmpfs", 0, "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); 

    /*创建/dev/null结点,重定向标准输入,输出以及标准出错。同时为了跟踪系统的行为Android使用kmsg系统。*/
    open_devnull_stdio();
**********************************************************
void open_devnull_stdio(void)
{
    int fd;
    static const char *name = "/dev/__null__";
    if (mknod(name, S_IFCHR | 0600, (1 << 8) | 3) == 0) {
        fd = open(name, O_RDWR);
        unlink(name);
        if (fd >= 0) {
            dup2(fd, 0);
            dup2(fd, 1);  //1为标准输出stdout、2为标准错误stderr,这里调用dup2重定向0,1,2文件描述符到/dev/null
            dup2(fd, 2);
            if (fd > 2) {
                close(fd);
            }
            return;
        }
    }

    exit(1);
}

关于mknod系统调用
int mknod(const char *pathname, mode_t mode, dev_t dev)
mknod创建一个设备文件,mode参数指定了文件的类型和访问权限,文件类型必须是S_IFREG, S_IFCHR, S_IFBLK, S_IFIFO or S_IFSOCK
如果文件类型是S_IFCHR or S_IFBLK,那么dev指定了新创建的设备的主次设备号,否则,dev被忽略

************************************************************
    log_init();  //创建/dev/kmsg设备结点,我们可以利用这个设备输出调试信息

    INFO("reading config file\n");  
  //定义在init.h中
  //#define INFO(x...)    log_write(6, "<6>init: " x)
  //6表示Log等级,我们用全局变量LOG_DEFAULT_LEVEL定义默认可输出的Log等级,如果INFO在终端无输出,那么可修改此变量


      /*解析初始化脚本,这里只是parse,将脚本解析到一个链表中,没有执行。 */  
     parse_config_file("/init.rc");    
   
     /*获得内核命令行参数*/  
     /* pull the kernel commandline and ramdisk properties file in */  
     qemu_init();  
     import_kernel_cmdline(0);  
   
     /* 根据上一部获得的hardware参数信息,解析额外的硬件相关init脚本, 一般qemu为init.goldfish.rc */  
     get_hardware_name();  
     snprintf(tmp, sizeof(tmp), "/init.%s.rc", hardware);  
     parse_config_file(tmp);  
   
     /* 找到init.rc paser链表中为early-init属性的项目,将其添加到action queue中 */  
     action_for_each_trigger("early-init", action_add_queue_tail);  
     /* 执行这些queue中的动作 */  
     drain_action_queue(); 

    INFO("device init\n");
    device_fd = device_init();

*******************************************************************

int device_init(void)
{
    suseconds_t t0, t1;
    int fd;

    fd = open_uevent_socket(); //建立NETLINK的socket,用于内核空间和用户空间的异步通信,
    if(fd < 0)
        return -1;

    fcntl(fd, F_SETFD, FD_CLOEXEC);
    fcntl(fd, F_SETFL, O_NONBLOCK);

    t0 = get_usecs();
    coldboot(fd, "/sys/class"); //递归遍历/sys/devices目录,查找uevent文件,如果存在,则向uevent文件节点写“add”字符串,触发uevent
    //事件,通过socket接收到内核的msg,分析msg字符串,建立相应的设备文件结点
    coldboot(fd, "/sys/block"); //对于subsystem是firmware,action是add的uevent事件,fork一个子进程,调用process_firmware_event
    //处理相应的uevent事件
    coldboot(fd, "/sys/devices");
    t1 = get_usecs();

    log_event_print("coldboot %ld uS\n", ((long) (t1 - t0)));

    make_device("/dev/pvrsrvkm", 0, 240, 0);

    make_device("/dev/fb0", 0, 29, 0);
    make_device("/dev/fb1", 0, 29, 1);
    make_device("/dev/fb2", 0, 29, 2);
    make_device("/dev/fb3", 0, 29, 3);
    make_device("/dev/fb4", 0, 29, 4);/*make_device在全局数组qemu_perms,devperms和链表devperms_partners中,查找相匹配的struct   

                       perms,如果存在,则获得相应的uid,gid和mode;否则,uid=gid=0, mode为0600;然后创建设备结点*/

    return fd;
}
*******************************************************************

    property_init();/*初始化属性系统,系统属性是设备文件/dev/ashmem,大小为32Kb的一块区域.用全局变量__system_property_area__引用,
      属性信息存储在这块区域1kb之后,全局变量pa_info_array指向其起始元素;初始化之后,读取根文件系统的/default.prop 

             文件并保存*/

    // only listen for keychords if ro.debuggable is true
    debuggable = property_get("ro.debuggable");
    if (debuggable && !strcmp(debuggable, "1")) {
        keychord_fd = open_keychord();
    } /*遍历service_list中的struct service svc,检测其成员keycodes是否为空
      keycodes是通过/dev/keychord来触发这个service,如果keycodes不为空,那么就添加一个新的keychord到链表keychords中*/

    if (console[0]) {
        snprintf(tmp, sizeof(tmp), "/dev/%s", console);
        console_name = strdup(tmp);
    }

    fd = open(console_name, O_RDWR);
    if (fd >= 0)
        have_console = 1;
    close(fd);
   /*打开console  ,/dev/console

     /*加载logo图片,格式是rgb565的raw data(/initlogo.rle),如果不存在此文件,则直接在console上打印android文字, 
     注意的是: android在首次加载时,会非常慢, 这个图就是提醒下我们是在加载模式下,此图显示后会被自动删除,因此默认只有烧code完毕后

     看到一次 */
  
    if( load_565rle_image(INIT_IMAGE_FILE) ) {
    fd = open("/dev/tty0", O_WRONLY);
    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);
    }
    }
 
    if (qemu[0])
        import_kernel_cmdline(1);

 /*设置相关的属性到属性系统中,默认32kb的属性区可以最大存储247个信息
  (8 header words + 247 toc words) = 1020 bytes 
  1024 bytes header and toc + 247 prop_infos @ 128 bytes = 32640 bytes */

     if (!strcmp(bootmode,"factory"))  
         property_set("ro.factorytest", "1");  
     else if (!strcmp(bootmode,"factory2"))  
         property_set("ro.factorytest", "2");  
     else  
         property_set("ro.factorytest", "0");  
  
     property_set("ro.serialno", serialno[0] ? serialno : "");  
     property_set("ro.bootmode", bootmode[0] ? bootmode : "unknown");  
     property_set("ro.baseband", baseband[0] ? baseband : "unknown");  
     property_set("ro.carrier", carrier[0] ? carrier : "unknown");  
     property_set("ro.bootloader", bootloader[0] ? bootloader : "unknown");  
   
     property_set("ro.hardware", hardware);  
     snprintf(tmp, PROP_VALUE_MAX, "%d", revision);  
     property_set("ro.revision", tmp); 

        /* execute all the boot actions to get us started */
     action_for_each_trigger("init", action_add_queue_tail); /*将action_list中name为init的项添加到action_queue中*/
     drain_action_queue();/*执行init section所有的command,主要是安装全局环境变量,加载动态模块,建立相关的文件结构,挂载mtd分区等*/


    property_set_fd = start_property_service();/*读取/system/build.prop,/system/default.prop,/data/local.prop到系统属性区,
      然后读取目录/data/property,如果有persist.开头的属性文件,则读取值并添加到系统属性 

     区,开启persistent_properties_loaded=1,创建socket /dev/socket/property_service,监  

    听等待连接*/

    /* create a signalling mechanism for the sigchld handler */
    if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) == 0) {
        signal_fd = s[0];
 
        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);
    }

    /* make sure we actually have all the pieces we need */
    if ((device_fd < 0) ||
        (property_set_fd < 0) ||
        (signal_recv_fd < 0)) {
        ERROR("init startup failure\n");
        return 1;
    }/*确认几个重要的socket成功建立*/

    /* execute all the boot actions to get us started */
    action_for_each_trigger("early-boot", action_add_queue_tail);
    action_for_each_trigger("boot", action_add_queue_tail);
    drain_action_queue(); /*添加early-boot,boot SECTION到将要发生的action队列中,执行队列中的所有action,启动service_list中
      classname为default并且没有disabled服务,比如启动控制台程序/system/bin/sh,修改属性值init.svc.console
      为running;启动程序/system/bin/pvrsrvinit,修改属性值init.svc.pvrsrvinit为running;启动程   

                           序/system/bin/servicemanager,/system/bin/vold,/system/bin/debuggerd,/system/bin/rild,
                           /system/bin/app_process(zygote) ,/system/bin/mediaserver, /system/bin/playmp3,/system/bin/dbus-daemon
       /system/bin/installd等,并修改相应的属性*/


    /* run all property triggers based on current state of the properties */
    queue_all_property_triggers();
    drain_action_queue();
    /*在action_list链表中查找名字包含串‘property:’的action,分析得到其属性名,在属性系统中查找属性名对应的值,如果值不为空
     则将此action添加到action_queue链表中,触发执行相应的action,这里启动adbd服务,fork一个子进程,加载执行程序/sbin/adbd*/


 
 注册轮询事件:
           - device_fd
           - property_set_fd
           -signal_recv_fd
           -如果有keychord,则注册keychord_fd

 如果支持BOOTCHART,则初始化BOOTCHART

 进入主进程循环:

      - 重置轮询事件的接受状态,revents為0
      - 查询action队列,并执行。
      - 重启需要重启的服务
      - 轮询注冊的事件
          - 如果signal_recv_fd的revents为POLLIN,则得到一个信号,获取并处理
          - 如果device_fd的revents为POLLIN,调用handle_device_fd
          - 如果property_fd的revents为POLLIN,调用handle_property_set_fd
          - 如果keychord_fd的revents为POLLIN,调用handle_keychord

更多相关文章

  1. NPM 和webpack 的基础使用
  2. 【阿里云镜像】使用阿里巴巴DNS镜像源——DNS配置教程
  3. Android(安卓)SQLite是线程安全的吗?
  4. Android:启动页有短暂白屏的解决方法
  5. 管道的私用,删除android的流氓
  6. android 获取服务器文件流 hander
  7. Android(安卓)studio 使用Google 的AdMob
  8. 疯狂Android讲义目录结构
  9. android 动画总结

随机推荐

  1. Android 2.2 r1 API 中文文档系列(11) ――
  2. Android下实现电话号码归属地的查询
  3. 【Android】数据库处理
  4. Bmob SDK导入和初始化
  5. Andorid访问WebService的学习笔记
  6. android编程14个很有用的代码片段
  7. Android中滚动的实现
  8. Android gradlew打包命令
  9. Android超人气系列动态壁纸下载(免费)
  10. android 获取视频和图片的缩略图