|
|
|
@ -48,11 +48,15 @@ init是第一个进程,也叫root进程或所有进程的父进程。在此阶
|
|
|
|
|
|
|
|
|
|
##### init进程第一阶段
|
|
|
|
|
|
|
|
|
|
1. 依据第一个参数进行跳转,并设置环境变量
|
|
|
|
|
1. 依据程序名进行跳转,就是先决定要跑哪个程序
|
|
|
|
|
|
|
|
|
|
如果第1个参数是ueventd,则执行`ueventd_main`,进行设备节点的创建和权限设定。
|
|
|
|
|
argv[0]是当前运行程序的名称,当前运行程序的名称有可能是init, modprobe, uenventd或watchdogd。其中,modprobe、uenventd和watchdogd都是指向init的软链接。
|
|
|
|
|
|
|
|
|
|
如果第1个参数是watchdogd,则执行`watchdogd_main`,用于系统出问题时重启系统。
|
|
|
|
|
如果当前运行的程序名是modprobe,则执行`modprobe_main`,应该是用来加载内核模块的。
|
|
|
|
|
|
|
|
|
|
如果当前运行的程序名是ueventd,则执行`ueventd_main`,进行设备节点的创建和权限设定。
|
|
|
|
|
|
|
|
|
|
如果当前运行的程序名是watchdogd,则执行`watchdogd_main`,用于系统出问题时重启系统。
|
|
|
|
|
|
|
|
|
|
如果REBOOT_BOOTLOADER_ON_PANIC,则`install_reboot_signal_handlers`,将一些信号的标志设置为SA_RESTART,一旦监听到这些信号则执行重启操作。
|
|
|
|
|
|
|
|
|
@ -60,9 +64,9 @@ init是第一个进程,也叫root进程或所有进程的父进程。在此阶
|
|
|
|
|
|
|
|
|
|
由于INIT_SECOND_STAGE是在is_first_stage里定义的,这就确保了is_first_stage只能执行一次。
|
|
|
|
|
|
|
|
|
|
2. 挂载文件系统并创建目录
|
|
|
|
|
2. 挂载文件系统并创建目录,Linux需要有这些文件系统才能运行
|
|
|
|
|
|
|
|
|
|
`umask(0)`用于清空文件权限。
|
|
|
|
|
`umask(0)`用于清空文件默认权限。这样才能保证后面代码里对文件权限的设置生效了,否则有可能被umask()的设置给屏蔽掉。
|
|
|
|
|
|
|
|
|
|
在根目录上建立initramdisk所需的基本文件系统(文件系统的其它部分是在随后的rc文件里建立的)。
|
|
|
|
|
|
|
|
|
@ -74,7 +78,7 @@ init是第一个进程,也叫root进程或所有进程的父进程。在此阶
|
|
|
|
|
|
|
|
|
|
3. 初始化日志输出,挂载分区设备
|
|
|
|
|
|
|
|
|
|
InitKernelLogging用于初始化日志系统,它首先把输入输出重定向到/sys/fs/selinux/null,然后调用InitLogging初始化日志系统。
|
|
|
|
|
InitKernelLogging用于初始化日志系统,它首先把stdin,stdout,stderr重定向到/sys/fs/selinux/null,这样printf或其它打印的函数都输出不了,就不会引发异常了(因为在这个阶段,printf函数是不能用的,会引发错误)。然后调用InitLogging初始化日志系统,这是把init进程的log打印到内核printk的log buffer,因为这个时候没有shell,不能进行通用的log输出,借助内核的功能是较好的选择。
|
|
|
|
|
|
|
|
|
|
DoFirstStageMount用于初始化特定设备并挂载。
|
|
|
|
|
|
|
|
|
@ -104,11 +108,11 @@ init是第一个进程,也叫root进程或所有进程的父进程。在此阶
|
|
|
|
|
|
|
|
|
|
keyctl_get_keyring_ID用于初始化进程会话密钥。KEY_SPEC_SESSION_KEYRING表示获取当前进程的会话密钥,1表示如获取不到则创建一个。
|
|
|
|
|
|
|
|
|
|
在`/dev`目录下创建文件`.booting`,表示booting正在进行中(可以在init.rc里看到,当booting结束会删除此文件)。
|
|
|
|
|
在`/dev`目录下创建文件`.booting`,这个文件仅仅起到标志的作用,表示系统正在启动(可以在init.rc里看到,当booting结束会删除此文件)。
|
|
|
|
|
|
|
|
|
|
property_init用于初始化属性系统,并从指定文件读取属性。
|
|
|
|
|
|
|
|
|
|
> property_init调用__system_property_area_init实现其功能。
|
|
|
|
|
> property_init调用__system_property_area_init实现其功能。详见bionic/libc/include/sys和bionic/libc/bionic/system_properties.cpp
|
|
|
|
|
|
|
|
|
|
process_kernel_dt和process_kernel_cmdline分别处理dt(设备树)和命令行属性,设备树中参数的优先级总是高于命令行。
|
|
|
|
|
|
|
|
|
@ -166,23 +170,24 @@ init是第一个进程,也叫root进程或所有进程的父进程。在此阶
|
|
|
|
|
|
|
|
|
|
bootmode是个全局静态变量,用来标示启动模式,android的启动模式分为正常开机流程和关机充电流程。
|
|
|
|
|
|
|
|
|
|
3. 触发所有的事件并不断监听新的事件
|
|
|
|
|
##### init进程第二阶段之服务处理
|
|
|
|
|
|
|
|
|
|
之前的工作是建立各种各样的事件,并没有去触发。只有在本while循环里才真正的触发所有的事件,并用epoll不断监听新的事件。
|
|
|
|
|
init进程作为1号进程是永不退出的,这里的while(true)循环就保证了这一点。
|
|
|
|
|
|
|
|
|
|
如要执行关机命令,则通过`HandlePowerctlMessage()`来执行之。
|
|
|
|
|
之前的工作是建立各种各样的事件,并没有去触发。只有在本while循环里才真正的触发所有的事件,并用epoll不断监听新的事件。
|
|
|
|
|
|
|
|
|
|
当没有属性设置且没有Server开始运行时,才通过`am.ExecuteOneCommand()`从current_executing_actions\_列表里执行一个command,由于外部是一个无限循环,所以current_executing_actions\_列表里的每个command都会执行一遍。这样的条件设定是为了保证属性设置和Server启动的完整性。
|
|
|
|
|
1. 如要执行关机命令,则通过`HandlePowerctlMessage()`来执行之。
|
|
|
|
|
|
|
|
|
|
当没有属性设置且没有Server开始运行时:
|
|
|
|
|
2. 当没有属性设置且没有Server开始运行时,才通过`am.ExecuteOneCommand()`从current_executing_actions\_列表里执行一个command,由于外部是一个无限循环,所以current_executing_actions\_列表里的每个command都会执行一遍。这样的条件设定是为了保证属性设置和Server启动的完整性。
|
|
|
|
|
|
|
|
|
|
3. 当没有属性设置且没有Server开始运行时:
|
|
|
|
|
- 如系统不是正在关机,执行`restart_processes()`来判断当前Server是否需要重启,如需重启则执行重启操作。
|
|
|
|
|
- 如有进程需要重启,设置`epoll_timeout_ms`的值
|
|
|
|
|
- 如有其它command需要执行,则将`epoll_timeout_ms`设置为0以立即唤醒。
|
|
|
|
|
|
|
|
|
|
`epoll_wait`用于监听事件的产生,参数`epoll_fd`是由前面的`epoll_create1()`创建的epoll的文件描述符,参数`ev`用于保存事件信息,参数`epoll_timeout_ms`是等待时间
|
|
|
|
|
4. `epoll_wait`用于监听事件的产生,参数`epoll_fd`是由前面的`epoll_create1()`创建的epoll的文件描述符,参数`ev`用于保存事件信息,参数`epoll_timeout_ms`是等待时间
|
|
|
|
|
|
|
|
|
|
如未监听到事件,则向log系统添加一条监听失败的信息;如监听到事件,则直接执行`ev.data.ptr()`(即epoll_ctl注册时的回调函数)。
|
|
|
|
|
5. 如未监听到事件,则向log系统添加一条监听失败的信息;如监听到事件,则直接执行`ev.data.ptr()`(即epoll_ctl注册时的回调函数)。
|
|
|
|
|
|
|
|
|
|
#### step5: Zygote
|
|
|
|
|
|
|
|
|
@ -282,7 +287,7 @@ Zygote启动系统服务,即ZygoteInit类里的startSystemServer方法。
|
|
|
|
|
|
|
|
|
|
系统服务运行起来后,引导完成,此时会发送开机启动广播`ACTION_BOOT_COMPLETED`
|
|
|
|
|
|
|
|
|
|
#### 附录:Android Init Language
|
|
|
|
|
#### 附录:init语言
|
|
|
|
|
|
|
|
|
|
详见:system/core/init/README.md
|
|
|
|
|
|
|
|
|
@ -290,8 +295,10 @@ Zygote启动系统服务,即ZygoteInit类里的startSystemServer方法。
|
|
|
|
|
|
|
|
|
|
从整体来看,一个rc文件是由若干个段组成的。一个段只能是Actions或Server其中之一:
|
|
|
|
|
|
|
|
|
|
- Actions,其中Commands和trigger是对Actions的补充。
|
|
|
|
|
- Servers,其中Options是对Servers的补充。
|
|
|
|
|
- Actions,由trigger和它下面的一堆Commands组成。
|
|
|
|
|
- Servers,由一堆Options组成。
|
|
|
|
|
|
|
|
|
|
注意:Actions和Servers不能重名。如果重名了,后出现的定义将会作为错误而忽略掉。
|
|
|
|
|
|
|
|
|
|
##### .rc文件
|
|
|
|
|
|
|
|
|
@ -301,7 +308,7 @@ init language用在.rc文件中,它可能出现在系统中的多个位置。
|
|
|
|
|
|
|
|
|
|
##### Actions
|
|
|
|
|
|
|
|
|
|
Actions就是触发器加一串命令,触发器用来决定是否执行一个Action。如果满足了触发器的条件且action还不在运行队列里,则这个action会被加到运行队列的尾部。
|
|
|
|
|
Actions就是触发器(trigger)加一串命令(commands),触发器用来决定是否执行一个Action。如果满足了触发器的条件且action还不在运行队列里,则这个action会被加到运行队列的尾部。
|
|
|
|
|
|
|
|
|
|
运行的格式如下:
|
|
|
|
|
|
|
|
|
@ -332,26 +339,49 @@ name是程序名,pathname是程序所在的路径,argument是程序的参数
|
|
|
|
|
Options是Services的修改器,它们影响init进程如何运行service以及何时运行service。
|
|
|
|
|
|
|
|
|
|
- console,此服务需要一个console。默认设备为`/dev/console`。也可自己指定,如`console /dev/tty0`,其中`/dev/`是可忽略的,即等同于`console tty0`。
|
|
|
|
|
|
|
|
|
|
- ctitical,此服务是一个设备关键服务。如果在4分钟内退出4次,设备将会重启进入recovery模式。
|
|
|
|
|
- disabled,此服务无法通过它的类来启动,必须通过它的名字来启动。
|
|
|
|
|
|
|
|
|
|
- disabled,此服务无法通过它所属的类型(class)来启动,必须通过它的名字来启动。这就意味着启动一个类型的服务时这种服务是不会被启动的,也就是它被disable掉了。
|
|
|
|
|
|
|
|
|
|
- setenv,在启动的进程里设置环境变量。
|
|
|
|
|
- socket,创建一个unix域socket,并将它的fd传给启动的进程。
|
|
|
|
|
|
|
|
|
|
- socket \<name> \<type> \<perm> [\<user> [\<group>]]
|
|
|
|
|
|
|
|
|
|
创建一个unix域socket,它的名字叫/dev/socket/\<name>,并将它的fd传给启动的进程。
|
|
|
|
|
|
|
|
|
|
- file,打开一个文件并将它的fd传给启动的进程。
|
|
|
|
|
- user,在执行服务前改变用户名。
|
|
|
|
|
|
|
|
|
|
- user,在执行服务前改变用户(user),默认的用户是root。
|
|
|
|
|
|
|
|
|
|
- group,在执行服务前改变组名。
|
|
|
|
|
|
|
|
|
|
- capabilities,在执行服务的时候设置capabilities.
|
|
|
|
|
|
|
|
|
|
- seclabel,在执行服务前改变为seclabel。
|
|
|
|
|
|
|
|
|
|
- oneshot,当服务退出后不要重启它。
|
|
|
|
|
- class,为服务指定所属的类。在同一个类里的服务可以同时打开或关闭。如不使用此选项明确指定服务所属的类,则默认在default类里。
|
|
|
|
|
|
|
|
|
|
- class,为服务指定所属的类型。在同一个类型里的服务可以同时打开或关闭。如不使用此选项明确指定服务所属的类型,则默认在default里。
|
|
|
|
|
|
|
|
|
|
- animation,将会包含所有启动和关闭animation所必须的服务。
|
|
|
|
|
|
|
|
|
|
- onrestart当服务重启的时候执行一条命令。
|
|
|
|
|
|
|
|
|
|
- writepid,当子进程forks的时候将其pid写到给定文件中。
|
|
|
|
|
|
|
|
|
|
- priority,调度service进程的优先级。
|
|
|
|
|
|
|
|
|
|
- namespace,当forking一个服务的时候,进入一个新的PID或挂载namespace。
|
|
|
|
|
|
|
|
|
|
- oom_score_adjust,设置子进程的变量值。
|
|
|
|
|
|
|
|
|
|
- memcg.swappiness,设置子进程的变量值。
|
|
|
|
|
|
|
|
|
|
- memcg.soft_limit_in_bytes,设置子进程的变量值。
|
|
|
|
|
|
|
|
|
|
- memcg.limit_in_bytes,设置子进程的变量值。
|
|
|
|
|
|
|
|
|
|
- shutdown,设置关机的时候此service进程的行为。默认是关机进程通过传递SIGTERM和SIGKILL来杀死此服务。
|
|
|
|
|
|
|
|
|
|
##### Triggers
|
|
|
|
@ -477,10 +507,7 @@ Triggers就是个字符串,用来匹配特定事件和触发一个action。分
|
|
|
|
|
> restarting, otherwise, it just starts the service.
|
|
|
|
|
|
|
|
|
|
`restorecon <path> [ <path>\* ]`
|
|
|
|
|
> Restore the file named by _path_ to the security context specified
|
|
|
|
|
> in the file\_contexts configuration.
|
|
|
|
|
> Not required for directories created by the init.rc as these are
|
|
|
|
|
> automatically labeled correctly by init.
|
|
|
|
|
> 将<path>所代表的文件恢复到安全上下文里,这个安全上下文是在file_contexts配置中指定的。init.rc创建的目录不需要执行这个命令,因为init会自动正确地标记它们。
|
|
|
|
|
|
|
|
|
|
`restorecon_recursive <path> [ <path>\* ]`
|
|
|
|
|
> Recursively restore the directory tree named by _path_ to the
|
|
|
|
@ -502,7 +529,7 @@ Triggers就是个字符串,用来匹配特定事件和触发一个action。分
|
|
|
|
|
> Set the rlimit for a resource.
|
|
|
|
|
|
|
|
|
|
`start <service>`
|
|
|
|
|
> Start a service running if it is not already running.
|
|
|
|
|
> 如果一个服务还没有运行则启动它。
|
|
|
|
|
> Note that this is _not_ synchronous, and even if it were, there is
|
|
|
|
|
> no guarantee that the operating system's scheduler will execute the
|
|
|
|
|
> service sufficiently to guarantee anything about the service's status.
|
|
|
|
@ -516,7 +543,7 @@ Triggers就是个字符串,用来匹配特定事件和触发一个action。分
|
|
|
|
|
|
|
|
|
|
`stop <service>`
|
|
|
|
|
|
|
|
|
|
> Stop a service from running if it is currently running.
|
|
|
|
|
> 如果一个服务正在运行则关闭它。
|
|
|
|
|
|
|
|
|
|
`swapon_all <fstab>`
|
|
|
|
|
> Calls fs\_mgr\_swapon\_all on the given fstab file.
|
|
|
|
@ -553,9 +580,7 @@ Triggers就是个字符串,用来匹配特定事件和触发一个action。分
|
|
|
|
|
> immediately.
|
|
|
|
|
|
|
|
|
|
`write <path> <content>`
|
|
|
|
|
> Open the file at _path_ and write a string to it with write(2).
|
|
|
|
|
> If the file does not exist, it will be created. If it does exist,
|
|
|
|
|
> it will be truncated. Properties are expanded within _content_.
|
|
|
|
|
> 把<content>写到<path>里。<content>里的属性将会展开。
|
|
|
|
|
|
|
|
|
|
##### Imports
|
|
|
|
|
|
|
|
|
@ -564,4 +589,6 @@ Triggers就是个字符串,用来匹配特定事件和触发一个action。分
|
|
|
|
|
#### 参考文档
|
|
|
|
|
|
|
|
|
|
- [foxleezh/AOSP](https://github.com/foxleezh/AOSP)
|
|
|
|
|
- 《深入理解Android卷1》邓凡平
|
|
|
|
|
- 《深入理解Android卷1》邓凡平
|
|
|
|
|
- [freshui/Android init进程分析](https://blog.csdn.net/freshui/article/details/5747199)
|
|
|
|
|
- [freshui/Android init脚本解析](https://blog.csdn.net/freshui/article/details/5772564)
|