Android源码阅读笔记1 - init.c的main函数分析

网上关于init.c的分析文章已经很多了,由于作者知识背景、着重点和描述角度的不同,写出来的文章让人觉得眼前一亮的精华点也不同。出于汇总精华、强化理解的目的,这里也写上一篇关于init.c的理解。本文基于android源码版本4.4.2_r1。


 C Code 
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"0NULL);
    
/*
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/<pid>/oom_adj & /proc/<pid>/oom_score_adj - Adjust the oom-killer
                                score
  3.2   /proc/<pid>/oom_score - Display current oom-killer score
  3.3   /proc/<pid>/io - Display the IO accounting fields
  3.4   /proc/<pid>/coredump_filter - Core dump filtering settings
  3.5   /proc/<pid>/mountinfo - Information about mounts
  3.6   /proc/<pid>/comm  & /proc/<pid>/task/<tid>/comm
  
*/

// 将proc文件系统以proc类型挂载到/proc目录下    
    mount( "proc""/proc""proc"0NULL);
    
/*
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"0NULL);

// 创建文件"/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文件中读取格式为:<key> = <value> 的内核启动参数,
// 然后调用export_kernel_boot_props()在属性系统中设置内核启动属性。
    process_kernel_cmdline();

// 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;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值