本篇文章给大家谈谈Linux启动流程和服务管理(init和systemd),以及linux启动流程的介绍的知识点,同时本文还将给你拓展androidinit.rcinit.%PRODUCT%.rc解析
本篇文章给大家谈谈Linux 启动流程和服务管理 (init 和 systemd),以及linux启动流程的介绍的知识点,同时本文还将给你拓展android init.rc init.%PRODUCT%.rc 解析命令、Android 初始化语言 (init.*.rc、init.conf 文件格式)、Android 系统 init 进程启动及 init.rc 全解析、CentOS 7 巨大变动之 systemd 取代 SysV 的 Init等相关知识,希望对各位有所帮助,不要忘了收藏本站喔。
本文目录一览:- Linux 启动流程和服务管理 (init 和 systemd)(linux启动流程的介绍)
- android init.rc init.%PRODUCT%.rc 解析命令
- Android 初始化语言 (init.*.rc、init.conf 文件格式)
- Android 系统 init 进程启动及 init.rc 全解析
- CentOS 7 巨大变动之 systemd 取代 SysV 的 Init
Linux 启动流程和服务管理 (init 和 systemd)(linux启动流程的介绍)
目录
一:Linux 启动流程
init 和 Systemd 的区别
二:Linux 服务管理 (service,systemctl)
一:Linux 启动流程
Rhel6 启动过程:
Rhel7 启动过程:
GRUB2 相较于 GRUB 一代的提升:更健壮、可移植、更强大。支持 BIOS、EFI 和 OpenFirmware,支持 GPT 和 MBR 分区表。支持非 Linux 系统,如苹果 HFS 文件系统和 Windows 的 NTFS 文件系统
systemd 被设计用来改进 sysvinit 的缺点,它和 ubuntu 的 upstart 是竞争对手,预计会取代它们。
systemd 的目标是:尽可能启动更少进程;尽可能将更多进程并行启动。systemd 尽可能减少对 shell 脚本的依赖。传统 sysvinit 使用 inittab 来决定运行哪些 shell 脚本,大量使用 shell 脚本被认为是效率低下无法并行的原因。systemd 使用了 Linux 专属技术,不再顾及 POSIX 兼容。
init 和 Systemd 的区别
init:
- 一是启动时间长,init 是串行启动,只有前一个进程启动完,才会启动下一个进程
- 二是启动脚本复杂,Init 进程只是执行启动脚本,不管其他事情,脚本需要自己处理各种情况,这往往使得脚本变得很长
- 由 Linux 内核加载运行,位于 /sbin/init , 是系统中第一个进程,PID 永远为 1
对于支持 service 的程序,安装的时候,会自动的在 /etc/init.d 目录添加一个配置文件。当我们使用 service 控制程序时,比如执行开启 httpd 的服务:service httpd start 。那么我们的 service 就会开启 /etc/init.d/httpd 配置文件里面指向的 /usr/sbin/httpd 可执行文件
systemd:
- 按需启动服务,减少系统资源消耗。
- 尽可能并行启动进程,减少系统启动等待时间
- 由 Linx 内核加载运行,位于 /usr/lib/systemd/systemd ,是系统中第一个进程,PID 永远为 1
对于支持 systemd 的程序,安装的时候,会自动的在 /usr/lib/systemd/system 目录添加一个配置文件。当我们使用 systemctl 控制该程序时,比如执行开启 httpd 服务:systemctl start httpd.service 。那么我们的 systemctl 就会开启 httpd.service 配置里面指向的 /usr/sbin/httpd 可执行文件
如果我们想让该程序开机启动,我们可以执行命令 systemctl enable httpd,这个命令相当于在 /etc/systemd/system 目录添加一个软链接,指向 /usr/lib/systemd/system 目录下的 httpd.service 文件。这是因为开机时,Systemd
只执行 /etc/systemd/system
目录里面的配置文件。
Init 进程的配置文件
参数 | 说明 |
---|---|
/etc/init.d/ | 服务启动脚本配置文件存放目录 |
/etc/inittab | 默认运行级别配置文件 |
/etc/init/rcS.conf | 系统初始化配置文件 |
/etc/init/rc.conf | 各运行级别初始化的配置文件 |
/etc/init/rcS-sulogin.conf | 单用户模式启动 /sbin/sushell 环境的配置文件 |
/etc/init/control-alt-delete.conf | 终端下的 ctrl+alt+del 热键操作的配置文件 |
/etc/sysconfig/init | tty 终端的配置文件 |
/etc/init/start-ttys.conf | 配置 tty 终端的开启数量、设备文件 |
/etc/init/tty.conf 或 /etc/init/serial.conf | 控制 tty 终端的开启 |
Systemd 进程的配置文件
参数 | 说明 |
---|---|
/etc/systemd/system/default.target | 取代 /etc/inittab 文件配置,通常符号链接到 /lib/systemd/system/graphical.target |
/run/systemd/system/ | 系统执行过程中所产生的服务脚本所在目录 |
/etc/systemd/system/ | 里面存放着不同级别的开启自启服务 |
/usr/lib/systemd/system/ 和 /lib/systemd/system/ 和,两个文件完全一样,因为 lib 是 /usr/lib 的软链接 | 每个服务最主要的启动脚本设置,类似于之前的 /etc/init.d/ |
运行级别和说明
运行级别 | 说明 | Rehl 6/7 命令 | Rhel7 命令 |
---|---|---|---|
0 | 关机状态,使用该级别将会关机 | init 0 | poweroff |
1 | 系统救援模式,多用于系统维护 | init 1 | systemctl isolate rescue.target |
2 | 字符界面的多用户模式 (不可访问网络) | init 2 | systemctl isolate mutil-user.target |
3 | 字符界面的完整多用户模式,大多数服务器主机运行此级别 | init 3 | systemctl isolate mutil-user.target |
4 | 未分配使用 | init 4 | systemctl isolate mutil-user.target |
5 | 图形界面的多用户模式,提供了图形桌面操作环境 | init 5 | systemctl isolate graphical.target |
6 | 重新启动主机 | init 6 | reboot |
查看运行级别:
- runlevel : 显示切换前的运行级别 和当前运行级别 (6/7)
- systemctl get-default : 显示当前运行级别 (7)
永久设置开机模式
- systemctl set-default multi-user.target 开机默认为文本模式
- systemctl set-default graphical.target 开机默认为图形模式
- 修改 /etc/inittab 默认运行级别配置文件
二:Linux 服务管理 (service,systemctl)
Rhel6 用 service 和 chkconfig 来管理服务,它是 SystemV 架构下的一个工具。
Rhel7 是用 systemctl 来管理服务,它融合了之前的 service 和 chkconfig 的功能于一体。可以使用它永久性或只在当前会话中启用 / 禁用服务。systemctl 是 systemd 架构下的一个工具。
动作 | Rhel6 旧指令 | Rhel7 新指令 |
---|---|---|
启动某服务 | service httpd start | systemctl start httpd |
停止某服务 | service httpd stop | systemctl stop httpd |
重启某服务 | service httpd restart | systemctl restart httpd |
检查服务状态 | service httpd status | systemctl status httpd |
删除某服务 | chkconfig --del httpd | 停掉应用,删除其配置文件 |
使服务开机自启动 | chkconfig --level 5 httpd on | systemctl enable httpd |
使服务开机不自启动 | chkconfig --level 5 httpd off | systemctl disable httpd |
显示所有已启动的服务 | chkconfig --list | systemctl list-unit-files | grep enabled |
加入自定义服务 | chkconfig --add test | systemctl load test |
查询服务是否开机自启 | chkconfig --list | grep httpd | systemctl is-enabled httpd |
查看启动失败的服务 | systemctl --failed |
systemd 的一些常用命令:
列出所有可用单元 : systemctl list-unit-files
列出所有运行的单元: systemctl list-unit-files | grep enabled
列出所有可用服务: systemctl list-unit-files --type=service
列出所有运行的服务: systemctl list-unit-files --type=service | grep enabled
屏蔽 httpd 服务:systemctl mask httpd
android init.rc init.%PRODUCT%.rc 解析命令
Android Init Language
---------------------
The Android Init Language consists of four broad classes of statements,
which are Actions, Commands, Services, and Options.
All of these are line-oriented, consisting of tokens separated by
whitespace. The c-style backslash escapes may be used to insert
whitespace into a token. Double quotes may also be used to prevent
whitespace from breaking text into multiple tokens. The backslash,
when it is the last character on a line, may be used for line-folding.
Lines which start with a # (leading whitespace allowed) are comments.
Actions and Services implicitly declare a new section. All commands
or options belong to the section most recently declared. Commands
or options before the first section are ignored.
Actions and Services have unique names. If a second Action or Service
is declared with the same name as an existing one, it is ignored as
an error. (??? should we override instead)
Actions
-------
Actions are named sequences of commands. Actions have a trigger which
is used to determine when the action should occur. When an event
occurs which matches an action''s trigger, that action is added to
the tail of a to-be-executed queue (unless it is already on the
queue).
Each action in the queue is dequeued in sequence and each command in
that action is executed in sequence. Init handles other activities
(device creation/destruction, property setting, process restarting)
"between" the execution of the commands in activities.
Actions take the form of:
on <trigger>
<command>
<command>
<command>
Services
--------
Services are programs which init launches and (optionally) restarts
when they exit. Services take the form of:
service <name> <pathname> [ <argument> ]*
<option>
<option>
...
Options
-------
Options are modifiers to services. They affect how and when init
runs the service.
critical
This is a device-critical service. If it exits more than four times in
four minutes, the device will reboot into recovery mode.
disabled
This service will not automatically start with its class.
It must be explicitly started by name.
setenv <name> <value>
Set the environment variable <name> to <value> in the launched process.
socket <name> <type> <perm> [ <user> [ <group> ] ]
Create a unix domain socket named /dev/socket/<name> and pass
its fd to the launched process. <type> must be "dgram", "stream" or "seqpacket".
User and group default to 0.
user <username>
Change to username before exec''ing this service.
Currently defaults to root. (??? probably should default to nobody)
Currently, if your process requires linux capabilities then you cannot use
this command. You must instead request the capabilities in-process while
still root, and then drop to your desired uid.
group <groupname> [ <groupname> ]*
Change to groupname before exec''ing this service. Additional
groupnames beyond the (required) first one are used to set the
supplemental groups of the process (via setgroups()).
Currently defaults to root. (??? probably should default to nobody)
oneshot
Do not restart the service when it exits.
class <name>
Specify a class name for the service. All services in a
named class may be started or stopped together. A service
is in the class "default" if one is not specified via the
class option.
onrestart
Execute a Command (see below) when service restarts.
Triggers
--------
Triggers are strings which can be used to match certain kinds
of events and used to cause an action to occur.
boot
This is the first trigger that will occur when init starts
(after /init.conf is loaded)
<name>=<value>
Triggers of this form occur when the property <name> is set
to the specific value <value>.
device-added-<path>
device-removed-<path>
Triggers of these forms occur when a device node is added
or removed.
service-exited-<name>
Triggers of this form occur when the specified service exits.
Commands
--------
exec <path> [ <argument> ]*
Fork and execute a program (<path>). This will block until
the program completes execution. It is best to avoid exec
as unlike the builtin commands, it runs the risk of getting
init "stuck". (??? maybe there should be a timeout?)
export <name> <value>
Set the environment variable <name> equal to <value> in the
global environment (which will be inherited by all processes
started after this command is executed)
ifup <interface>
Bring the network interface <interface> online.
import <filename>
Parse an init config file, extending the current configuration.
hostname <name>
Set the host name.
chdir <directory>
Change working directory.
chmod <octal-mode> <path>
Change file access permissions.
chown <owner> <group> <path>
Change file owner and group.
chroot <directory>
Change process root directory.
class_start <serviceclass>
Start all services of the specified class if they are
not already running.
class_stop <serviceclass>
Stop all services of the specified class if they are
currently running.
domainname <name>
Set the domain name.
insmod <path>
Install the module at <path>
mkdir <path> [mode] [owner] [group]
Create a directory at <path>, optionally with the given mode, owner, and
group. If not provided, the directory is created with permissions 755 and
owned by the root user and root group.
mount <type> <device> <dir> [ <mountoption> ]*
Attempt to mount the named device at the directory <dir>
<device> may be of the form mtd@name to specify a mtd block
device by name.
<mountoption>s include "ro", "rw", "remount", "noatime", ...
setkey
TBD
setprop <name> <value>
Set system property <name> to <value>.
setrlimit <resource> <cur> <max>
Set the rlimit for a resource.
start <service>
Start a service running if it is not already running.
stop <service>
Stop a service from running if it is currently running.
symlink <target> <path>
Create a symbolic link at <path> with the value <target>
sysclktz <mins_west_of_gmt>
Set the system clock base (0 if system clock ticks in GMT)
trigger <event>
Trigger an event. Used to queue an action from another
action.
wait <path> [ <timeout> ]
Poll for the existence of the given file and return when found,
or the timeout has been reached. If timeout is not specified it
currently defaults to five seconds.
write <path> <string> [ <string> ]*
Open the file at <path> and write one or more strings
to it with write(2)
Properties
----------
Init updates some system properties to provide some insight into
what it''s doing:
init.action
Equal to the name of the action currently being executed or "" if none
init.command
Equal to the command being executed or "" if none.
init.svc.<name>
State of a named service ("stopped", "running", "restarting")
Example init.conf
-----------------
# not complete -- just providing some examples of usage
#
on boot
export PATH /sbin:/system/sbin:/system/bin
export LD_LIBRARY_PATH /system/lib
mkdir /dev
mkdir /proc
mkdir /sys
mount tmpfs tmpfs /dev
mkdir /dev/pts
mkdir /dev/socket
mount devpts devpts /dev/pts
mount proc proc /proc
mount sysfs sysfs /sys
write /proc/cpu/alignment 4
ifup lo
hostname localhost
domainname localhost
mount yaffs2 mtd@system /system
mount yaffs2 mtd@userdata /data
import /system/etc/init.conf
class_start default
service adbd /sbin/adbd
user adb
group adb
service usbd /system/bin/usbd -r
user usbd
group usbd
socket usbd 666
service zygote /system/bin/app_process -Xzygote /system/bin --zygote
socket zygote 666
service runtime /system/bin/runtime
user system
group system
on device-added-/dev/compass
start akmd
on device-removed-/dev/compass
stop akmd
service akmd /sbin/akmd
disabled
user akmd
group akmd
Debugging notes
---------------
By default, programs executed by init will drop stdout and stderr into
/dev/null. To help with debugging, you can execute your program via the
Andoird program logwrapper. This will redirect stdout/stderr into the
Android logging system (accessed via logcat).
For example
service akmd /system/bin/logwrapper /sbin/akmd
下面的是比较旧的汉语翻译,地址:http://blog.sina.com.cn/s/blog_6fba73770100nn6h.html
在Android中使用启动脚本init.rc,可以在系统的初始化中进行简单的操作。
init.rc启动脚本路径:system/core/rootdir/init.rc
内容:
Commands:命令
Actions:动作
Triggers:触发条件
Services:服务
Options:选项
Properties:属性
Commands是一些基本操作。如:
mkdir /system
mkdir /data 0771 system system
mkdir /persist 0771 system system
devwait /dev/block/mmcblk0p12
mount ext3 /dev/block/mmcblk0p
Action表示一系列命令,通常在Triggers中调用,如:
on init //表示一个触发条件
sysclktz 0
loglevel 3
# setup the global environment
export PATH /sbin:/system/sbin:/system/bin:/system/xbin
export LD_LIBRARY_PATH /system/lib
export ANDROID_BOOTLOGO 1
Services通常表示启动一个可执行程序,Options是服务的附加内容,用于配合服务使用。
service vold /system/bin/vold //vold是服务名称,/system/bin/vold是所对应的可执行程序。
socket vold stream 0660 root mount //socket是配合服务使用的选项
ioprio be 2
service netd /system/bin/netd
socket netd stream 0660 root system
配合服务使用的选项有socket,user,group,oneshot。
oneshot表示该服务只启动一次,而如果没有oneshot选项,这个可执行程序将一直存在——如果可执行程序被杀死,则会重新启动。
Properties是系统中使用的一些值,可以进行设置和读写。
setprop ro.HIDDEN_APP_MEM 5120 //setprop用于设置属性
setprop ro.CONTENT_PROVIDER_MEM 5632
setprop ro.EMPTY_APP_MEM 6144
...
on property:ro.kernel.qemu=1 //on property用于判断属性
start adbd
这里的属性在整个android系统运行中都是一致的。
init脚本的关键字可以参考init进程中的system/core/init/keyword.h文件。如:
KEYWORD(chroot, COMMAND, 1, do_chroot) //chroot是命令,do_chroot()是调用的函数,这个函数在init进程中的system/core/init/builtins.c文件中定义。
Android 初始化语言 (init.*.rc、init.conf 文件格式)
下文转载自:http://hi.baidu.com/zhlg_hzh/blog/item/4ad24a807c71d3db9023d967.html
红猎人:看了很多类似的文章,只有这篇讲述有条理,清楚,值得收藏!
=============================================================
Android 初始化语言 (init.*.rc、init.conf 文件格式)
Android 初始化语言包含了四种类型的声明:Actions (行动)、Commands (命令)、Services (服务)和 Options (选项)。
所有这些都是以行为单位的,各种记号由空格来隔开。C 语言风格的反斜杠号可用于在记号间插入空格。双引号也可用于防止字符串被空格分割成多个记
注释行以井号(#)开头(允许以空格开头)。
Actions 和 Services 声明一个新的分组。所有的命令或选项都属于最近申明的分组。位于第一个分组之前的命令或选项将会被忽略。
Actions 和 Services 有唯一的名字。如果有重名的情况,第二个申明的将会被作为错误忽略。( ???我们是否应该以覆盖来代替忽略)
Actions (行动)
----------
Actions 其实就是一序列的 Commands (命令)。Actions 都有一个 trigger (触发器),它被用于决定 action 的执行时间。当一个符合 action 触发条
队列中的每一个 action 都被依次提取出,而这个 action 中的每个 command (命令)都将被依次执行。Init 在这些命令的执行期间还控制着其他的活动(
Actions 的形式如下:
on <trigger>
<command>
<command>
<command>
Services (服务)
----------
Services (服务)是一个程序,他在初始化时启动,并在退出时重启(可选)。Services (服务)的形式如下:
service <name> <pathname> [ <argument> ]*
<option>
<option>
...
Options (选项)
----------
Options (选项)是一个 Services (服务)的修正者。他们影响 Services (服务)在何时,并以何种方式运行。
critical (关键)
说明这是一个对于设备关键的服务。如果他四分钟内退出大于四次,系统将会重启并进入 recovery (恢复)模式。
disabled (失效)
说明这个服务不会同与他同 trigger (触发器)下的服务自动启动。他必须被明确的按名启动。
setenv <name> <value> (设置环境变量)
在进程启动时将环境变量 <name> 设置为 < value>。
socket <name> <type> <perm> [ <user> [ <group> ] ]
创建一个 Uinx 域的名为 /dev/socket/<name> 的套接字,并传递它的文件描述符给已启动的进程。<type> 必须是 "dgram" 或 "
user <username>
在启动这个服务前改变该服务的用户名。此时默认为 root。(???有可能的话应该默认为 nobody)。当前,如果你的进程要求 L
group <groupname> [ <groupname> ]*
在启动这个服务前改变该服务的组名。除了(必需的)第一个组名,附加的组名通常被用于设置进程的补充组(通过 setgroups ()
oneshot
服务退出时不重启。
class <name>
指定一个服务类。所有同一类的服务可以同时启动和停止。如果不通过 class 选项指定一个类,则默认为 "default" 类服务。
onrestart
当服务重启,执行一个命令(下详)。
Triggers (触发器)
----------
Triggers (触发器)是一个用于匹配特定事件类型的字符串 ,用于使 Actions (行动)发生。
boot
这是 init 执行后的第一个被触发的 Triggers (触发器)。(在 /init.conf (启动配置文件)被装载之后)
<name>=<value>
这种形式的 Triggers (触发器)会在属性 <name> 被设置为指定的 < value > 时被触发。
device-added-<path>
device-removed-<path>
这种形式的 Triggers (触发器)会在一个设备节点文件被增删时触发。
service-exited-<name>
这种形式的 Triggers (触发器)会在一个特定的服务退出时触发。
Commands (命令)
----------
exec <path> [ <argument> ]*
创建和执行一个程序(<path>)。在程序完全执行前,init 将会阻塞。由于它不是内置命令,应尽量避免使用 exec ,它可能会引起 init 卡
export <name> <value>
在全局环境变量中设在环境变量 <name> 为 < value>。(这将会被所有在这命令之后运行的进程所继承)
ifup <interface>
启动网络接口 <interface>
import <filename>
解析一个 init 配置文件,扩展当前配置。
hostname <name>
设置主机名。
chmod <octal-mode> <path>
更改文件访问权限。
chown <owner> <group> <path>
更改文件的所有者和组。
class_start <serviceclass>
启动所有指定服务类下的未运行服务。
class_stop <serviceclass>
停止指定服务类下的所有已运行的服务。
domainname <name>
设置域名。
insmod <path>
加载 <path> 中的模块。
mkdir <path> [mode] [owner] [group]
创建一个目录 <path> ,可以选择性地指定 mode、owner 以及 group。如果没有指定,默认的权限为 755 ,并属于 root 用户和 root 组。
mount <type> <device> <dir> [ <mountoption> ]*
试图在目录 <dir> 挂载指定的设备。<device> 可以是以 mtd@name 的形式指定一个 mtd 块设备。<mountoption> 包括 "ro"、"rw"、"re
setkey
待完成 ...... (暂时不可用)
setprop <name> <value>
设置系统属性 <name> 为 <value > 值 .
setrlimit <resource> <cur> <max>
设置 <resource> 的 rlimit (资源限制)。
start <service>
启动指定服务(如果此服务还未运行)。
stop <service>
停止指定服务(如果此服务在运行中)。
symlink <target> <path>
创建一个指向 <path> 的软连接 < target>。
sysclktz <mins_west_of_gmt>
设置系统时钟基准(0 代表时钟滴答以格林威治平均时(GMT)为准)
trigger <event>
触发一个事件。用于将一个 action 与另一个 action 排列。(?????)
write <path> <string> [ <string> ]*
打开路径为 <path> 的一个文件,并写入一个或多个字符串。
Properties (属性)
----------
Init 更新一些系统属性以提供对正在发生的事件的监控能力 :
init.action
此属性值为正在被执行的 action 的名字,如果没有则为 ""。
init.command
此属性值为正在被执行的 command 的名字,如果没有则为 ""。
init.svc.<name>
名为 <name> 的 service 的状态 ("stopped" (停止), "running" (运行), "restarting" (重启))
init.conf 实例
-----------------
# not complete -- just providing some examples of usage
#
on boot
export PATH /sbin:/system/sbin:/system/bin
export LD_LIBRARY_PATH /system/lib
mkdir /dev
mkdir /proc
mkdir /sys
mount tmpfs tmpfs /dev
mkdir /dev/pts
mkdir /dev/socket
mount devpts devpts /dev/pts
mount proc proc /proc
mount sysfs sysfs /sys
write /proc/cp /alignment 4
ifup lo
hostname localhost
domainname localhost
mount yaffs2 mtd@system /system
mount yaffs2 mtd@userdata /data
import /system/etc/init.conf
class_start default
service adbd /sbin/adbd
user adb
group adb
service usbd /system/bin/usbd -r
user usbd
group usbd
socket usbd 666
service zygote /system/bin/app_process -Xzygote /system/bin --zygote
socket zygote 666
service runtime /system/bin/runtime
user system
group system
on device-added-/dev/compass
start akmd
on device-removed-/dev/compass
stop akmd
service akmd /sbin/akmd
disabled
user akmd
group akmd
调试记录
---------------
在默认情况下,程序在被 init 执行时会将标准输出和标准错误都重定向到 /dev/null (丢弃)。若你想要获得调试信息,你可以通过 Andoird 系统中的 logwrapp
例如:
service akmd /system/bin/logwrapper /sbin/akmd
Android 系统 init 进程启动及 init.rc 全解析
转:https://blog.csdn.net/zhonglunshun/article/details/78615980
服务启动机制
system/core/init/init.c 文件 main 函数中 parse_config_file (init.rc) 读取并解析 init.rc 文件内容。将 service 信息放置到 system/core/init/init_parser.cpp 的 service_list 中
system/core/init/init.c 文件 main 函数继续执行 restart_servie_if_needed (…) -> service_start (…) -> Execve (…) 建立 service 进程;
为了让大伙看得更明白,上个图先《总体启动框架图》:
init.rc 简介
目前 Linux 有很多通讯机制可以在用户空间和内核空间之间交互,例如设备驱动文件(位于 /dev 目录中)、内存文件(/proc、/sys 目录等)。了解 Linux 的同学都应该知道 Linux 的重要特征之一就是一切都是以文件的形式存在的,例如,一个设备通常与一个或多个设备文件对应。这些与内核空间交互的文件都在用户空间,所以在 Linux 内核装载完,需要首先建立这些文件所在的目录。而完成这些工作的程序就是本文要介绍的 init。Init 是一个命令行程序。其主要工作之一就是建立这些与内核空间交互的文件所在的目录。当 Linux 内核加载完后,要做的第一件事就是调用 init 程序,也就是说,init 是用户空间执行的第一个程序。
尽管 init 完成的工作不算很多,不过代码还是非常复杂的。Init 程序并不是由一个源代码文件组成的,而是由一组源代码文件的目标文件链接而成的。这些文件位于如下的目录。
需要明白的是,这些 init.rc 只是语法文件,并不是程序,真正的入口则是上面提到的 system/core/init/init.c
因为 init.c 文件比较大,在文章的第二部分我会简要的通过 main 函数分析 init 启动流程;
init.rc 有两个,分别位于:
./system/core/rootdir/init.rc
./bootable/recovery/etc/init.rc
从目录上大致可以猜测,这两个 init.rc 使用场景不一样,一个是刷机用到的,也就是进入 recorvery 模式,一个是正常启动用到的;我们这里重点分析的是上面那个,也是 init.c 关联的那个;
init.rc 语法结构解析
要了解 init.rc 是怎么解析的,我们需要先看看说明文档,说明文档在,当然也可以看下热心网友的中文对照版本;
init.rc 位于 /bootable/recovery/etc/init.rc
Android 初始化语言包含了四种类型的声明:
Actions(行为)、Commands(命令)、Services(服务)和 Options(选项)
所有这些都是以行为单位的,各种记号由空格来隔开。
C 语言风格的反斜杠号可用于在记号间插入空格。
双引号也可用于防止字符串被空格分割成多个记号。
行末的反斜杠用于折行,注释行以井号(#)开头(允许以空格开头)。
需要注意的是,这个只是一个语法文件,就像一个 xml 文件一样,没有执行顺序的,解析器通过读这个文件获取想要的数据,包括 service,action 等
Actions 和 Services 声明一个新的分组 Section。所有的命令或选项都属于最近声明的分组。位于第一个分组之前的命令或选项将会被忽略。
Actions 和 Services 有唯一的名字。如果有重名的情况,第二个申明的将会被作为错误忽略。
Actions
Actions(行为)是一系列命令的开始
Actions 代表一些 Action.Action 代表一组命令 (Commands),Actions 都有一个 trigger(触发器), 该触发器决定了何时执行这个 Action, 即在什么情况下才能执行该 Action 中的定义命令。当一些条件满足触发器的条件时,该 Action 中定义的命令会被添加到要执行命令队列的尾部 (如果这组命令已经在队列中,则不会再次添加).
队列中的每一个 action 都被依次提取出,而这个 action 中的每个 command(命令)在一个 Action 从队列移除时,该 Action 定义的命令会依次被执行.
Action 的格式如下:
on <trgger> [&& <trigger>]*
<command1>
<command2>
<command3>
...
1
2
3
4
5
on 后面跟着一个触发器,当 trigger 被触发时,command1,command2,command3,会依次执行,直到下一个 Action 或下一个 Service。
简单来说,Actions 就是 Android 在启动时定义的一个启动脚本,当条件满足时,会执行该脚本,脚本里都是一些命令 commands,不同的脚本用 on 来区分。
Triggers(触发器)
trigger 即我们上面所说的触发器,本质上是一个字符串,能够匹配某种包含该字符串的事件.
trigger 又被细分为事件触发器 (event trigger) 和属性触发器 (property trigger).
Triggers(触发器)是一个用于匹配特定事件类型的字符串,用于使 Actions 发生。
事件触发器可由”trigger” 命令或初始化过程中通过 QueueEventTrigger () 触发,通常是一些事先定义的简单字符串,例如:boot,late-init
属性触发器是当指定属性的变量值变成指定值时触发,其格式为 property:=*
一个 Action 可以有多个属性触发器,但是最多有一个事件触发器。下面我们看两个例子:
on boot && property:a=b
1
该 Action 只有在 boot 事件发生时,并且属性 a 和 b 相等的情况下才会被触发.
on property:a=b && property:c=d
1
该 Action 会在以下三种情况被触发:
在启动时,如果属性 a 的值等于 b 并且属性 c 的值等于 d
在属性 c 的值已经是 d 的情况下,属性 a 的值被更新为 b
在属性 a 的值已经是 b 的情况下,属性 c 的值被更新为 d
当前 AIL 中常用的有以下几种事件触发器:
类型 说明
-------------------------------------------------
boot init.rc 被装载后触发
device-added-<path> 指定设备被添加时触发
device-removed-<path> 指定设备被移除时触发
service-exited-<name> 在特定服务 (service) 退出时触发
early-init 初始化之前触发
late-init 初始化之后触发
init 初始化时触发(在 /init.conf (启动配置文件)被装载之后)
1
2
3
4
5
6
7
8
9
Init 的触发是由 init.c 里的函数 action_for_each_trigger 来决定的(在 main 函数中被调用)。
Services
Services(服务)是一个程序,以 service 开头,由 init 进程启动,一般运行于另外一个 init 的子进程,所以启动 service 前需要判断对应的可执行文件是否存在。init 生成的子进程,定义在 rc 文件,其中每一个 service,在启动时会通过 fork 方式生成子进程。Services(服务)的形式如下:
service <name> <pathname> [ <argument> ]*
<option>
<option>
...
1
2
3
4
其中:
name: 服务名
pathname: 当前服务对应的程序位置
option:当前服务设置的选项
argument 可选参数
init.rc 文件详解
为了方便理解,我把整个 init.rc 解析一边,便于大家了解整个流程;如果想要了解 recovery 下的 init 语法解析,参考这篇文章《recovery 下的 init.rc 语法解析》
代码量比较大,如果觉得看起来费劲,可以挑绿色部分看;
# Copyright (C) 2012 The Android Open Source Project
#
# IMPORTANT: Do not create world writable files or directories.
# This is a common source of Android security bugs.
#
"【import <filename> 一个 init 配置文件,扩展当前配置。】"
import /init.environ.rc
import /init.usb.rc
import /init.${ro.hardware}.rc
import /init.${ro.zygote}.rc
import /init.trace.rc
"【触发条件 early-init,在 early-init 阶段调用以下行】"
on early-init
# Set init and its forked children''s oom_adj.
write /proc/1/oom_score_adj -1000
"【打开路径为 <path> 的一个文件,并写入一个或多个字符串】"
# Apply strict SELinux checking of PROT_EXEC on mmap/mprotect calls.
write /sys/fs/selinux/checkreqprot 0
# Set the security context for the init process.
# This should occur before anything else (e.g. ueventd) is started.
"【这段脚本的意思是 init 进程启动之后就马上调用函数 setcon 将自己的安全上下文设置为 “u:r:init:s0”,即将 init 进程的 domain 指定为 init。】"
setcon u:r:init:s0
# Set the security context of /adb_keys if present.
"【恢复指定文件到 file_contexts 配置中指定的安全上线文环境】"
restorecon /adb_keys
"【执行 start ueventd 的命令。ueventd 是一个 service 后面有定义】"
start ueventd
"【mkdir <path> [mode] [owner] [group] // 创建一个目录 < path>,可以选择性地指定 mode、owner 以及 group。如果没有指定,默认的权限为 755,并属于 root 用户和 root 组。】"
# create mountpoints
mkdir /mnt 0775 root system
on init
"【设置系统时钟的基准,比如 0 代表 GMT, 即以格林尼治时间为准】"
sysclktz 0
"【设置 kernel 日志等级】"
loglevel 6 ####
write /proc/bootprof "INIT: on init start" ####
"【symlink <target> <path> // 创建一个指向 < path > 的软连接 < target>。】"
# Backward compatibility
symlink /system/etc /etc
symlink /sys/kernel/debug /d
# Right now vendor lives on the same filesystem as system,
# but someday that may change.
symlink /system/vendor /vendor
"【创建一个目录 <path>,可以选择性地指定 mode、owner 以及 group。】"
# Create cgroup mount point for cpu accounting
mkdir /acct
mount cgroup none /acct cpuacct
mkdir /acct/uid
"【mount <type> <device> <dir> [ <mountoption> ] // 在目录 < dir > 挂载指定的设备。<device> 可以是以 mtd@name 的形式指定一个 mtd 块设备。<mountoption > 包括 ro、rw、remount、noatime、 ...】"
# Create cgroup mount point for memory
mount tmpfs none /sys/fs/cgroup mode=0750,uid=0,gid=1000
mkdir /sys/fs/cgroup/memory 0750 root system
mount cgroup none /sys/fs/cgroup/memory memory
write /sys/fs/cgroup/memory/memory.move_charge_at_immigrate 1
"【chown <owner> <group> <path> // 改变文件的所有者和组。】"
"【后面的一些行因为类似,就省略了】"
.....
# Healthd can trigger a full boot from charger mode by signaling this
# property when the power button is held.
on property:sys.boot_from_charger_mode=1
"【停止指定类别服务类下的所有已运行的服务】"
class_stop charger
"【触发一个事件,将该 action 排在某个 action 之后 (用于 Action 排队)】"
trigger late-init
# Load properties from /system/ + /factory after fs mount.
on load_all_props_action
"【从 /system,/vendor 加载属性。默认包含在 init.rc】"
load_all_props
# Indicate to fw loaders that the relevant mounts are up.
on firmware_mounts_complete
"【删除指定路径下的文件】"
rm /dev/.booting
# Mount filesystems and start core system services.
on late-init
"【触发一个事件。用于将一个 action 与另一个 action 排列。】"
trigger early-fs
trigger fs
trigger post-fs
trigger post-fs-data
# Load properties from /system/ + /factory after fs mount. Place
# this in another action so that the load will be scheduled after the prior
# issued fs triggers have completed.
trigger load_all_props_action
# Remove a file to wake up anything waiting for firmware.
trigger firmware_mounts_complete
trigger early-boot
trigger boot
on post-fs
...
"【一些创造目录,建立链接,更改权限的操作,这里省略】"
on post-fs-data
...
"【一些创造目录,建立链接,更改权限的操作,这里省略】"
"【恢复指定文件到 file_contexts 配置中指定的安全上线文环境】"
restorecon /data/mediaserver
"【将系统属性 <name> 的值设置为 < value>, 即以键值对的方式设置系统属性】"
# Reload policy from /data/security if present.
setprop selinux.reload_policy 1
"【以递归的方式恢复指定目录到 file_contexts 配置中指定的安全上下文中】"
# Set SELinux security contexts on upgrade or policy update.
restorecon_recursive /data
# If there is no fs-post-data action in the init.<device>.rc file, you
# must uncomment this line, otherwise encrypted filesystems
# won''t work.
# Set indication (checked by vold) that we have finished this action
#setprop vold.post_fs_data_done 1
on boot
"【初始化网络】"
# basic network init
ifup lo
"【设置主机名为 localhost】"
hostname localhost
"【设置域名 localdomain】"
domainname localdomain
"【设置资源限制】"
# set RLIMIT_NICE to allow priorities from 19 to -20
setrlimit 13 40 40
"【这里省略了一些 chmod,chown, 等操作,不多解释】"
...
# Define default initial receive window size in segments.
setprop net.tcp.default_init_rwnd 60
"【重启 core 服务】"
class_start core
on nonencrypted
class_start main
class_start late_start
on property:vold.decrypt=trigger_default_encryption
start defaultcrypto
on property:vold.decrypt=trigger_encryption
start surfaceflinger
start encrypt
on property:sys.init_log_level=*
loglevel ${sys.init_log_level}
on charger
class_start charger
on property:vold.decrypt=trigger_reset_main
class_reset main
on property:vold.decrypt=trigger_load_persist_props
load_persist_props
on property:vold.decrypt=trigger_post_fs_data
trigger post-fs-data
on property:vold.decrypt=trigger_restart_min_framework
class_start main
on property:vold.decrypt=trigger_restart_framework
class_start main
class_start late_start
on property:vold.decrypt=trigger_shutdown_framework
class_reset late_start
class_reset main
on property:sys.powerctl=*
powerctl ${sys.powerctl}
# system server cannot write to /proc/sys files,
# and chown/chmod does not work for /proc/sys/ entries.
# So proxy writes through init.
on property:sys.sysctl.extra_free_kbytes=*
write /proc/sys/vm/extra_free_kbytes ${sys.sysctl.extra_free_kbytes}
# "tcp_default_init_rwnd" Is too long!
on property:sys.sysctl.tcp_def_init_rwnd=*
write /proc/sys/net/ipv4/tcp_default_init_rwnd ${sys.sysctl.tcp_def_init_rwnd}
"【守护进程】"
## Daemon processes to be run by init.
##
service ueventd /sbin/ueventd
class core
critical
seclabel u:r:ueventd:s0
"【日志服务进程】"
service logd /system/bin/logd
class core
socket logd stream 0666 logd logd
socket logdr seqpacket 0666 logd logd
socket logdw dgram 0222 logd logd
seclabel u:r:logd:s0
"【Healthd 是 android4.4 之后提出来的一种中介模型,该模型向下监听来自底层的电池事件,向上传递电池数据信息给 Framework 层的 BatteryService 用以计算电池电量相关状态信息】"
service healthd /sbin/healthd
class core
critical
seclabel u:r:healthd:s0
"【控制台进程】"
service console /system/bin/sh
"【为当前 service 设定一个类别。相同类别的服务将会同时启动或者停止,默认类名是 default】"
class core
"【服务需要一个控制台】"
console
"【服务不会自动启动,必须通过服务名显式启动】"
disabled
"【在执行此服务之前切换用户名,当前默认的是 root. 自 Android M 开始,即使它要求 linux capabilities, 也应该使用该选项。很明显,为了获得该功能,进程需要以 root 用户运行】"
user shell
seclabel u:r:shell:s0
on property:ro.debuggable=1
start console
# adbd is controlled via property triggers in init.<platform>.usb.rc
service adbd /sbin/adbd --root_seclabel=u:r:su:s0
class core
"【创建一个 unix 域下的 socket, 其被命名 /dev/socket/<name>. 并将其文件描述符 fd 返回给服务进程。其中,type 必须为 dgram,stream 或者 seqpacke,user 和 group 默认是 0.seclabel 是该 socket 的 SELLinux 的安全上下文环境,默认是当前 service 的上下文环境,通过 seclabel 指定】"
socket adbd stream 660 system system
disabled
seclabel u:r:adbd:s0
# adbd on at boot in emulator
on property:ro.kernel.qemu=1
start adbd
"【内存管理服务,内存不够释放内存】"
service lmkd /system/bin/lmkd
class core
critical
socket lmkd seqpacket 0660 system system
"【ServiceManager 是一个守护进程,它维护着系统服务和客户端的 binder 通信。
在 Android 系统中用到最多的通信机制就是 Binder,Binder 主要由 Client、Server、ServiceManager 和 Binder 驱动程序组成。其中 Client、Service 和 ServiceManager 运行在用户空间,而 Binder 驱动程序运行在内核空间。核心组件就是 Binder 驱动程序了,而 ServiceManager 提供辅助管理的功能,无论是 Client 还是 Service 进行通信前首先要和 ServiceManager 取得联系。而 ServiceManager 是一个守护进程,负责管理 Server 并向 Client 提供查询 Server 的功能。】"
service servicemanager /system/bin/servicemanager
class core
user system
group system
critical
onrestart restart healthd
"【servicemanager 服务启动时会重启 zygote 服务】"
onrestart restart zygote
onrestart restart media
onrestart restart surfaceflinger
onrestart restart drm
"【Vold 是 Volume Daemon 的缩写,它是 Android 平台中外部存储系统的管控中心,是管理和控制 Android 平台外部存储设备的后台进程】"
service vold /system/bin/vold
class core
socket vold stream 0660 root mount
ioprio be 2
"【Netd 是 Android 系统中专门负责网络管理和控制的后台 daemon 程序】"
service netd /system/bin/netd
class main
socket netd stream 0660 root system
socket dnsproxyd stream 0660 root inet
socket mdns stream 0660 root system
socket fwmarkd stream 0660 root inet
"【debuggerd 是一个 daemon 进程,在系统启动时随着 init 进程启动。主要负责将进程运行时的信息 dump 到文件或者控制台中】"
service debuggerd /system/bin/debuggerd
class main
service debuggerd64 /system/bin/debuggerd64
class main
"【Android RIL (Radio Interface Layer) 提供了 Telephony 服务和 Radio 硬件之间的抽象层】"
# for using TK init.modem.rc rild-daemon setting
#service ril-daemon /system/bin/rild
# class main
# socket rild stream 660 root radio
# socket rild-debug stream 660 radio system
# user root
# group radio cache inet misc audio log
"【提供系统 范围内的 surface composer 功能,它能够将各种应用 程序的 2D、3D surface 进行组合。】"
service surfaceflinger /system/bin/surfaceflinger
class core
user system
group graphics drmrpc
onrestart restart zygote
"【DRM 可以直接访问 DRM clients 的硬件。DRM 驱动用来处理 DMA,内存管理,资源锁以及安全硬件访问。为了同时支持多个 3D 应用,3D 图形卡硬件必须作为一个共享资源,因此需要锁来提供互斥访问。DMA 传输和 AGP 接口用来发送图形操作的 buffers 到显卡硬件,因此要防止客户端越权访问显卡硬件。】"
#make sure drm server has rights to read and write sdcard ####
service drm /system/bin/drmserver
class main
user drm
# group drm system inet drmrpc ####
group drm system inet drmrpc sdcard_r ####
"【媒体服务,无需多说】"
service media /system/bin/mediaserver
class main
user root ####
# google default ####
# user media ####
group audio camera inet net_bt net_bt_admin net_bw_acct drmrpc mediadrm media sdcard_r system net_bt_stack ####
# google default ####
# group audio camera inet net_bt net_bt_admin net_bw_acct drmrpc mediadrm ####
ioprio rt 4
"【设备加密相关服务】"
# One shot invocation to deal with encrypted volume.
service defaultcrypto /system/bin/vdc --wait cryptfs mountdefaultencrypted
disabled
"【当服务退出时,不重启该服务】"
oneshot
# vold will set vold.decrypt to trigger_restart_framework (default
# encryption) or trigger_restart_min_framework (other encryption)
# One shot invocation to encrypt unencrypted volumes
service encrypt /system/bin/vdc --wait cryptfs enablecrypto inplace default
disabled
oneshot
# vold will set vold.decrypt to trigger_restart_framework (default
# encryption)
"【开机动画服务】"
service bootanim /system/bin/bootanimation
class core
user graphics
# group graphics audio ####
group graphics media audio ####
disabled
oneshot
"【在 Android 系统中,PackageManagerService 用于管理系统中的所有安装包信息及应用程序的安装卸载,但是应用程序的安装与卸载并非 PackageManagerService 来完成,而是通过 PackageManagerService 来访问 installd 服务来执行程序包的安装与卸载的。】"
service installd /system/bin/installd
class main
socket installd stream 600 system system
service flash_recovery /system/bin/install-recovery.sh
class main
seclabel u:r:install_recovery:s0
oneshot
"【vpn 相关的服务】"
service racoon /system/bin/racoon
class main
socket racoon stream 600 system system
# IKE uses UDP port 500. Racoon will setuid to vpn after binding the port.
group vpn net_admin inet
disabled
oneshot
"【android 中有 mtpd 命令可以连接 vpn】"
service mtpd /system/bin/mtpd
class main
socket mtpd stream 600 system system
user vpn
group vpn net_admin inet net_raw
disabled
oneshot
service keystore /system/bin/keystore /data/misc/keystore
class main
user keystore
group keystore drmrpc
"【可以用 dumpstate 获取设备的各种信息】"
service dumpstate /system/bin/dumpstate -s
class main
socket dumpstate stream 0660 shell log
disabled
oneshot
"【mdnsd 是多播 DNS 和 DNS 服务发现的守护程序。】"
service mdnsd /system/bin/mdnsd
class main
user mdnsr
group inet net_raw
socket mdnsd stream 0660 mdnsr inet
disabled
oneshot
"【触发关机流程继续往下走】"
service pre-recovery /system/bin/uncrypt
class main
disabled
"【当服务退出时,不重启该服务】"
oneshot
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
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
init.c 全解析
接下来我们具体分析以下这个 main 函数的执行过程;可能比较长,大家耐心看一下:
int main( int argc, char **argv )
{
#创 建一些 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 );
#init 的 标准输入,标准输出,标准错误文件描述符定向到__null__,意味着没有输入和输出,它的输入和输出全部写入到 Log 中
open_devnull_stdio();
#初始化 log 写入 init 进 信息
log_init();
#读取并 且解析 init.rc 文件(这个文件在根目录下)
parse_config_file( "/init.rc" );
#取得硬件 为打印我们的设备名 fs100
get_hardware_name();
snprintf( tmp, sizeof(tmp), "/init.%s.rc", hardware );
#读取并 且解析硬件相关的 init 脚本文件,
parse_config_file( tmp );
#触发在 init 脚本文件中名字为 early-init 的 action,并且执行其 commands,其实是: on early-init
action_for_each_trigger( "early-init", action_add_queue_tail );
drain_action_queue();
#初始化动态设备管理,设备文件有变化时反应给内核,后面具体解释
device_fd = device_init (); # 初 始 化 设 备 管 理 务
#加载启动动画,如果动画打开失败,则在屏幕上打印: A N D R O I D 字样。
if ( load_565rle_image( INIT_IMAGE_FILE ) )
{
fd = open( "/dev/tty0", O_WRONLY );
if ( fd >= 0 )
{
const char *msg;
msg = "\n"
"\n"
"\n"
879 "\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 );
}
}
#触发 在 init 脚本文件中名字为 init 的 action,并且执行其 commands,其实是:on init
action_for_each_trigger( "init", action_add_queue_tail );
drain_action_queue();
#启动系统属性服务: system property service
property_set_fd = start_property_service();
#创建 socket 用来处理孤儿进程信号
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 );
}
#触发 在 init 脚本文件中名字为 early-boot 和 boot 的 action,并且执行其 commands,其实是:on early-boot 和 on boot
action_for_each_trigger( "early-boot", action_add_queue_tail );
action_for_each_trigger( "boot", action_add_queue_tail );
drain_action_queue();
#启动所有属性变化触发命令,其实是: on property:ro.xx.xx=xx
queue_all_property_triggers();
drain_action_queue();
#进入 死循环()
for (;; )
{
#启 动所有 init 脚本中声明的 service,
#如 :266 service servicemanager /system/bin/servicemanager
#user system
#critical
#onrestart restart zygote
#onrestart restart media
restart_processes();
#多路监听设备管理,子进程运行状态,属性服务
nr = poll( ufds, fd_count, timeout );
if ( nr <= 0 )
continue;
if ( ufds[2].revents == POLLIN )
{
read( signal_recv_fd, tmp, sizeof(tmp) );
while ( !wait_for_one_process( 0 ) )
;
continue;
}
if ( ufds[0].revents == POLLIN )
handle_device_fd( device_fd );
if ( ufds[1].revents == POLLIN )
handle_property_set_fd( property_set_fd );
if ( ufds[3].revents == POLLIN )
handle_keychord( keychord_fd );
}
return(0);
}
---------------------
作者:疯人院的院长大人
来源:CSDN
原文:https://blog.csdn.net/zhonglunshun/article/details/78615980
版权声明:本文为博主原创文章,转载请附上博文链接!
CentOS 7 巨大变动之 systemd 取代 SysV 的 Init

1 systemd 是什么


2 Systemd 物理文件组成
- # inittab is no longer used when using systemd.
- #
- # ADDING CONFIGURATION HERE WILL HAVE NO EFFECT ON YOUR SYSTEM.
- #
- # Ctrl-Alt-Delete is handled by /etc/systemd/system/ctrl-alt-del.target
- #
- # systemd uses ''targets'' instead of runlevels. By default, there are two main targets:
- #
- # multi-user.target: analogous to runlevel 3
- # graphical.target: analogous to runlevel 5
- #
- # To set a default target, run:
- #
- # ln -sf /lib/systemd/system/<target name>.target /etc/systemd/system/default.target
- # This file is part of systemd.
- #
- # systemd is free software; you can redistribute it and/or modify it
- # under the terms of the GNU Lesser General Public License as published by
- # the Free Software Foundation; either version 2.1 of the License, or
- # (at your option) any later version.
- [Unit]
- Description=Multi-User System
- Documentation=man:systemd.special(7)
- Requires=basic.target
- Conflicts=rescue.service rescue.target
- After=basic.target rescue.service rescue.target
- AllowIsolate=yes
- [Install]
- Alias=default.target
3 Systemd 运行原理
3.1 systemd 的基本概念
(1)配置单元 unit
- service 后代服务进程,如 httpd,mysqld 等
- soket 对应一个套接字,之后对应到一个 service,类似于 xinetd 的功能
- device 对应 udev 规则标记的一个设备
- mount 系统中的一个挂载点,systemd 据此进行自动挂载,为了与 SystemV 兼容,目前 systemd 自动处理 /etc/fstab 并转化为 mount
- automount 自动挂载点
- swap 配置交换分区
- target 配置单元的逻辑分组,包含多个相关的配置单元,可以当成是 SystemV 中的运行级。
- timer 定时器。用来定时触发用户定义的操作,它可以用来取代传统的 atd,crond 等。
- snapshot 与 target 类似,表示当前的运行状态
(2)依赖关系
(3) Target 和 runlevel
表 1. Sysvinit 运行级别和 systemd 目标的对应表
Sysvinit 运行级别 | Systemd 目标 | 备注 |
---|---|---|
0 | runlevel0.target, poweroff.target | 关闭系统。 |
1, s, single | runlevel1.target, rescue.target | 单用户模式。 |
2, 4 | runlevel2.target, runlevel4.target, multi-user.target | 用户定义 / 域特定运行级别。默认等同于 3。 |
3 | runlevel3.target, multi-user.target | 多用户,非图形化。用户可以通过多个控制台或网络登录。 |
5 | runlevel5.target, graphical.target | 多用户,图形化。通常为所有运行级别 3 的服务外加图形化登录。 |
6 | runlevel6.target, reboot.target | 重启 |
emergency | emergency.target | 紧急 Shell |
如前所述,在 Systemd 中,所有的服务都并发启动,比如 Avahi、D-Bus、livirtd、X11、HAL 可以同时启动。乍一看,这似乎有点儿问题,比如 Avahi 需要 syslog 的服务,Avahi 和 syslog 同时启动,假设 Avahi 的启动比较快,所以 syslog 还没有准备好,可是 Avahi 又需要记录日志,这岂不是会出现问题?
Systemd 的开发人员仔细研究了服务之间相互依赖的本质问题,发现所谓依赖可以分为三个具体的类型,而每一个类型实际上都可以通过相应的技术解除依赖关系。
并发启动原理之一:解决 socket 依赖
绝大多数的服务依赖是套接字依赖。比如服务 A 通过一个套接字端口 S1 提供自己的服务,其他的服务如果需要服务 A,则需要连接 S1。因此如果服务 A 尚未启动,S1 就不存在,其他的服务就会得到启动错误。所以传统地,人们需要先启动服务 A,等待它进入就绪状态,再启动其他需要它的服务。Systemd 认为,只要我们预先把 S1 建立好,那么其他所有的服务就可以同时启动而无需等待服务 A 来创建 S1 了。如果服务 A 尚未启动,那么其他进程向 S1 发送的服务请求实际上会被 Linux 操作系统缓存,其他进程会在这个请求的地方等待。一旦服务 A 启动就绪,就可以立即处理缓存的请求,一切都开始正常运行。
那么服务如何使用由 init 进程创建的套接字呢?
Linux 操作系统有一个特性,当进程调用 fork 或者 exec 创建子进程之后,所有在父进程中被打开的文件句柄 (file descriptor) 都被子进程所继承。套接字也是一种文件句柄,进程 A 可以创建一个套接字,此后当进程 A 调用 exec 启动一个新的子进程时,只要确保该套接字的 close_on_exec 标志位被清空,那么新的子进程就可以继承这个套接字。子进程看到的套接字和父进程创建的套接字是同一个系统套接字,就仿佛这个套接字是子进程自己创建的一样,没有任何区别。
这个特性以前被一个叫做 inetd 的系统服务所利用。Inetd 进程会负责监控一些常用套接字端口,比如 Telnet,当该端口有连接请求时,inetd 才启动 telnetd 进程,并把有连接的套接字传递给新的 telnetd 进程进行处理。这样,当系统没有 telnet 客户端连接时,就不需要启动 telnetd 进程。Inetd 可以代理很多的网络服务,这样就可以节约很多的系统负载和内存资源,只有当有真正的连接请求时才启动相应服务,并把套接字传递给相应的服务进程。
和 inetd 类似,systemd 是所有其他进程的父进程,它可以先建立所有需要的套接字,然后在调用 exec 的时候将该套接字传递给新的服务进程,而新进程直接使用该套接字进行服务即可。
并发启动原理之二:解决 D-Bus 依赖
D-Bus 是 desktop-bus 的简称,是一个低延迟、低开销、高可用性的进程间通信机制。它越来越多地用于应用程序之间通信,也用于应用程序和操作系统内核之间的通信。很多现代的服务进程都使用 D-Bus 取代套接字作为进程间通信机制,对外提供服务。比如简化 Linux 网络配置的 NetworkManager 服务就使用 D-Bus 和其他的应用程序或者服务进行交互:邮件客户端软件 evolution 可以通过 D-Bus 从 NetworkManager 服务获取网络状态的改变,以便做出相应的处理。
D-Bus 支持所谓 "bus activation" 功能。如果服务 A 需要使用服务 B 的 D-Bus 服务,而服务 B 并没有运行,则 D-Bus 可以在服务 A 请求服务 B 的 D-Bus 时自动启动服务 B。而服务 A 发出的请求会被 D-Bus 缓存,服务 A 会等待服务 B 启动就绪。利用这个特性,依赖 D-Bus 的服务就可以实现并行启动。
并发启动原理之三:解决文件系统依赖
系统启动过程中,文件系统相关的活动是最耗时的,比如挂载文件系统,对文件系统进行磁盘检查(fsck),磁盘配额检查等都是非常耗时的操作。在等待这些工作完成的同时,系统处于空闲状态。那些想使用文件系统的服务似乎必须等待文件系统初始化完成才可以启动。但是 systemd 发现这种依赖也是可以避免的。
Systemd 参考了 autofs 的设计思路,使得依赖文件系统的服务和文件系统本身初始化两者可以并发工作。autofs 可以监测到某个文件系统挂载点真正被访问到的时候才触发挂载操作,这是通过内核 automounter 模块的支持而实现的。比如一个 open () 系统调用作用在 "/misc/cd/file1" 的时候,/misc/cd 尚未执行挂载操作,此时 open () 调用被挂起等待,Linux 内核通知 autofs,autofs 执行挂载。这时候,控制权返回给 open () 系统调用,并正常打开文件。
Systemd 集成了 autofs 的实现,对于系统中的挂载点,比如 /home,当系统启动的时候,systemd 为其创建一个临时的自动挂载点。在这个时刻 /home 真正的挂载设备尚未启动好,真正的挂载操作还没有执行,文件系统检测也还没有完成。可是那些依赖该目录的进程已经可以并发启动,他们的 open () 操作被内建在 systemd 中的 autofs 捕获,将该 open () 调用挂起(可中断睡眠状态)。然后等待真正的挂载操作完成,文件系统检测也完成后,systemd 将该自动挂载点替换为真正的挂载点,并让 open () 调用返回。由此,实现了那些依赖于文件系统的服务和文件系统本身同时并发启动。
当然对于 "/" 根目录的依赖实际上一定还是要串行执行,因为 systemd 自己也存放在 / 之下,必须等待系统根目录挂载检查好。
不过对于类似 /home 等挂载点,这种并发可以提高系统的启动速度,尤其是当 /home 是远程的 NFS 节点,或者是加密盘等,需要耗费较长的时间才可以准备就绪的情况下,因为并发启动,这段时间内,系统并不是完全无事可做,而是可以利用这段空余时间做更多的启动进程的事情,总的来说就缩短了系统启动时间。
4 Systemd 配置使用
4.1 对于系统开发人员
开发人员需要了解 systemd 的更多细节。比如您打算开发一个新的系统服务,就必须了解如何让这个服务能够被 systemd 管理。这需要您注意以下这些要点:
- 后台服务进程代码不需要执行两次派生来实现后台精灵进程,只需要实现服务本身的主循环即可。
- 不要调用 setsid (),交给 systemd 处理
- 不再需要维护 pid 文件。
- Systemd 提供了日志功能,服务进程只需要输出到 stderr 即可,无需使用 syslog。
- 处理信号 SIGTERM,这个信号的唯一正确作用就是停止当前服务,不要做其他的事情。
- SIGHUP 信号的作用是重启服务。
- 需要套接字的服务,不要自己创建套接字,让 systemd 传入套接字。
- 使用 sd_notify () 函数通知 systemd 服务自己的状态改变。一般地,当服务初始化结束,进入服务就绪状态时,可以调用它。
对于开发者来说,工作量最大的部分应该是编写配置单元文件,定义所需要的单元。
举例来说,开发人员开发了一个新的服务程序,比如 httpd,就需要为其编写一个配置单元文件以便该服务可以被 systemd 管理,类似 UpStart 的工作配置文件。在该文件中定义服务启动的命令行语法,以及和其他服务的依赖关系等。
此外我们之前已经了解到,systemd 的功能繁多,不仅用来管理服务,还可以管理挂载点,定义定时任务等。这些工作都是由编辑相应的配置单元文件完成的。我在这里给出几个配置单元文件的例子。
下面是 SSH 服务的配置单元文件,服务配置单元文件以.service 为文件名后缀。
#cat /etc/system/system/sshd.service
[Unit]
Description=OpenSSH server daemon
[Service]
EnvironmentFile=/etc/sysconfig/sshd
ExecStartPre=/usr/sbin/sshd-keygen
ExecStart=/usrsbin/sshd –D $OPTIONS
ExecReload=/bin/kill –HUP $MAINPID
KillMode=process
Restart=on-failure
RestartSec=42s
[Install]
WantedBy=multi-user.target
文件分为三个小节。第一个是 [Unit] 部分,这里仅仅有一个描述信息。第二部分是 Service 定义,其中,ExecStartPre 定义启动服务之前应该运行的命令;ExecStart 定义启动服务的具体命令行语法。第三部分是 [Install],WangtedBy 表明这个服务是在多用户模式下所需要的。
那我们就来看下 multi-user.target 吧:
#cat multi-user.target
[Unit]
Description=Multi-User System
Documentation=man.systemd.special(7)
Requires=basic.target
Conflicts=rescue.service rescure.target
After=basic.target rescue.service rescue.target
AllowIsolate=yes
[Install]
Alias=default.target
第一部分中的 Requires 定义表明 multi-user.target 启动的时候 basic.target 也必须被启动;另外 basic.target 停止的时候,multi-user.target 也必须停止。如果您接着查看 basic.target 文件,会发现它又指定了 sysinit.target 等其他的单元必须随之启动。同样 sysinit.target 也会包含其他的单元。采用这样的层层链接的结构,最终所有需要支持多用户模式的组件服务都会被初始化启动好。
在 [Install] 小节中有 Alias 定义,即定义本单元的别名,这样在运行 systemctl 的时候就可以使用这个别名来引用本单元。这里的别名是 default.target,比 multi-user.target 要简单一些。。。
此外在 /etc/systemd/system 目录下还可以看到诸如 *.wants 的目录,放在该目录下的配置单元文件等同于在 [Unit] 小节中的 wants 关键字,即本单元启动时,还需要启动这些单元。比如您可以简单地把您自己写的 foo.service 文件放入 multi-user.target.wants 目录下,这样每次都会被默认启动了。
最后,让我们来看看 sys-kernel-debug.mout 文件,这个文件定义了一个文件挂载点:
#cat sys-kernel-debug.mount
[Unit]
Description=Debug File Syste
DefaultDependencies=no
ConditionPathExists=/sys/kernel/debug
Before=sysinit.target
[Mount]
What=debugfs
Where=/sys/kernel/debug
Type=debugfs
这个配置单元文件定义了一个挂载点。挂载配置单元文件有一个 [Mount] 配置小节,里面配置了 What,Where 和 Type 三个数据项。这都是挂载命令所必须的,例子中的配置等同于下面这个挂载命令:
mount –t debugfs /sys/kernel/debug debugfs
配置单元文件的编写需要很多的学习,必须参考 systemd 附带的 man 等文档进行深入学习。希望通过上面几个小例子,大家已经了解配置单元文件的作用和一般写法了。
4.2 对于系统管理员
系统管理员的主要工具是 systemctl。多数管理员应该都已经非常熟悉系统服务和 init 系统的管理,比如 service、chkconfig 以及 telinit 命令的使用。systemd 也完成同样的管理任务,只是命令工具 systemctl 的语法有所不同而已,因此用表格来对比 systemctl 和传统的系统管理命令会非常清晰。
表 2. Systemd 命令和 sysvinit 命令的对照表
Sysvinit 命令 | Systemd 命令 | 备注 |
---|---|---|
service foo start | systemctl start foo.service | 用来启动一个服务 (并不会重启现有的) |
service foo stop | systemctl stop foo.service | 用来停止一个服务 (并不会重启现有的)。 |
service foo restart | systemctl restart foo.service | 用来停止并启动一个服务。 |
service foo reload | systemctl reload foo.service | 当支持时,重新装载配置文件而不中断等待操作。 |
service foo condrestart | systemctl condrestart foo.service | 如果服务正在运行那么重启它。 |
service foo status | systemctl status foo.service | 汇报服务是否正在运行。 |
ls /etc/rc.d/init.d/ | systemctl list-unit-files --type=service | 用来列出可以启动或停止的服务列表。 |
chkconfig foo on | systemctl enable foo.service | 在下次启动时或满足其他触发条件时设置服务为启用 |
chkconfig foo off | systemctl disable foo.service | 在下次启动时或满足其他触发条件时设置服务为禁用 |
chkconfig foo | systemctl is-enabled foo.service | 用来检查一个服务在当前环境下被配置为启用还是禁用。 |
chkconfig –list | systemctl list-unit-files --type=service | 输出在各个运行级别下服务的启用和禁用情况 |
chkconfig foo –list | ls /etc/systemd/system/*.wants/foo.service | 用来列出该服务在哪些运行级别下启用和禁用。 |
chkconfig foo –add | systemctl daemon-reload | 当您创建新服务文件或者变更设置时使用。 |
telinit 3 | systemctl isolate multi-user.target (OR systemctl isolate runlevel3.target OR telinit 3) | 改变至多用户运行级别。 |
除了表 2 列出的常见用法,系统管理员还需要了解其他一些系统配置和管理任务的改变。
首先我们了解 systemd 如何处理电源管理,命令如下表所示:
表 3,systemd 电源管理命令
命令 | 操作 |
---|---|
systemctl reboot | 重启机器 |
systemctl poweroff | 关机 |
systemctl suspend | 待机 |
systemctl hibernate | 休眠 |
systemctl hybrid-sleep | 混合休眠模式(同时休眠到硬盘并待机) |
关机不是每个登录用户在任何情况下都可以执行的,一般只有管理员才可以关机。正常情况下系统不应该允许 SSH 远程登录的用户执行关机命令。否则其他用户正在工作,一个用户把系统关了就不好了。为了解决这个问题,传统的 Linux 系统使用 ConsoleKit 跟踪用户登录情况,并决定是否赋予其关机的权限。现在 ConsoleKit 已经被 systemd 的 logind 所替代。
logind 不是 pid-1 的 init 进程。它的作用和 UpStart 的 session init 类似,但功能要丰富很多,它能够管理几乎所有用户会话 (session) 相关的事情。logind 不仅是 ConsoleKit 的替代,它可以:
- 维护,跟踪会话和用户登录情况。如上所述,为了决定关机命令是否可行,系统需要了解当前用户登录情况,如果用户从 SSH 登录,不允许其执行关机命令;如果普通用户从本地登录,且该用户是系统中的唯一会话,则允许其执行关机命令;这些判断都需要 logind 维护所有的用户会话和登录情况。
- Logind 也负责统计用户会话是否长时间没有操作,可以执行休眠 / 关机等相应操作。
- 为用户会话的所有进程创建 CGroup。这不仅方便统计所有用户会话的相关进程,也可以实现会话级别的系统资源控制。
- 负责电源管理的组合键处理,比如用户按下电源键,将系统切换至睡眠状态。
- 多席位 (multi-seat) 管理。如今的电脑,即便一台笔记本电脑,也完全可以提供多人同时使用的计算能力。多席位就是一台电脑主机管理多个外设,比如两个屏幕和两个鼠标 / 键盘。席位一使用屏幕 1 和键盘 1;席位二使用屏幕 2 和键盘 2,但他们都共享一台主机。用户会话可以自由在多个席位之间切换。或者当插入新的键盘,屏幕等物理外设时,自动启动 gdm 用户登录界面等。所有这些都是多席位管理的内容。ConsoleKit 始终没有实现这个功能,systemd 的 logind 能够支持多席位。
5 总结
在不才作者看来,作为系统初始化系统,systemd 的最大特点有两个:
- 令人惊奇的激进的并发启动能力,极大地提高了系统启动速度;
- 用 CGroup 统计跟踪子进程,干净可靠。
此外,和其前任不同的地方在于,systemd 已经不仅仅是一个初始化系统了。
Systemd 出色地替代了 sysvinit 的所有功能,但它并未就此自满。因为 init 进程是系统所有进程的父进程这样的特殊性,systemd 非常适合提供曾经由其他服务提供的功能,比如定时任务 (以前由 crond 完成) ;会话管理 (以前由 ConsoleKit/PolKit 等管理) 。仅仅从本文皮毛一样的介绍来看,Systemd 已经管得很多了,可它还在不断发展。它将逐渐成为一个多功能的系统环境,能够处理非常多的系统管理任务,有人甚至将它看作一个操作系统。
好的一点是,这非常有助于标准化 Linux 的管理!从前,不同的 Linux 发行版各行其事,使用不同方法管理系统,从来也不会互相妥协。比如如何将系统进入休眠状态,不同的系统有不同的解决方案,即便是同一个 Linux 系统,也存在不同的方法,比如一个有趣的讨论:如何让 ubuntu 系统休眠,可以使用底层的 /sys/power/state 接口,也可以使用诸如 pm-utility 等高层接口。存在这么多种不同的方法做一件事情对像我这样的普通用户而言可不是件有趣的事情。systemd 提供统一的电源管理命令接口,这件事情的意义就类似全世界的人都说统一的语言,我们再也不需要学习外语了,多么美好!
如果所有的 Linux 发行版都采纳了 systemd,那么系统管理任务便可以很大程度上实现标准化。此外 systemd 有个很棒的承诺:接口保持稳定,不会再轻易改动。对于软件开发人员来说,这是多么体贴又让人感动的承诺啊!
今天关于Linux 启动流程和服务管理 (init 和 systemd)和linux启动流程的介绍的讲解已经结束,谢谢您的阅读,如果想了解更多关于android init.rc init.%PRODUCT%.rc 解析命令、Android 初始化语言 (init.*.rc、init.conf 文件格式)、Android 系统 init 进程启动及 init.rc 全解析、CentOS 7 巨大变动之 systemd 取代 SysV 的 Init的相关知识,请在本站搜索。
本文标签: