网上关于init.c的分析文章已经很多了,由于作者知识背景、着重点和描述角度的不同,写出来的文章让人觉得眼前一亮的精华点也不同。出于汇总精华、强化理解的目的,这里也写上一篇关于init.c的理解。本文基于android源码版本4.4.2_r1。
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 |
// 文件位置:\system\core\init\init.c int main( int argc, char **argv) { int fd_count = 0; struct pollfd ufds[ 4]; char *tmpdev; char* debuggable; char tmp[ 32]; int property_set_fd_init = 0; int signal_fd_init = 0; int keychord_fd_init = 0; bool is_charger = false; // 如果第一个参数是ueventd,则执行ueventd_main if (!strcmp(basename(argv[ 0]), "ueventd")) return ueventd_main(argc, argv); // 如果第一个参数是watchdogd,则执行watchdogd_main if (!strcmp(basename(argv[ 0]), "watchdogd")) return watchdogd_main(argc, argv); // 清除系统默认权限,确保新创建目录的权限由mkdir设定。 /* clear the umask */ umask( 0); // 相当于chmod 777 /* 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); /* int mount(const char *source, const char *target, const char *filesystemtype, unsigned long mountflags, const void *data); source :设备 /dev/sda等; target :挂载点/mnt/usb等; filesystemtype:"minix","ext2", "msdos", "proc", "nfs", "iso9660" ,“vfat”etc.; mountflags:flag值; public enum MountFlags : ulong { MS_RDONLY = 1, // Mount read-only. MS_NOSUID = 2, // Ignore suid and sgid bits. MS_NODEV = 4, // Disallow access to device special files. MS_NOEXEC = 8, // Disallow program execution. MS_SYNCHRONOUS = 16, // Writes are synced at once. MS_REMOUNT = 32, // Alter flags of a mounted FS. MS_MANDLOCK = 64, // Allow mandatory locks on an FS. S_WRITE = 128, // Write on file/directory/symlink. S_APPEND = 256, // Append-only file. S_IMMUTABLE = 512, // Immutable file. MS_NOATIME = 1024, // Do not update access times. MS_NODIRATIME = 2048, // Do not update directory access times. MS_BIND = 4096, // Bind directory at different place. }; // End En data:例如:”codepage=936,iocharset=cp936“。 */ /* tmpfs的详细解释可参见\kernel\Documentation\filesystems\tmpfs.txt文件。 这里摘录一部分做概要介绍: Tmpfs is a file system which keeps all files in virtual memory. Everything in tmpfs is temporary in the sense that no files will be created on your hard drive. If you unmount a tmpfs instance, everything stored therein is lost. 备注:android的匿名共享内存(ashmem)对应于tmpfs中文件。 具体可参见ashmem的mmap操作。 */ // 将tmpfs文件系统以tmpfs类型挂载到/dev目录下 mount( "tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"); mkdir( "/dev/pts", 0755); mkdir( "/dev/socket", 0755); // 将devpts文件系统以devpts类型挂载到/dev/pts目录下 mount( "devpts", "/dev/pts", "devpts", 0, NULL); /* proc的详细解释可参见\kernel\Documentation\filesystems\proc.txt文件。 这里摘录一部分做概要介绍: The proc file system acts as an interface to internal data structures in the kernel. It can be used to obtain information about the system and to change certain kernel parameters at runtime (sysctl). 1 Collecting System Information 1.1 Process-Specific Subdirectories 1.2 Kernel data 1.3 IDE devices in /proc/ide 1.4 Networking info in /proc/net 1.5 SCSI info 1.6 Parallel port info in /proc/parport 1.7 TTY info in /proc/tty 1.8 Miscellaneous kernel statistics in /proc/stat 1.9 Ext4 file system parameters 2 Modifying System Parameters 3 Per-Process Parameters 3.1 /proc/ score 3.2 /proc/ 3.3 /proc/ 3.4 /proc/ 3.5 /proc/ 3.6 /proc/ */ // 将proc文件系统以proc类型挂载到/proc目录下 mount( "proc", "/proc", "proc", 0, NULL); /* sysfs的详细解释可参见\kernel\Documentation\filesystems\sysfs.txt文件。 这里摘录一部分做概要介绍: sysfs is a ram-based filesystem initially based on ramfs. It provides a means to export kernel data structures, their attributes, and the linkages between them to userspace. sysfs is tied inherently to the kobject infrastructure. For every kobject that is registered with the system, a directory is created for it in sysfs. That directory is created as a subdirectory of the kobject's parent, expressing internal object hierarchies to userspace. Top-level directories in sysfs represent the common ancestors of object hierarchies; i.e. the subsystems the objects belong to. Attributes can be exported for kobjects in the form of regular files in the filesystem. Sysfs forwards file I/O operations to methods defined for the attributes, providing a means to read and write kernel attributes. 备注:sysfs把连接在系统上的设备和总线组织成为一个分级的文件,使得它们可以在用户空间存取。 */ // 将sysfs文件系统以sysfs类型挂载到/sys目录下 mount( "sysfs", "/sys", "sysfs", 0, NULL); // 创建文件"/dev/.booting"来表示目前正处于启动中的状态 /* indicate that booting is in progress to background fw loaders, etc */ close(open( "/dev/.booting", O_WRONLY | O_CREAT, 0000)); /* 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. */ // 屏蔽标准的输入输出, // 即将与进程相关的标准输入(0),标准输出(1),标准错误输出(2)定向到/dev/__null__。 open_devnull_stdio(); // 初始化内核log系统,创建/dev/__kmsg__ klog_init(); // 属性存储空间初始化。打开/dev/__properties__,然后mmap一块大小为(128 * 1024)的内存出来 property_init(); // 从/proc/cpuinfo中获取机器硬件名称 get_hardware_name(hardware, &revision); // 调用import_kernel_cmdline从/proc/cmdline文件中读取格式为: // 然后调用export_kernel_boot_props()在属性系统中设置内核启动属性。 // selinux安全机制初始化 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(全称应该是restore context),标记/dev、/dev/socket、/dev/__properties__、/sys目录的 // 安全上下文 restorecon( "/dev"); restorecon( "/dev/socket"); restorecon( "/dev/__properties__"); restorecon_recursive( "/sys"); // 判断当前启动模式是否是充电模式 is_charger = !strcmp(bootmode, "charger"); INFO( "property init\n"); // 非充电模式下,加载default.prop文件中的属性到属性系统中 if (!is_charger) property_load_boot_defaults(); INFO( "reading config file\n"); // 解析/init.rc初始化文件 init_parse_config_file( "/init.rc"); // 触发/init.rc中的early-init action,通过action_add_queue_tail将之添加到可执行队列action_queue中。 // 这个action主要目的是通过early-init启动ueventd服务,这个服务负责uevent(user space event)的处理, // uevent是内核向用户空间发出的一个事件通知,使应用程序能够有机会对该event做出反应。 action_for_each_trigger( "early-init", action_add_queue_tail); /* 备注:buildin意指内置,即不在init.rc、init.XXX.rc等rc中配置的action */ // 创建名为wait_for_coldboot_done的action并添加到链表action_queue和action_list中。 // android 冷过程结束后会生成dev/.coldboot_done文件,wait_for_coldboot_done这个action会等待 // dev/.coldboot_done文件的生成,等待时长为5s。当然这个action不会阻塞android的冷启动过程, // 它会每查询一次就会休眠0.1s,直到冷启动结束。 queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done"); // 创建名为mix_hwrng_into_linux_rng的action并添加到链表action_queue和action_list中。 queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng"); // 创建名为keychord_init的action并添加到链表action_queue和action_list中。 // keychord是组合按键设备,Android暂时还不支持keychord机制,keychord机制就是在init.rc文件中为每个 // 服务配置组合键,在服务解析时为指定服务设置相应的键码值。 queue_builtin_action(keychord_init_action, "keychord_init"); // 创建名为console_init的action并添加到链表action_queue和action_list中。 // console_init // 1.如果/proc/cmdline指定了控制台终端,那么优先使用这个控制台,如果没有指定,那么将使用默认 // 控制台终端/dev/console。 // 2.加载开机图片,参考load_565rle_image函数 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); /* skip mounting filesystems in charger mode */ if (!is_charger) { action_for_each_trigger( "early-fs", action_add_queue_tail); action_for_each_trigger( "fs", action_add_queue_tail); action_for_each_trigger( "post-fs", action_add_queue_tail); action_for_each_trigger( "post-fs-data", action_add_queue_tail); } /* Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random * wasn't ready immediately after wait_for_coldboot_done */ queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng"); // 读取属性文件,并设置相关属性。 queue_builtin_action(property_service_init_action, "property_service_init"); // 创建套接字对,以便init进程在收到子进程终止信号(SIGCHLD)时调用sigchld_handler queue_builtin_action(signal_init_action, "signal_init"); // 检查属性socket句柄(property_set_fd)及信号句柄(signal_recv_fd)是否安装成功,删除/dev/.booting queue_builtin_action(check_startup_action, "check_startup"); if (is_charger) { action_for_each_trigger( "charger", action_add_queue_tail); } else { action_for_each_trigger( "early-boot", action_add_queue_tail); action_for_each_trigger( "boot", action_add_queue_tail); } /* run all property triggers based on current state of the properties */ // 根据当前property值来触发该property对应的action queue_builtin_action(queue_property_triggers_action, "queue_property_triggers"); #if BOOTCHART queue_builtin_action(bootchart_init_action, "bootchart_init"); #endif for(;;) { int nr, i, timeout = - 1; // 检查action_queue列表是否为空。如果不为空的话,那么init进程就会将保存在列表头中的action移除, // 并且执行这个被移除的action中的command。一次循环中执行一个command execute_one_command(); // 重启因异常退出而又需要重新启动的service。 // 在启动脚本/init.rc中,我们可以指定一个进程在退出之后能否自动重新启动。通过遍历service_list, // 找到flag为需要重启(SVC_RESTARTING)的service,调用restart_service_if_needed启动它。 restart_processes(); /* struct pollfd { int fd; // 需要监控的文件描述符 short events; // 监控fd上的事件,由调用方设置 short revents; // fd上发生的事件,由返回方设置 }; */ if (!property_set_fd_init && get_property_set_fd() > 0) { // 监听来自property service的事件。 // 当调用函数property_set来改变一个系统属性值时,系统就会通过一个socket(通过调用函数 // get_property_set_fd可以获得它的文件描述符)来向init进程发送一个属性值改变事件通知。 // init进程接收到这个属性值改变事件之后,就会调用函数handle_property_set_fd来进行相应的处理。 ufds[fd_count].fd = get_property_set_fd(); ufds[fd_count].events = POLLIN; ufds[fd_count].revents = 0; fd_count++; property_set_fd_init = 1; } if (!signal_fd_init && get_signal_fd() > 0) { // 监听子进程异常退出(SIGCHLD)信号 // 在Linux内核中,如果父进程不等待子进程结束就退出,那么当子进程结束的时候,就会变成一个僵尸进程, // 从而占用系统的资源。为了回收这些僵尸进程,init进程会安装一个SIGCHLD信号接收器。 // 当那些父进程已经退出了的子进程退出的时候,内核就会发出一个SIGCHLD信号给init进程。 // init进程可以通过一个socket(通过调用函数get_signal_fd可以获得它的文件描述符)来将接收到的 // SIGCHLD信号读取回来,并且调用函数handle_signal来对接收到的SIGCHLD信号进行处理, // 即回收那些已经变成了僵尸的子进程。 // handle_signal会通过进程服务列表(service_list)检查进程的设置选项,若选项没有配置oneshot(SVC_ONE_SHOT), // 则设置重启选项(SVC_RESTARTING),oneshot选项定义在init.rc文件的service部分中; // 若进程带有oneshot选项,进程终止时不会被重启。 ufds[fd_count].fd = get_signal_fd(); ufds[fd_count].events = POLLIN; ufds[fd_count].revents = 0; fd_count++; signal_fd_init = 1; } if (!keychord_fd_init && get_keychord_fd() > 0) { // 处理一种称为“chorded keyboard”的键盘输入事件。Android当前未使用。 // 这种类型为chorded keyboard”的键盘设备通过不同的铵键组合来描述不同的命令或者操作, // 它对应的设备文件为/dev/keychord。 ufds[fd_count].fd = get_keychord_fd(); ufds[fd_count].events = POLLIN; ufds[fd_count].revents = 0; fd_count++; keychord_fd_init = 1; } // 修正死去service重启需要等待的timeout if (process_needs_restart) { // 系统当前时间gettime()已经大于指定的重启时间process_needs_restart,此时有进程需要重启,poll无需等待 timeout = (process_needs_restart - gettime()) * 1000; if (timeout < 0) timeout = 0; } // 如果有action需要处理,设置timeout为0,即poll不阻塞, // 这样init进程就可以循环执行队列action_queue中的Action if (!action_queue_empty() || cur_action) timeout = 0; #if 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 // 监控事件的发生 // 系统调用poll在监控句柄池时,如果超时时间到了或者有事件发生时,才会返回。 // 如果timeout=-1时,只有事件发生才会返回; // 如果timeout=0时,poll不阻塞,直接返回。 // poll返回ufds中revents不为0的fd个数;因没有任何事件发生而超时,返回0;失败时,返回-1。 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(); // 处理property事件 else if (ufds[i].fd == get_keychord_fd()) handle_keychord(); // 处理keychord事件。Android当前未使用 else if (ufds[i].fd == get_signal_fd()) handle_signal(); // 处理signal事件 } } } return 0; } |