Chinaunix首页 | 论坛 | 博客
  • 博客访问: 852356
  • 博文数量: 189
  • 博客积分: 4310
  • 博客等级: 上校
  • 技术积分: 1925
  • 用 户 组: 普通用户
  • 注册时间: 2009-11-27 08:56
文章分类

全部博文(189)

文章存档

2015年(1)

2013年(2)

2012年(1)

2011年(39)

2010年(98)

2009年(48)

分类: LINUX

2011-08-02 19:17:40

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

 


阅读(2008) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~