深入理解init_1----init分析(基于Android 2.2,源码来自Google)
2017-02-28 16:56
746 查看
深入理解init_1—-init分析
1、概述Init是一个进程,确切的说,它是Linux系统中用户空间的第一个进程。由于android是基于Linux内核的,所以init也是Android系统中用户空间的第一个进程,它的进程号是1.作为天字第一号进程,init被赋予了很多及其重要的工作职责,下面就关注其中两个比较重要的职责:
1) Init 进程负责创建系统中几个关键进程,尤其是下一章要介绍的zygote,它更是java世界的开创者。那么init进程如何创建zygote的呢?
2) Android系统有很多属性,于是init就提供了一个property service (属性服务)来管理他们。那么这个属性服务怎样工作的呢?
下面我们就这两个问题来分析init:
(1) Init 如何创建 zygote?
(2) Init的属性服务是如何工作的。
init.c 主要函数功能概略图
2、init分析
2.1、文件位置
System/core/init.c
2.2部分代码分析
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_handler. act.sa_handler = sigchld_handler; act.sa_flags = SA_NOCLDSTOP; act.sa_mask = 0; act.sa_restorer = NULL; sigaction(SIGCHLD, &act, 0); /* clear the umask */ umask(0); /* 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. */ //创建一些文件夹,并挂载设备,这些是与Linux相关的 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); /* 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(); /* 设置init的日志输出设备为 /dev/_kmsg_,不过这个文件被打开后,会立即被UNLINK了,这样,其他进程就没有办法打开这个文件读取日志信息。 */ log_init(); //解析init.rc 配置信息 INFO("reading config file\n"); parse_config_file("/init.rc"); /* pull the kernel commandline and ramdisk properties file in */ qemu_init(); import_kernel_cmdline(0); //下面这个函数通过读取/proc/cpuinfo 得到机器的Hardware名,笔者手机是HTC G7,手机为bravo。 get_hardware_name(); snprintf(tmp, sizeof(tmp), "/init.%s.rc", hardware); //解析这个和机器相关的的配置文件,笔者的G7手机对应的文件为init.bravo.rc parse_config_file(tmp); /*解析完上述两个配置文件,会得到一些列的Action,下面两句代码将执行那些处于early-init阶段的Action,init将动作执行的时间划分为四个阶段early-init 、init、early-boot、boot 。由于有些动作必须在其它动作完成后才能执行,所以就有了先后之分,哪些动作属于哪个阶段由配置文件决定,后面会介绍配置文件的相关信息*/ action_for_each_trigger("early-init", action_add_queue_tail); drain_action_queue(); //创建利用Uevent与Linux内核交互的Socket INFO("device init\n"); device_fd = device_init(); //初始化和属性相关的资源 property_init(); // only listen for keychords if ro.debuggable is true //初始化 /dev/keychord设备 keychord_fd = open_keychord(); 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); /*INIT_IMAGE_FILE 定义为“/initlogo.rle”,下面这个函数将加载这个文件作为系统的开机画面,注意,它不是开机动画控制程序bootanimation加载的开机动画文件,如果加载 initlogo.rle文件失败(可能是没有这个文件),则会打开 /dev/ty0设备,并会输出“ANDROID” 字样作为开机动画,在模拟器上看到的开机动画就是它*/ 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); 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 函数设置属性项,一个属性项包括属性名和属性值。 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 */ //执行位于init阶段的过程 action_for_each_trigger("init", action_add_queue_tail); drain_action_queue(); /* read any property files on system or data and * fire up the property service. This must happen * after the ro.foo properties are set above so * that /data/local.prop cannot interfere with them. */ //启动属性服务 property_set_fd = start_property_service(); /* create a signalling mechanism for the sigchld handler */ //调用socketpair函数创建两个已经connect好的socket。Socketpair是Linux系统调用。不熟悉的可以使用 man socketpair 查询相关信息 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; } /* execute all the boot actions to get us started */ //执行文件中的early-boot和boot阶段的动作。 action_for_each_trigger("early-boot", action_add_queue_tail); action_for_each_trigger("boot", action_add_queue_tail); drain_action_queue(); /* run all property triggers based on current state of the properties */ queue_all_property_triggers(); drain_action_queue(); /* enable property triggers */ property_triggers_enabled = 1; //init关注来自四个方面的事情 ufds[0].fd = device_fd; //device_fd用于监听来自内核的Uevent事件 ufds[0].events = POLLIN; ufds[1].fd = property_set_fd; //property_set_fd用于监听来自属性服务器的事件 ufds[1].events = POLLIN; ufds[2].fd = signal_recv_fd; //signal_recv_fd由socketpair创建,它的时间来自于另一个socket ufds[2].events = POLLIN; fd_count = 3; if (keychord_fd > 0) { ufds[3].fd = keychord_fd; //如果keychord设备初始化成功,则init也会关注来自这个设备的事件 ufds[3].events = POLLIN; fd_count++; } else { ufds[3].events = 0; ufds[3].revents = 0; } #if BOOTCHART // Boot chart 是一个小工具,它能对系统的性能进行分析,并且生成系统启动过程的图表,以提供一些有价值的信息,而这些信息的作用是帮助提升系统的启动速度。 bootchart_count = bootchart_init(); if (bootchart_count < 0) { ERROR("bootcharting init failure\n"); } else if (bootchart_count > 0) { NOTICE("bootcharting started (period=%d ms)\n", bootchart_count*BOOTCHART_POLLING_MS); } else { NOTICE("bootcharting ignored\n"); } #endif //从此init将进入一个无限循环。 for(;;) { int nr, i, timeout = -1; for (i = 0; i < fd_count; i++) ufds[i].revents = 0; //在循环中执行动作 drain_action_queue(); restart_processes(); //重启那些已经死去的进程 if (process_needs_restart) { timeout = (process_needs_restart - gettime()) * 1000; if (timeout < 0) timeout = 0; } #if BOOTCHART //和 BOOTCHART 相关 if (bootchart_count > 0) { if (timeout < 0 || timeout > BOOTCHART_POLLING_MS) timeout = BOOTCHART_POLLING_MS; if (bootchart_step() < 0 || --bootchart_count == 0) { bootchart_finish(); bootchart_count = 0; } } #endif //调用PULL等待一些事情的发生 nr = poll(ufds, fd_count, timeout); if (nr <= 0) continue; //ufds[2] 保存的是signal_recv_fd,用于接收来自socket的消息。 if (ufds[2].revents == POLLIN) { /* we got a SIGCHLD - reap and restart as needed */ /*有一个子进程去世,init要处理这个事情*/ read(signal_recv_fd, tmp, sizeof(tmp)); while (!wait_for_one_process(0)) ; continue; } if (ufds[0].revents == POLLIN) handle_device_fd(device_fd); //处理Uevent事件 if (ufds[1].revents == POLLIN) handle_property_set_fd(property_set_fd); //处理属性服务其事件 if (ufds[3].revents == POLLIN) handle_keychord(keychord_fd); //处理keychord事件 } return 0; }
从上面的代码可以得到,init的工作任务很重,上面的代码省略了不少,可结果还是很长,从上面分析的结果来看,可将init的工作流程精简为以下四点:
1) 解析两个配置文件,我们将分析其中一个init.rc 文件。
2) 执行各个阶段的动作,创建zygote就是在其中某个阶段完成的。
3) 调用property_init ,初始化属性相关的资源,并且通过property_start_service启动属性服务。
4) Init进入一个无限循环,并且等待一些事情的发生。重点可关注init如何处理来自socket和来自属性服务的相关事情。
文档参考:
整理抄录自 — 《深入理解Android卷1》,邓凡平著。
相关文章推荐
- 深入理解init_4``````````init控制service(基于Android2.2,代码源自Google)
- 深入理解init_2-----解析配置文件init.rc(基于Android 2.2,代码源于Google)
- 深入理解init_3 --------- 解析Zygote 的service(基于源码2.2,代码源自Google)
- 深入理解init_5-----属性服务(基于Android 2.2,代码源自Google)
- AMS分析--基于深入理解android(3)
- Android2.2源码init机制分析
- AMS分析--基于深入理解android(4)
- 【Android开发】深入理解硬盘缓存类DiskLruCache:源码分析
- 【Android 开发】深入理解内存缓存类LruCache:源码分析
- 深入理解Android事件分发机制之源码分析
- AMS分析--基于深入理解android(1)
- Android深入源码分析理解Aidl整体调用流程(雷惊风)
- AMS分析--基于深入理解android(2)
- 深入Android内核——Android init.c源码深入分析
- Android源码分析-深入理解setContentView方法
- 深入理解Spark ML:基于ALS矩阵分解的协同过滤算法与源码分析
- 基于android中读取assets目录下a.txt文件并进行解析的深入分析
- 对象参数深入分析jQuery.prototype.init选择器源码 Strut2教程-java教程
- 浅析android锁屏开机绘制流程(基于android4.0源码分析)