これは心から書いたブログですので、よくお読みいただき、記事の改善点を見つけていただければ幸いです。ありがとうございます。
サービス起動メカニズム
- system / core / init / init.cファイルのmain関数のparse_config_file(init.rc)は、init.rcファイルの内容を読み取って解析します。system / core / init /init_parser.cppのservice_listにサービス情報を配置します。
- system / core / init / init.cファイルの主な機能は、restart_service_if_needed(...)-> service_start(...)-> Execve(...)を実行し続け、サービスプロセスを確立します。
誰もがよりはっきりと見えるようにするために、最後の写真は最初に「全体的なスタートアップフレームワーク」です。
init.rcの紹介
現在、Linuxには、デバイスドライバファイル(/ devディレクトリにあります)、メモリファイル(/ proc、/ sysディレクトリなど)など、ユーザースペースとカーネルスペースの間で相互作用する多くの通信メカニズムがあります。Linuxを知っている学生は、Linuxの重要な機能の1つは、すべてがファイルの形式で存在することです。たとえば、デバイスは通常、1つ以上のデバイスファイルに対応します。カーネルスペースと相互作用するこれらのファイルはユーザースペースにあるため、Linuxカーネルにロードした後、最初にこれらのファイルが配置されているディレクトリを作成する必要があります。これらのタスクを完了するプログラムは、この記事で紹介するinitです。initはコマンドラインプログラムです。その主なタスクの1つは、カーネル空間と相互作用するこれらのファイルが配置されるディレクトリを作成することです。Linuxカーネルがロードされたら、最初に行うことはinitプログラムを呼び出すことです。つまり、initはユーザースペースで実行される最初のプログラムです。
initによって行われる作業はそれほど多くありませんが、コードは依然として非常に複雑です。Initプログラムは、ソースコードファイルで構成されていませんが、ソースコードファイルのオブジェクトファイルのセットをリンクすることによって形成されています。これらのファイルは、次のディレクトリにあります。
これらのinit.rcは単なる文法ファイルであり、プログラムではないことを理解する必要があります。実際のエントリポイントは、上記のsystem / core / init /init.cです。init.rcファイルは比較的大きいため、記事の後半では、main関数を使用してinitの起動プロセスを簡単に分析します。
次の場所に2つのinit.rcがあります。
./system/core/rootdir/init.rc
./bootable/recovery/etc/init.rc
カタログから、2つのinit.rcの使用シナリオが異なることが推測できます.1つはフラッシュ、つまりリカバリモードに入るのに使用され、もう1つは通常の起動に使用されます。ここで焦点を当てるのは上記のシナリオです。 init.cに関連付けられているもの。
init.rcの文法構造分析
init.rcがどのように解析されるかを理解するには、最初にドキュメントを確認する必要があります。
init.rcは/bootable/recovery/etc/init.rcにあります。
Android初期化言語には、次の4種類の宣言が含まれています。
- 行動
- コマンド
- サービス
- オプション
これらはすべて行単位であり、さまざまな数値はスペースで区切られています。
Cスタイルの円記号を使用して、トークン間にスペースを挿入できます。
二重引用符を使用して、文字列がスペースによって複数のトークンに分割されるのを防ぐこともできます。
行末の円記号は行を折り返すために使用され、コメント行は「#」記号で始まります(スペースは使用できます)。
これは、xmlファイルと同様に、単なる文法ファイルであり、実行順序はありません。パーサーはこのファイルを読み取って、サービスやアクションなどの目的のデータを取得します。
アクションとサービスは、新しいグループ化セクションを宣言します。すべてのコマンドまたはオプションは、最後に宣言されたグループに属します。最初のグループの前のコマンドまたはオプションは無視されます。
アクションとサービスには一意の名前があります。名前が重複している場合、2番目のステートメントはエラーとして無視されます。
行動
アクションはいくつかのアクションを表します。アクションは一連のコマンド(コマンド)を表します。アクションには、このアクションをいつ実行するか、つまり、アクションで定義されたコマンドを実行できる状況を決定するトリガー(トリガー)があります。一部の条件がトリガーの条件を満たしている場合、アクションで定義されたコマンドは、実行されるコマンドキューの最後に追加されます(このコマンドのグループがキューにある必要がある場合、追加されません)。
キュー内の各アクションは1回抽出され、アクションがキューから削除されると、このアクション内の各コマンドが順番に実行されます。
アクションの形式は次のとおりです。
on <trigger> [&& <trigger>]*
<command1>
<command2>
<command3>
...
オンの後にトリガーが続きます。トリガーがトリガーされると、command1、command2、およびcommand3が、次のアクションまたは次のサービスまで順番に実行されます。
簡単に言うと、アクションは起動時にAndroidによって定義される起動スクリプトです。条件が満たされると、スクリプトが実行されます。スクリプトにはいくつかのコマンドがあります。異なるスクリプトはオンで区別されます。
引き金
トリガーは、前述のトリガーであり、基本的に、文字列を含む特定のイベントに一致する可能性のある文字列です。
トリガーは、イベントトリガーとプロパティトリガーに細分されます。
トリガーは、アクションを発生させるために特定のイベントタイプに一致するために使用される文字列です。
イベントトリガーは、「trigger」コマンドまたは初期化中にQueueEventTrigger()を介してトリガーできます。通常、これらは、boot、late-initなどの事前に定義された単純な文字列です。
プロパティトリガーは、指定されたプロパティの変数値が指定された値になり、その形式がproperty:= *になるとトリガーされます。
アクションには複数の属性トリガーを含めることができますが、最大で1つのイベントトリガーがあります。以下の2つの例を見てみましょう。
on boot && property: a=b
このアクションは、ブートイベントが発生し、属性aとbが等しい場合にのみトリガーされます。
on property:a=b && c=d
アクションは、次の3つの状況でトリガーされます。
- 起動時に、属性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(启动配置文件)被装载之后)
Initのトリガーは、init.cの関数action_for_each_trigger(メイン関数で呼び出されます)によって決定されます。
サービス
サービス(service)は、serviceで起動し、initプロセスで起動するプログラムです。通常、別のinitサブプログラムで実行されるため、サービスを起動する前に、対応する実行可能ファイルが存在するかどうかを確認する必要があります。initによって生成された子プロセスはrcファイルで定義されており、その中の各サービスは、開始時にforkによって子プロセスを生成します。サービスの形式は次のとおりです。
service <name> <pathname> [<argument>] *
<option>
<option>
<option>
...
その中で:
- 名前:サービス名;
- パス名:現在のサービスによって設定されたオプション。
- オプション:サービスによって設定されたオプション。
- 引数:オプションのパラメーター。
詳細なinit.rcファイル
理解を容易にするために、プロセス全体を理解できるようにinit.rc全体を解析します。リカバリ中のinit構文分析を理解したい場合は、この記事「リカバリ中の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