DEV Community

larson
larson

Posted on

Android system init process startup and init.rc full analysis

This is a blog written with heart, and I hope everyone can read it carefully and help find the improvement of the article, thank you;

Service startup mechanism

  1. parse_config_file (init.rc) in the main function of the system/core/init/init.c file reads and parses the contents of the init.rc file. Place the service information in the service_list of system/core/init/init_parser.cpp
  2. The main function of the system/core/init/init.c file continues to execute restart_servie_if_needed(...) -> service_start(...) -> Execve(...) to establish the service process;

In order to let everyone see more clearly, the last picture first "Overall Startup Framework":
这里写图片描述

init.rc Introduction

At present, Linux has many communication mechanisms that can interact between user space and kernel space, such as device driver files (located in the /dev directory), memory files (/proc, /sys directories, etc.). Students who know Linux should know that one of the important features of Linux is that everything exists in the form of files. For example, a device usually corresponds to one or more device files. These files that interact with the kernel space are in the user space, so after loading in the Linux kernel, you need to first create the directory where these files are located. The program to complete these tasks is the init described in this article. Init is a command line program. One of its main tasks is to create a directory where these files that interact with the kernel space are located. When the Linux kernel is loaded, the first thing to do is to call the init program, that is, init is the first program executed in user space.

Although the work done by init is not much, the code is still very complicated. Init program is not composed of one source code file, but is formed by linking a set of object files of source code files. These files are located in the following directories.

It should be understood that these init.rc are just grammar files, not programs. The real entry point is the system/core/init/init.c mentioned above.
Because the init.c file is relatively large, in the second part of the article I will briefly analyze the init startup process through the main function;

There are two init.rc, located in:
./system/core/rootdir/init.rc
./bootable/recovery/etc/init.rc
It can be guessed from the catalog that the two init.rc usage scenarios are different. One is used for flashing, that is, to enter the recorvery mode, and the other is used for normal startup; our focus here is the above one, which is also init The one associated with .c;

init.rc grammatical structure analysis

To understand how init.rc is parsed, we need to look at the documentation first. The documentation is there. Of course, you can also look at the Chinese version
init.rc is located in /bootable/recovery/etc/init.rc

The Android initialization language contains four types of declarations:
Actions (behavior), Commands (command), Services (service) and Options (options)

All of these are in units of lines, and various signs are separated by spaces.
C-style backslashes can be used to insert spaces between tokens.
Double quotes can also be used to prevent a string from being split into multiple tokens by spaces.
The backslash at the end of the line is used to wrap the line, and the comment line starts with a pound sign (#) (a space is allowed).

It should be noted that this is only a grammar file, just like an xml file, without execution order, the parser obtains the desired data by reading this file, including service, action, etc.

Actions and Services declare a new grouping Section. All commands or options belong to the most recently declared group. Commands or options before the first group will be ignored.
Actions and Services have unique names. If there is a duplicate name, the second statement will be ignored as an error.

Actions

Actions are the start of a series of commands
Actions represent some Actions. Actions represent a set of commands (Commands), and Actions have a trigger (trigger) that determines when to execute this Action, that is, under what circumstances can the defined command in the Action be executed. When some conditions meet the conditions of the trigger, the command defined in the Action will be added to the end of the command queue to be executed (if this group of commands is already in the queue, it will not be added again).

Each action in the queue is extracted in turn, and each command in this action is executed in turn when an Action is removed from the queue.

The format of Action is as follows:

on <trgger> [&& <trigger>]*
   <command1>
   <command2>
   <command3>
   ...
Enter fullscreen mode Exit fullscreen mode

On is followed by a trigger. When the trigger is triggered, command1, command2, and command3 will be executed in sequence until the next Action or the next Service.

To put it simply, Actions is a startup script defined by Android at startup. When the conditions are met, the script will be executed. There are some commands in the script, and different scripts are distinguished by on.

Triggers (Triggers)

Trigger is the trigger we mentioned above, which is essentially a string that can match a certain event containing the string.
Triggers are subdivided into event triggers and property triggers.
Triggers (triggers) is a string used to match specific event types to make Actions happen.

Event triggers can be triggered by the "trigger" command or via QueueEventTrigger() during the initialization process, usually some simple strings defined in advance, such as: boot, late-init
Property trigger is triggered when the variable value of the specified property becomes the specified value, and its format is property:=*

An Action can have multiple attribute triggers, but there is at most one event trigger. Let's look at two examples below:

on boot && property:a=b
Enter fullscreen mode Exit fullscreen mode

This Action will only be triggered when the boot event occurs and the attributes a and b are equal.

on property:a=b && property:c=d
Enter fullscreen mode Exit fullscreen mode

The Action will be triggered in the following three situations:

-At startup, if the value of attribute a is equal to b and the value of attribute c is equal to d
-When the value of attribute c is already d, the value of attribute a is updated to b
-When the value of attribute a is already b, the value of attribute c is updated to d

The following event triggers are commonly used in AIL:

Type Description
-------------------------------------------------
Triggered after boot init.rc is loaded
device-added-<path> triggers when the specified device is added
device-removed-<path> triggers when the specified device is removed
service-exited-<name> is triggered when a specific service (service) exits
Triggered before early-init initialization
Triggered after late-init initialization
Triggered when init is initialized (after /init.conf (startup configuration file) is loaded)
Enter fullscreen mode Exit fullscreen mode

The trigger of Init is determined by the function action_for_each_trigger in init.c (called in the main function).

Services

A service is a program that starts with service and is started by the init process. Generally, it runs in another child process of init. Therefore, it is necessary to determine whether the corresponding executable file exists before starting the service. The sub-processes generated by init are defined in the rc file, and each service in it will generate sub-processes through the fork method at startup. The form of Services is as follows:

service <name> <pathname> [<argument> ]*
    <option>
    <option>
    ...
Enter fullscreen mode Exit fullscreen mode

among them:

-name: service name
-pathname: the program location corresponding to the current service
-option: the option set by the current service
-argument optional parameter

init.rc file detailed

In order to facilitate understanding, I put the whole init.rc analysis aside, so that everyone can understand the whole process; if you want to understand the init syntax analysis under recovery, refer to this article "Init.rc syntax analysis under recovery"
The amount of code is relatively large, if you think it looks laborious, you can pick the green part to see;

# 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> an init configuration file to extend the current configuration.]"
import /init.environ.rc
import /init.usb.rc
import /init.${ro.hardware}.rc
import /init.${ro.zygote}.rc
import /init.trace.rc

"[Trigger condition early-init, call the following line in the early-init phase]"
on early-init
    # Set init and its forked children's oom_adj.
    write /proc/1/oom_score_adj -1000
"[Open a file with path <path> and write one or more strings]"
    # 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.
    "[This script means that the function setcon is called immediately after the init process is started to set its security context to "u:r:init:s0", that is, the domain of the init process is designated as init.]"
    setcon u:r:init:s0

    # Set the security context of /adb_keys if present.
    "[Restore the specified file to the safe online text environment specified in the file_contexts configuration]"
    restorecon /adb_keys

"[Execute the start ueventd command. ueventd is a service with a definition behind it]"
    start ueventd

"[Mkdir <path> [mode] [owner] [group] //Create a directory <path>, you can optionally specify the mode, owner, and group. If not specified, the default permission is 755, and belongs to the root user and root group.]"
    # create mountpoints
    mkdir /mnt 0775 root system

on init
"[Set the reference of the system clock, such as 0 for GMT, which is based on Greenwich Mean Time]"
    sysclktz 0

"[Set Kernel Log Level]"
loglevel 6 ####
    write /proc/bootprof "INIT: on init start" ####
To
"[Symlink <target> <path> //Create a soft link <target> pointing to <path>.]"
    # 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

"[Create a directory <path>, you can optionally specify mode, owner and group.]"
    # Create cgroup mount point for cpu accounting
    mkdir /acct
    mount cgroup none /acct cpuacct
    mkdir /acct/uid

"[Mount <type> <device> <dir> [<mountoption>] //Mount the specified device in the directory <dir>. <device> can specify an mtd block device in the form of mtd@name. <mountoption> Including 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> //Change the owner and group of the file.]"

    "[Some of the following lines are omitted because they are similar]"
    .....

# 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
"[Stop all running services under the specified service category]"
    class_stop charger
    "[Trigger an event, arrange the action after a certain action (for Action queuing)]"
    trigger late-init

# Load properties from /system/ + /factory after fs mount.
on load_all_props_action
"[Load attributes from /system, /vendor. It is included in init.rc by default]"
    load_all_props

# Indicate to fw loaders that the relevant mounts are up.
on firmware_mounts_complete
"[Delete the file under the specified path]"
    rm /dev/.booting

# Mount filesystems and start core system services.
on late-init
"[Trigger an event. Used to arrange an action with another 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
...
    "[Some operations for creating directories, establishing links, and changing permissions are omitted here]"

on post-fs-data
...
"[Some operations for creating directories, establishing links, and changing permissions are omitted here]"

"[Restore the specified file to the safe online text environment specified in the file_contexts configuration]"
    restorecon /data/mediaserver

"[Set the value of the system property <name> to <value>, that is, set the system property in the form of key-value pairs]"
    # Reload policy from /data/security if present.
    setprop selinux.reload_policy 1

"[Recursively restore the specified directory to the security context specified in the file_contexts configuration]"
    # 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
"[Initialize Network]"
    # basic network init
    ifup lo
    "[Set the host name to localhost]"
    hostname localhost
    "[Set the domain name localdomain]"
    domainname localdomain

"[Set Resource Limit]"
    # set RLIMIT_NICE to allow priorities from 19 to -20
    setrlimit 13 40 40

"[Some chmod, chown, and other operations are omitted here, no more explanation]"
   ...


    # Define default initial receive window size in segments.
    setprop net.tcp.default_init_rwnd 60
To
"[Restart core service]"
    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 Process]"
## Daemon processes to be run by init.
##
service ueventd /sbin/ueventd
    class core
    critical
    seclabel u:r:ueventd:s0

"[Log Service Process]"
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 is an intermediary model proposed after android4.4. This model listens to battery events from the bottom layer and transmits battery data information upward to the BatteryService of the Framework layer to calculate battery power related status information]"
service healthd /sbin/healthd
    class core
    critical
    seclabel u:r:healthd:s0

"[Console Process]"
service console /system/bin/sh
"[Set a category for the current service. Services of the same category will be started or stopped at the same time, the default class name is default]"
    class core
    "[Service requires a console]"
    console
    "[The service will not start automatically, it must be explicitly started by the service name]"
    disabled
    "[Switch the user name before executing this service, the current default is root. Since Android M, even if it requires linux capabilities, this option should be used. Obviously, in order to get this function, the process needs to be run as 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
    "[Create a socket under the unix domain, which is named /dev/socket/<name>. And returns its file descriptor fd to the service process. Among them, the type must be dgram, stream or seqpacke, and the default for user and group is 0.seclabel is the security context of the SELLinux of the socket, the default is the context of the current service, specified by 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

"[Memory management service, memory is insufficient to release memory]"
service lmkd /system/bin/lmkd
    class core
    critical
    socket lmkd seqpacket 0660 system system

"[ServiceManager is a daemon process that maintains the binder communication between system services and clients.
The most used communication mechanism in the Android system is Binder, which is mainly composed of Client, Server, ServiceManager and Binder drivers. Among them, Client, Service, and ServiceManager run in user space, while Binder driver runs in kernel space. The core component is the Binder driver, and ServiceManager provides auxiliary management functions, whether it is Client or Service before communicating with ServiceManager. The ServiceManager is a daemon process that is responsible for managing the Server and providing the client with the function of querying the Server. 】"
service servicemanager /system/bin/servicemanager
    class core
    user system
    group system
    critical
    onrestart restart healthd
    "[Zygote service will be restarted when the servicemanager service starts]"
    onrestart restart zygote
    onrestart restart media
    onrestart restart surfaceflinger
    onrestart restart drm

"[Vold is the abbreviation of Volume Daemon, it is the control center of the external storage system in the Android platform, and is the background process for managing and controlling the external storage device of the Android platform]"
service vold /system/bin/vold
    class core
    socket vold stream 0660 root mount
    ioprio be 2

"[Netd is a background daemon program specifically responsible for network management and control in the Android system]"
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 is a daemon process, which starts with the init process when the system starts. It is mainly responsible for dumping the information of the process running to a file or console]"
service debuggerd /system/bin/debuggerd
    class main

service debuggerd64 /system/bin/debuggerd64
    class main

"[Android RIL (Radio Interface Layer) provides an abstraction layer between Telephony services and Radio hardware]"
# 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

"[Provides a system-wide surface composer function, which can combine 2D and 3D surfaces of various applications.]"
service surfaceflinger /system/bin/surfaceflinger
    class core
    user system
    group graphics drmrpc
    onrestart restart zygote

"[DRM can directly access the hardware of DRM clients. The DRM driver is used to handle DMA, memory management, resource locks, and secure hardware access. In order to support multiple 3D applications at the same time, the 3D graphics card hardware must be used as a shared resource, so a lock is required Provide mutually exclusive access. DMA transfer and AGP interface are used to send buffers for graphics operations to the graphics hardware, so it is necessary to prevent the client from unauthorized access to the graphics hardware.]"
#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 ####

"[Media Services, no need to say more]"
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

"[Device Encryption Related Services]"
# One shot invocation to deal with encrypted volume.
service defaultcrypto /system/bin/vdc --wait cryptfs mountdefaultencrypted
    disabled
    "[When the service exits, do not restart the service]"
    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)

"[Startup Animation Service]"
service bootanim /system/bin/bootanimation
    class core
    user graphics
# group graphics audio ####
    group graphics media audio ####
    disabled
    oneshot

"[In the Android system, PackageManagerService is used to manage all the installation package information in the system and the installation and uninstallation of applications, but the installation and uninstallation of applications is not completed by PackageManagerService, but through PackageManagerService to access the installd service to execute the package Installed and uninstalled.]"
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 related services]"
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

"[There are mtpd commands in android to connect to 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

"[You can use dumpstate to obtain various information about the device]"
service dumpstate /system/bin/dumpstate -s
    class main
    socket dumpstate stream 0660 shell log
    disabled
    oneshot

"[Mdnsd is a daemon for multicast DNS and DNS service discovery.]"
service mdnsd /system/bin/mdnsd
    class main
    user mdnsr
    group inet net_raw
    socket mdnsd stream 0660 mdnsr inet
    disabled
    oneshot

"[Trigger shutdown process to continue down]"
service pre-recovery /system/bin/uncrypt
    class main
    disabled
    "[When the service exits, do not restart the service]"
    oneshot

Enter fullscreen mode Exit fullscreen mode

init.c full analysis

Next, we analyze the execution process of the following main function in detail; it may be relatively long, so please take a look:


int main( int argc, char **argv)
{
#Create some directories in the linux root file system
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's standard input, standard output, and standard error file descriptors are directed to __null__, which means that there is no input and output, and all its input and output are written to Log
open_devnull_stdio();
#Initial log Write init information
log_init();
#Read and parse the init.rc file (this file is in the root directory)
parse_config_file( "/init.rc" );
#Get hardware To print our device name fs100
get_hardware_name();
snprintf( tmp, sizeof(tmp), "/init.%s.rc", hardware );
    #Read and parse hardware-related init script files,
parse_config_file( tmp );
# Trigger the action named early-init in the init script file and execute its commands, which is actually: on early-init
action_for_each_trigger( "early-init", action_add_queue_tail );
drain_action_queue();
#Initialize dynamic device management, react to the kernel when the device file changes, which will be explained later
device_fd = device_init(); # Initialize device management
#Load the startup animation. If the animation fails to open, print on the screen: 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 "; boot animation */
write( fd, msg, strlen( msg) );
close( fd );
}
}

#Trigger The action named init in the init script file and execute its commands is actually: on init
action_for_each_trigger( "init", action_add_queue_tail );
drain_action_queue();
#Start the system property service: system property service
property_set_fd = start_property_service();
#Create socket to handle orphan process signals
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 );
}
#Trigger The actions named early-boot and boot in the init script file and execute their commands are actually: on early-boot and on boot
action_for_each_trigger( "early-boot", action_add_queue_tail );
action_for_each_trigger( "boot", action_add_queue_tail );
drain_action_queue();
#Start all property change trigger commands, which is actually: on property:ro.xx.xx=xx
queue_all_property_triggers();
drain_action_queue();
#Enter the infinite loop ()
for (;;)
{
#Start all services declared in the init script,
#如:266 service servicemanager /system/bin/servicemanager
#user system
#critical
#onrestart restart zygote
#onrestart restart media
restart_processes();
#Multi-channel monitoring equipment management, child process running status, attribute service
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);
}

Enter fullscreen mode Exit fullscreen mode

Top comments (0)