The 11th holiday is a bit of a depravity, unlimited firepower a bit of an addiction, caution, caution
The Init process is the first user process created after the Linux kernel is started and is very important.
The Init process starts many important daemons during initialization, so understanding the Init process can help us better understand the Android system.
Before introducing the Init process, let’s take a quick look at the Android startup process. From a system perspective, the Android startup process can be divided into three major stages:
Bootloader guide
Load and start the Linux kernel
Start the Android system
, can be divided into- Start the
The Init process
- Start the
Zygote
- Start the
SystemService
- Start the
SystemServer
- Start the
Home
- And so on…
- Start the
Let’s take a look at the startup process diagram:
The following is a brief introduction to the startup process:
Bootloader
guide
When you press the power button to start up, the Bootloader runs first
Bootloader
The main function is to initialize basic hardware devices (such as CPU, memory, Flash, etc.) and establish memory space mapping for loadingThe Linux kernel
Prepare the proper operating environment.- Once the
The Linux kernel
Loaded,Bootloader
It will be erased from memory - If the
Bootloader
During runtime, press the predefined key combination to enter the update module of the system.Android
The download update can be opt-inFastboot mode
orRecovery mode
:Fastboot
isAndroid
Design a set throughUSB
To update theAndroid
A protocol for partitioning images that allows developers to quickly update specified partitions.Recovery
isAndroid
Unique upgrade system. usingRecovery
Mode can be used to restore factory Settings, or perform OTA, patch, and firmware upgrades. Enter theRecovery
Mode actually starts a text modeLinux
- Load and start
Linux
The kernel
The Android boot.img file stores the Linux kernel and a root file system
Bootloader
theboot.img
The image loads into memory- then
Linux
The kernel performs initialization of the entire system - Then loading
Root file system
- Finally start
Init
process
- Start the
Init
process
After the Linux kernel is loaded, the Init process is started, which is the first process on the system
Init
The process is resolved during startupLinux
Configuration script ofinit.rc
File. According to theinit.rc
The contents of the document,Init
Process will be:- loading
Android
File system of - Creating a system directory
- Initialize the property system
- Start the
Android
System important daemons, such asUSB daemon
,Adb daemon
,Vold daemon
,Rild daemon
Etc.
- loading
- In the end,
Init
Processes also act as daemons to modify properties, restart crashed processes, and so on
- Start the
ServiceManager
ServiceManager is started by the Init process. As mentioned in Binder section, its main function is to manage Binder services and be responsible for the registration and search of Binder services
- Start the
Zygote
process
Zygote process is started when Init process initialization is complete. The Zygote process is responsible for forking out application processes and is the parent of all application processes
Zygote
Created during process initializationAndroid virtual machine
, preloaded system resource files andJava
class- All from
Zygote
processfork
Out user processes will inherit and share these preloaded resources without wasting time reloading, speeding up the application startup process - After the startup,
Zygote
The process also becomes a daemon, responsible for responding to startupAPK
The request of
- Start the
SystemServer
SystemServer is the Zygote process fork out of the first process, is the core process of the entire Android system
SystemServer
In the runningAndroid
Most of the systemBinder service
SystemServer
Start the local service firstSensorManager
- Then the boot includes
ActivityManagerService
,WindowsManagerService
,PackageManagerService
All withinJava service
- Start the
MediaServer
MediaServer is started by the Init process. It includes several multimedia related local Binder services, including CameraService, AudioFlingerService, MediaPlayerService, AudioPolicyService
- Start the
Launcher
SystemServer
Load all of themJava service
After, the final call is madeActivityManagerService
theSystemReady()
methods- in
SystemReady()
Method, will emitIntent<android.intent,category.HOME>
- Anything that responds to this
Intent
theapk
It’s going to work, generallyLauncher
The application goes back and responds to thisIntent
Init
Initialization of a process
The Init process source directory is under system/core/ Init. The program’s entry function main() is in the file init.c
main()
Flow of a function
The book is the use of Android 5.0 source code, compared to Android 9.0 this part has a lot of changes, but the direction is consistent, can only be compared to learn…
The main() function is a long one that starts the entire Init process. Because there are many points involved, here we first understand the overall process, details will be added later, bit by bit ha
The structure of the main() function of the Init process looks like this:
int main(int argc, char** argv) {
// Start parameter judgment section
if (is_first_stage) {
// Initialize the first phase section
}
// Initialize the phase 2 part
while (true) {
// An infinite loop part
}
Copy the code
Start program parameter judgment
After entering the main() function, first check the filename of the launcher
Function source code:
if (!strcmp(basename(argv[0]), "ueventd")) {
return ueventd_main(argc, argv);
}
if (!strcmp(basename(argv[0]), "watchdogd")) {
return watchdogd_main(argc, argv);
}
Copy the code
- If the file name is
ueventd
, the implementation ofueventd
The main function of the daemonueventd_main
- If the file name is
watchdogd
, the implementation ofwatchdogd
The main function of the daemonwatchdogd_main
- If no, continue
Strange enough to start with, the Init process also includes the start of two other daemons, mainly because the code of these daemons overlaps so much that the developers simply put them together.
Let’s take a look at the snippet from Android.mk:
# Create symlinks.
LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT)/sbin; \ ln -sf .. /init$(TARGET_ROOT_OUT)/sbin/ueventd; \ ln -sf .. /init$(TARGET_ROOT_OUT)/sbin/watchdogd
Copy the code
- At compile time,
Android
Two Pointers are generatedinit
Symbolic links to filesueventd
andwatchdogd
- So, when you start up with these two symlinks,
main()
The function can determine which one to start based on the name
The first phase of initialization
Set the file property mask
Function source code:
// Clear the umask.
umask(0);
Copy the code
By default, a process creates a file folder with the attribute 022. Using umask(0) means that the process creates the attribute 0777
mount
Corresponding file system
Function source code:
// Get the basic filesystem setup we need put together in the initramdisk
// on / and then we'll let the rc file figure out the rest.
mount("tmpfs"."/dev"."tmpfs", MS_NOSUID, "mode=0755");
mkdir("/dev/pts".0755);
mkdir("/dev/socket".0755);
mount("devpts"."/dev/pts"."devpts".0.NULL);
#define MAKE_STR(x) __STRING(x)
mount("proc"."/proc"."proc".0."hidepid=2,gid=" MAKE_STR(AID_READPROC));
/ /...
mount("sysfs"."/sys"."sysfs".0.NULL);
mount("selinuxfs"."/sys/fs/selinux"."selinuxfs".0.NULL);
/ /...
// Mount staging areas for devices managed by vold
// See storage config details at http://source.android.com/devices/storage/
mount("tmpfs"."/mnt"."tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
"mode=0755,uid=0,gid=1000");
// /mnt/vendor is used to mount vendor-specific partitions that can not be
// part of the vendor partition, e.g. because they are mounted read-write.
mkdir("/mnt/vendor".0755);
InitKernelLogging(argv);
Copy the code
Create some basic directories, such as /dev, /proc, /sys, and mount some file systems, such as TMPFS, devpt, proc, and sysfs, to these directories
tmpfs
Is a memory-based file system,mount
Then you can use it.tmpfs
All files in the file system are stored in the memory. The access speed is fast, but the files are lost during power failure. Therefore, it is suitable for storing some temporary filestmpfs
The size of a file system changes dynamically. It takes up a small amount of space at first and then increases as more files are addedAndroid
willtmpfs
The file systemmount
to/dev
Directory./dev
The directory is used to store device nodes created by the system
devpts
Is a virtual terminal file system, usuallymount
to/dev/pts
directoryproc
It is also a memory-based virtual file system that can be thought of as an interface to the kernel’s internal data structures- It is used to obtain information about the system
- You can also modify specific kernel parameters at run time
sysfs
File system andproc
The file system is similar, it is inLinux 2.6
The kernel is introduced to organize system devices and buses in a hierarchy that allows them to be accessed in user space
Initialize thekernel
theLog
system
InitKernelLogging() is used for initialization. Since the Android log system has not been started, Init can only use the kernel log system
// Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually
// talk to the outside world...
InitKernelLogging(argv);
Copy the code
Initialize theSELinux
// Set up SELinux, loading the SELinux policy.
SelinuxSetupKernelLogging();
SelinuxInitialize();
Copy the code
SELinux is a secure kernel added to Android 4.3, more on this later
The second phase of initialization
create.booting
Empty file
Create an empty file in the /dev directory. Booting indicates initialization is in progress
// At this point we're in the second stage of init.
InitKernelLogging(argv);
LOG(INFO) << "init second stage started!";
/ /...
// Indicate that booting is in progress to background fw loaders, etc.
close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
Copy the code
Note that we are already in phase two of initialization
is_booting()
The function will rely on empty files.booting
To determine if the process is being initialized- The file will be deleted after initialization
Initialize theAndroid
Attribute system of
property_init();
Copy the code
The property_init() function creates a shared area to store property values, as described below
parsingkernel
Parameters and related Settings
// If arguments are passed both on the command line and in DT,
// properties set in DT always have priority over the command-line ones.
process_kernel_dt();
process_kernel_cmdline();
// Propagate the kernel variables to internal variables
// used by init as well as the current required properties.
export_kernel_boot_props();
// Make the time that init started available for bootstat to log.
property_set("ro.boottime.init", getenv("INIT_STARTED_AT"));
property_set("ro.boottime.init.selinux", getenv("INIT_SELINUX_TOOK"));
// Set libavb version for Framework-only OTA match in Treble build.
const char* avb_version = getenv("INIT_AVB_VERSION");
if (avb_version) property_set("ro.boot.avb_version", avb_version);
Copy the code
This section is about setting properties. Let’s look at some key methods:
process_kernel_dt()
Function: Read device tree (DT
), find system properties, and then passproperty_set
Setting System Propertiesprocess_kernel_cmdline()
Function: parsingkernel
thecmdline
File extraction toandroidboot.
A string that starts with a string, passesproperty_set
Set the system propertiesexport_kernel_boot_props()
Function: sets some additional properties. This function defines a collection of properties defined in the collection fromkernel
Read and record
For the second stageSELinux
Set up the
Perform stage 2 SELinux setup and restore some file security contexts
// Now set up SELinux for second stage.
SelinuxSetupKernelLogging();
SelabelInitialize();
SelinuxRestoreContext();
Copy the code
Initializes the child process termination signal handler
epoll_fd = epoll_create1(EPOLL_CLOEXEC);
if (epoll_fd == - 1) {
PLOG(FATAL) << "epoll_create1 failed";
}
sigchld_handler_init();
Copy the code
In Linux, the parent process signals the end of the child process by catching a SIGCHLD signal, which is emitted when the child process terminates.
- In order to prevent
init
Becomes the child process ofzombies
(zombie process),init
Gets the end code of the child process when the child process ends - The end code is used to remove the subprocesses from the program table to prevent the zombie subprocesses from occupying the space in the program table
- When the program table space reaches its maximum, the system can no longer start new processes, which can cause serious system problems
Set system properties and enable the property service
property_load_boot_defaults();
export_oem_lock_status();
start_property_service();
set_usb_controller();
Copy the code
property_load_boot_defaults()
,export_oem_lock_status()
,set_usb_controller()
All three of these functions call and set system propertiesstart_property_service()
: Enables the system property service
loadinginit.rc
file
Init. rc is a configurable initialization file that is used in Android as a startup script for a program. It stands for Run Commands run command
Typically, third-party custom vendors can configure an additional initialization configuration: init.%PRODUCT%.rc. The configuration file is parsed during init initialization to complete the customized configuration.
The rules and logic of the init.rc file will be explained in detail below. First, take a look at its flow in the main function.
Function code:
const BuiltinFunctionMap function_map;
Action::set_function_map(&function_map);
subcontexts = InitializeSubcontexts();
// Create an action object
ActionManager& am = ActionManager::GetInstance();
// Create a service object
ServiceList& sm = ServiceList::GetInstance();
// Load and parse the init.rc file into the corresponding object
LoadBootScripts(am, sm);
Copy the code
After parsing is complete, execution is performed
am.QueueEventTrigger("early-init");
// Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
// Wait until the initialization of the cold swap device is complete
am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
// ... so that we can start queuing up actions that require stuff from /dev.
am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
// Initialize the combination key listening module
am.QueueBuiltinAction(keychord_init_action, "keychord_init");
// Display Android Logo on the screen
am.QueueBuiltinAction(console_init_action, "console_init");
// Trigger all the boot actions to get us started.
am.QueueEventTrigger("init");
// Starting the BoringSSL self test, for NIAP certification compliance.
am.QueueBuiltinAction(StartBoringSslSelfTest, "StartBoringSslSelfTest");
// Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
// wasn't ready immediately after wait_for_coldboot_done
am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
// Don't mount filesystems or start core system services in charger mode.
std: :string bootmode = GetProperty("ro.bootmode"."");
if (bootmode == "charger") {
am.QueueEventTrigger("charger");
} else {
am.QueueEventTrigger("late-init");
}
// Run all property triggers based on current state of the properties.
am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");
Copy the code
am.QueueEventTrigger
The function indicates that a desired time condition has been reached- Such as
am.QueueEventTrigger("early-init")
Show thatearly-init
Conditional trigger, corresponding action can start execution - Note that this function only takes the point in time (e.g.
early-init
) to fill inevent_queue_
Run queue - At the back of the
while(true)
The loop will actually fetch in order and trigger the corresponding action
- Such as
Here,
init.rc
The relevantaction
andservice
Parsing completed- The corresponding list is ready
- The corresponding
Trigger
It has also been added
Now comes the execution phase:
while (true) {
// Execute the Action in the command list
if(! (waiting_for_prop || Service::is_exec_service_running())) { am.ExecuteOneCommand(); }if(! (waiting_for_prop || Service::is_exec_service_running())) {if(! shutting_down) {// Start the service in the service list
auto next_process_restart_time = RestartProcesses();
/ /...
}
/ /...
}
// Listen for the child's death notification
epoll_event ev;
int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, epoll_timeout_ms));
if (nr == - 1) {
PLOG(ERROR) << "epoll_wait failed";
} else if (nr == 1) {((void(*)()) ev.data.ptr)(); }}Copy the code
At this point, the overall flow of the main function is analyzed. We lost a lot of detail in the analysis, and now it’s time to fill in the details.
Start theService
process
RestartProcesses() is called in the while() loop of main to start the service processes in the list of services.
static std::optional<boot_clock::time_point> RestartProcesses(a) {
/ /...
// Loop through each service
for (const auto& s : ServiceList::GetInstance()) {
// Check whether the flag bit is SVC_RESTARTING
if(! (s->flags() & SVC_RESTARTING))continue;
/ /... Omit time dependent judgments
// Start the service process
auto result = s->Start();
}
/ /...
}
Copy the code
RestartProcesses() checks each service: s->Start() is used to Start services that have the SVC_RESTARTING flag.
S ->Start(); Method, let’s look at it in detail (deleted version) :
Result<Success> Service::Start(a) {
/ /... Omit the part
// Clear service tags
// Check the status of the service
// Check whether the service binary exists
// Initialize console, SCON (security context), etc
/ /... Omit the part
pid_t pid = - 1;
if (namespace_flags_) {/ / when the service defines the namespace assignment for CLONE_NEWPID | CLONE_NEWNS
pid = clone(nullptr.nullptr, namespace_flags_ | SIGCHLD, nullptr);
} else {
pid = fork();
}
if (pid == 0) {// The child process was successfully created
/ /... Omit the part
// Setenv, writepID, redirection standard IO
/ /... Omit the part
// As requested, set our gid, supplemental gids, uid, context, and
// priority. Aborts on failure.
SetProcessAttributes();
if(! ExpandArgsAndExecv(args_)) {// Parse the parameters and start the service
PLOG(ERROR) << "cannot execve('" << args_[0] < <"')";
}
// Do not understand the purpose of this exit
_exit(127);
}
if (pid < 0) {
// Failed to create the child process
pid_ = 0;
return ErrnoError() << "Failed to fork";
}
/ /... Omit the part
// Execute other parameters of service, such as oom_score_adj, create and set parameters related to ProcessGroup
/ /... Omit the part
NotifyStateChange("running");
return Success();
}
Copy the code
There are many more details about the startup process of a Service. This section is a brief overview of the process, including scON, PID, SID, PGID, and more.
Lazy! Ability, time is limited, first go down to learn, to be used to gnaw it
Parsing startup scriptsinit.rc
The most important thing to do when the Init process starts is to parse and execute the startup file init.rc. Download link for official documentation
init.rc
The file format
The init.rc file is organized in sections
-
Sections fall into two broad categories:
action
: by keywordon
Start represents a collection of commandsservice
: by keywordservice
Start: Indicates the method and parameters for starting a process
-
Section starts with the keyword on or service and ends with the next ON or service
-
Comments in section begin with #
Give me an example:
import /init.usb.rc
import /init.${ro.hardware}.rc
on early-init
mkdir /dev/memcg/system 0550 system system
start ueventd
on init
symlink /system/bin /bin
symlink /system/etc /etc
on nonencrypted
class_start main
class_start late_start
on property:sys.boot_from_charger_mode=1
class_stop charger
trigger late-init
service ueventd /sbin/ueventd
class core
critical
seclabel u:r:ueventd:s0
shutdown critical
service flash_recovery /system/bin/install-recovery.sh
class main
oneshot
service ueventd /sbin/ueventd
class core
critical
seclabel u:r:ueventd:s0
shutdown critical
Copy the code
Neither actions nor services are executed in the order written in the file, and it is up to the Init process at runtime to decide if and when they are executed.
For init.rc action:
- The keyword
on
The string that follows is calledtrigger
, as aboveearly-init
,init
And so on. trigger
This is followed by a list of commands, each line of which is a command.
For service init.rc:
- The keyword
service
The back isThe service name
, you can usestart
addThe service name
To start a service. Such asstart ueventd
The service name
This is followed by the path to the process’s executable and startup parametersservice
The following lines are calledoption
, eachoption
A line- Such as:
class main
In theclass
Indicates the category to which the service belongsclass_start
To start a set of services likeclass_start main
- Such as:
Want to learn more, you can refer to the README document source, path is the system/core/init/README md
init.rc
The key word
This part is for the system/core/init/README. Md document finishing, pick the key records
The Android RC script contains four types of declarations: Action, Commands, Services, and Options
- All instructions are in units of action, and symbols are separated by Spaces.
The c language
Style of backslash\
Can be used to insert Spaces between symbols- Double quotation marks
""
Can be used to prevent strings from being split into multiple tokens by Spaces - Backslash at the end of the line
\
Can be used for line folding - Comments to
#
At the beginning Action
andServices
Used to declare a group- All of the
Commands
andOptions
Belong to the most recently declared group - Before the first group
Commands
orOptions
Will be ignored
- All of the
Actions
Actions is a collection of Commands. Each Action has a trigger that determines when to execute it. When the trigger matches the Action’s trigger, the Action is added to the end of the execution queue
Each Action is fetched from the queue in turn, and each Command of the Action is executed in turn.
The format of Actions is as follows:
on < trigger >
< command >
< command >
< command >
Copy the code
Services
Programs defined by Services are started in Init and restarted if they exit.
The format of Services is as follows:
service <name> <pathname> [ <argument> ]*
<option>
<option>
...
Copy the code
Options
Options is a modification of Services. They determine when and how a Service runs.
critical
: indicates that this is a keyService
If theService
If you restart the system for more than four times within four minutes, the system automatically restartsrecovery
modelconsole [<console>]
: indicates that the service requires a console- The second parameter is used to specify a specific console name, which defaults to
/dev/console
- Omit when specifying
/dev/
Some, such as/dev/tty0
To be writtenconsole tty0
- The second parameter is used to specify a specific console name, which defaults to
disabled
: indicates that the service failsclass_start
Start. It must be orderedstart service_name
Is specified to startsetenv <name> <value>
In:Service
Start up with environment variablesname
Set tovalue
socket <name> <type> <perm> [ <user> [ <group> [ <seclabel> ] ] ]
: Creates a file named/dev/socket/<name>
And passes the file descriptor to the process to starttype
Must bedgram
,stream
,seqpacket
user
andgroup
The default is 0seclabel
Is thissocket
theSElinux
Security context, which defaults to currentservice
The context in which the
user <username>
: Switch the user name before executing the service. The default is root. If the process does not have corresponding permissions, the command cannot be usedoneshot
:Service
The system does not restart after exitingclass <name>
:Service
Specify a name. All services with the same name can be started and stopped simultaneously. If you don’t passclass
Displays the specified, default isdefault
onrestart
: whenService
During the restart, run a command
There are a lot of other things, I’m not going to go through all of them, like shutdown
, just follow the official instructions
Triggers
A trigger is essentially a string that matches some kind of event containing that string. Trigger is subdivided into event trigger and property trigger.
-
Event triggers can be triggered by the trigger command or by QueueEventTrigger() during initialization
- Usually simple strings defined in advance, such as:
boot
.late-init
Etc.
- Usually simple strings defined in advance, such as:
-
A property trigger is fired when the variable value of the specified property becomes the specified value
- The format for
property:<name>=*
- The format for
Note that an Action can have multiple property triggers, but at most one event trigger. Take a look at the official example:
on boot && property:a=b
Copy the code
The Action above is triggered only when a boot event occurs and property A is equal to value B.
For the following Action
on property:a=b && property:c=d
Copy the code
There are three trigger cases:
- At startup, if
Property a
The value is equal to theb
andProperties of the c
The value is equal to thed
- in
Properties of the c
The value of theta is already zerod
In the case of,Property a
The value of is updated tob
- in
Property a
The value of theta is already zerob
In the case of,Properties of the c
The value of is updated tod
Event triggers generally include:
type | instructions |
---|---|
boot |
Triggered when init.rc is loaded |
device-added-<path> |
Triggered when a device is added |
device-removed-<path> |
Triggered when the specified device is removed |
service-exited-<name> |
Triggered when a particular service exits |
early-init |
Triggered before initialization |
late-init |
Triggered after initialization |
init |
Triggered during initialization |
Commands
Command is the list of commands used for Action or in the Option
of Service.
static const Map builtin_functions = {
{"chmod", {2.2, {true, do_chmod}}},
{"chown", {2.3, {true, do_chown}}},
{"class_start", {1.1, {false, do_class_start}}},
{"class_stop", {1.1, {false, do_class_stop}}},
......
};
Copy the code
Let’s take a look at a few commonly used ones
bootchart [start|stop]
: Enables or disables the process startup time recording tool
//init.rc file
mkdir /data/bootchart 0755 shell shell
bootchart start
Copy the code
- in
The Init process
Will start inbootchart
, time collection is not performed by default - When we need to collect the startup time, we need to create one
/data/bootchart/enabled
file
chmod <octal-mode> <path>
: Changes file permissions
chmode 0755 /metadata/keystone
Copy the code
chown <owner> <group> <path>
: Changes the owner and group of the file
chown system system /metadata/keystone
Copy the code
mkdir <path> [mode] [owner] [group]
: Creates a specified directory
mkdir /data/bootchart 0755 shell shell
Copy the code
trigger <event>
: Trigger somethingEvent (Action)
, used to place theThe event
In a certainThe event
after
on late-init
trigger early-fs
trigger fs
trigger post-fs
trigger late-fs
trigger post-fs-data
trigger zygote-start
trigger load_persist_props_action
trigger early-boot
trigger boot
Copy the code
class_start <serviceclass>
: Starts all specified servicesclass
Not running services under
class_start main
class_start late_start
Copy the code
class_stop <serviceclass>
: Stops all specified servicesclass
Running services under
class_stop charger
Copy the code
exec [ <seclabel> [ <user> [ <group>\* ] ] ] -- <command> [ <argument>\* ]
: Passes the given parameterfork
And start a command.
- The specific orders are in
--
After the start - Parameters include
seclable
(Default-
),user
,group
exec
Is blocking. No other command is run until the current command is completedInit
The process is suspended.
exec - system system -- /system/bin/tzdatacheck /system/usr/share/zoneinfo /data/misc/zoneinfo
Copy the code
There are a lot of instructions will not be introduced, refer to the official documentation and source code
init
Script parsing
In the main function of init. CPP, LoadBootScripts() is used to load the rc script.
/** * after 7.0, init.rc was split, each service has its own rc file * they are basically loaded into /system/etc/init, /vendor/etc/init, /odm/etc/init, etc. * Will to resolve these rc files in the directory, to perform the relevant action * = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = * for custom service, You can copy the rc file to the partition/etc/init directory */ according to the partition label at compile time
static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
// Create a parser
Parser parser = CreateParser(action_manager, service_list);
std: :string bootscript = GetProperty("ro.boot.init_rc"."");
if (bootscript.empty()) {
// If ro.boot.init_rc is not specially configured, parse the init.rc file first
parser.ParseConfig("/init.rc");
if(! parser.ParseConfig("/system/etc/init")) {
late_import_paths.emplace_back("/system/etc/init");
}
if(! parser.ParseConfig("/product/etc/init")) {
late_import_paths.emplace_back("/product/etc/init");
}
if(! parser.ParseConfig("/odm/etc/init")) {
late_import_paths.emplace_back("/odm/etc/init");
}
if(! parser.ParseConfig("/vendor/etc/init")) {
late_import_paths.emplace_back("/vendor/etc/init"); }}else {
// Parse data in ro.boot.init_rc directlyparser.ParseConfig(bootscript); }}/** * Create parsers. There are only three sections: service, on, and import *. There are three parsers: ServiceParser, ActionParser, and ImportParser */
Parser CreateParser(ActionManager& action_manager, ServiceList& service_list) {
Parser parser;
parser.AddSectionParser("service".std::make_unique<ServiceParser>(&service_list, subcontexts));
parser.AddSectionParser("on".std::make_unique<ActionParser>(&action_manager, subcontexts));
parser.AddSectionParser("import".std::make_unique<ImportParser>(&parser));
return parser;
}
Copy the code
init
Daemon started in
There are many daemons defined in init.rc, 9 let’s look at them:
Import /init.usb.rc # service adbd /system/bin/adbd --root_seclabel=u:r:su:s0 # class core #...... On boot # Start standard binderized HAL daemons class_start HAL # Start all class core services like ADB, console etc class_start core on eraly-init start ueventd on post-fs # Load properties from # /system/build.prop, # /odm/build.prop, # /vendor/build.prop and # /factory/factory.prop load_system_props Service # Servicemanager is used for IPC between framework/application processes by using the LOCAL_INIT_RC command. Use the AIDL interface start Servicemanager # hwServicemanager for IPC between framework/vendor processes. Use the HIDL interface # for IPC between vendor processes. Use HIDL interface start hwServicemanager # vNDServicemanager for IPC between vendor/vendor processes, Start vndServicemanager on post-fs-data # Service ueventd /sbin/ueventd class core...... Service console /system/bin/sh class core...... Import /init.${ro.zygote}. Rc # Zygote starts some related services. # service zygote /system/bin/ app_process64-xzygote /system/bin --zygote --start-system-server --socket-name=zygote # onrestart restart audioserver # onrestart restart cameraserver # onrestart restart media # Onrestart restart netd # onrestart restart wificond # Zygote On zygote-start && Property :ro.crypto. State =unencrypted Start netd start Zygote start zygote_secondaryCopy the code
The startup process and the services that need to be started are basically customized with init.rc.
It feels like the Init process has made development a lot easier by parsing *. Rc. The people who designed the AIL were really fierce…
Init
Signal processing by a process
The Init process is the first process in the system. All other processes in the system are descendants of the Init process.
By Linux’s design, the Init process is responsible for cleaning up these offspring when they die to prevent them from becoming zombie processes.
zombies
Introduction to the
For details on zombie processes, see Wikipedia – Zombie processes
On UniX-like systems, a zombie process is a process that has completed execution (through exit system calls, or by fatal errors or termination signals at runtime) but still has its process control block in the operating system’s process table and is in a terminated state.
This occurs when a child process needs to retain entries to allow its parent to read the exit status of the child process: Once the exit state is read by the wait system call, zombie process entries are removed from the process table, called reaped.
Normally, the process is waited by its parent and reclaimed by the system.
The avoidance of zombie processes
The parent process
throughwait
andwaitpid
Equifunctional waitThe child process
End, this will lead toThe parent process
hang- if
The parent process
I’m busy, so I can use itsignal
Function forSIGCHLD
The installationhandler
Because theThe child process
After the end,The parent process
Will receive the signal, can be inhandler
In the callwait
recycling - if
The parent process
Don’t care aboutThe child process
When does it end, then it can be usedSignal (SIGCHLD, SIG_IGN)
Notify the kernel of its own pairThe child process
The end is not interested, thenThe child process
When finished, the kernel will reclaim and no longer giveThe parent process
Send a signal - And there are some tricks, just
fork
Twice,The parent process
First,fork
aThe child process
And then continue working with the child processfork
aSun Jincheng
After the exit, thenSun Jincheng
beinit
Take over,Sun Jincheng
After the end,Init
Recycled. However,The child process
Do your own recycling
So what do we do with the Init process
Initialize theSIGCHLD
signal
Sigchld_handler_init () : sigchLD_HANDler_init (); sigchLD_handler_init ();
// file : init.cpp int main(int argc, char** argv) { ...... sigchld_handler_init(); . } // file : Sigchld_handler. CPP void sigchLD_handler_init () {// Create a signalling mechanism for SIGCHLD. Int s[2]; int s[2]; int s[2]; socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s); signal_write_fd = s[0]; signal_read_fd = s[1]; Signal_write_fd if we catch SIGCHLD. Signal_write_fd if we catch SIGCHLD sigaction act; memset(&act, 0, sizeof(act)); act.sa_handler = SIGCHLD_handler; act.sa_flags = SA_NOCLDSTOP; // Register SIGCHLD signal SIGAction (SIGCHLD, & ACT, 0); ReapAnyOutstandingChildren(); // register signal_read_fd to epoll_fd register_epoll_handler(signal_read_fd, handle_signal); } // file : CPP /** * SIGchLD_handler. CPP /** * SigchLD_handler Write data to signal_write_fd * At this point the other signal_read_fd in the socket pair becomes readable. */ static void SIGCHLD_handler(int) { if (TEMP_FAILURE_RETRY(write(signal_write_fd, "1", 1)) == -1) { PLOG(ERROR) << "write(signal_write_fd) failed"; } } // file : The sigchLD_handler. CPP /** * register_epoll_handler function registers the socket file descriptor to the polling descriptor epoll_fd */ void register_epoll_handler(int) fd, void (*fn)()) { epoll_event ev; ev.events = EPOLLIN; ev.data.ptr = reinterpret_cast<void*>(fn); if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) { PLOG(ERROR) << "epoll_ctl failed"; }}Copy the code
Initialization of the signal is done through the system call sigAction ()
- parameter
act
In:sa_handler
The handler used to specify the signalsa_flags
Used to specify the trigger flag,SA_NOCLDSTOP
Flag indicates that it is received when the child process terminatesSIGCHLD
signal
In Linux system, signal is also called soft interrupt, signal arrival will interrupt the process is processing work, so do not call some non-reentrant functions in the signal processing function. And Linux does not queue signals, and no matter how many more signals come in during signal processing, the kernel only sends one more signal to the process after the current signal handler completes, so our signal handler executes as fast as possible in order not to lose the signal
In the case of SIGCHLD signals, the parent process needs to perform wait operations, which can take a long time, so there needs to be a way to resolve this contradiction
- The above code creates a pair of locals
socket
Used for interprocess communication - When the signal comes,
SIGCHLD_handler
The handler is just directed tosocket
In thesignal_write_fd
Just write data - In this way, signal processing shifts to
socket
On the processing of
At this point, we need to listen for signal_read_fd and provide a callback function, which is what register_epoll_handler() does
- In the function
EPOLLIN
Indicates that the file descriptor is triggered when it is readable *fn
After the triggerCallback function pointer
Phi is assigned to phiev.data.ptr
Note that this pointer variable will be used later- The provided callback function is
handle_signal()
Response to the death event of the child process
After the Init process starts, it listens for the created socket. If data arrives, the main thread wakes up and calls handle_signal().
static void handle_signal(a) {
// Clear outstanding requests.
// Clear data in signal_read_fd
char buf[32];
read(signal_read_fd, buf, sizeof(buf));
ReapAnyOutstandingChildren();
}
void ReapAnyOutstandingChildren(a) {
while (ReapOneProcess()) {
}
}
static bool ReapOneProcess(a) {
siginfo_t siginfo = {};
// This returns a zombie pid or informs us that there are no zombies left to be reaped.
// It does NOT reap the pid; that is done below.
// Check whether zombie processes exist
if (TEMP_FAILURE_RETRY(waitid(P_ALL, 0, &siginfo, WEXITED | WNOHANG | WNOWAIT)) ! =0) {
PLOG(ERROR) << "waitid failed";
return false;
}
// No zombie process returns directly
auto pid = siginfo.si_pid;
if (pid == 0) return false;
// At this point we know we have a zombie pid, so we use this scopeguard to reap the pid
// whenever the function returns from this point forward.
// We do NOT want to reap the zombie earlier as in Service::Reap(), we kill(-pid, ...) and we
// want the pid to remain valid throughout that (and potentially future) usages.
// Wait for the child process to terminate
auto reaper = make_scope_guard([pid] { TEMP_FAILURE_RETRY(waitpid(pid, nullptr, WNOHANG)); }); .if(! service)return true;
// Perform other operations to exitservice->Reap(siginfo); . }Copy the code
After receiving the SIGCHLD signal from the child process, we will find the corresponding Service object of the process, and then call Reap function. Let’s look at the contents of this function:
void Service::Reap(const siginfo_t& siginfo) {
// If it is not oneshot or a restarted child process
// Kill the entire process group. On second thought, kill first to prepare for the restart
// This will not cause errors when the child process already exists
if(! (flags_ & SVC_ONESHOT) || (flags_ & SVC_RESTART)) { KillProcessGroup(SIGKILL); }// Do some cleanup of the current process.// Oneshot processes go into the disabled state on exit,
// except when manually restarted.
// Set SVC_DISABLED if it is oneshot or not a restarted child process
if((flags_ & SVC_ONESHOT) && ! (flags_ & SVC_RESTART)) { flags_ |= SVC_DISABLED; }// Disabled and reset processes do not get restarted automatically.
// If it is SVC_DISABLED or SVC_RESET
// Set the process status to stopped and return
if (flags_ & (SVC_DISABLED | SVC_RESET)) {
NotifyStateChange("stopped");
return;
}
// If we crash > 4 times in 4 minutes, reboot into recovery.
// omit crash count detection.// Omit some process state Settings that are reboot dependent
// Execute all onrestart commands for this service.
// Execute the onrestart command
onrestart_.ExecuteAllCommands();
// Set the state to Restart
NotifyStateChange("restarting");
return;
}
Copy the code
In the Reap() function
- According to the corresponding process
Service
The object’sflags_
Flag bit to determine whether the process can be restarted - If you need to reboot, just
flags_
Flag bit additionSVC_RESTARTING
Sign a
At this point, we’re clearhandle_signal()
The internal flow of a function, and where is it called from?
Let’s go back to the main() method of init.cpp:
while (true) {... epoll_event ev;int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, epoll_timeout_ms));
if (nr == - 1) {
PLOG(ERROR) << "epoll_wait failed";
} else if (nr == 1) {((void(*)()) ev.data.ptr)(); }}Copy the code
Note that ev.data.ptr, remember that the register_epoll_handler() function does not
void register_epoll_handler(int fd, void (*fn)(a)) {... ev.data.ptr = reinterpret_cast<void*>(fn); . }Copy the code
When epoll_wait receives data, it executes ((void (*)()) ev.data.ptr)(); , our callback function handle_signal()
Init process death notification to child process logic, source code has been changed, but the core logic has not changed, and take care of it, hahaha ~ ~
Property system
Introduction to the
Properties are used extensively in Android to save system Settings or to pass simple information between processes
- each
attribute
byThe property name
andAttribute values
composition The property name
Usually a long string of.
The prefix of these names has a specific meaning and cannot be changed arbitrarilyAttribute values
It can only be a string
The Java layer can get and set properties by:
//class android.os.SystemProperties
@SystemApi
public static String get(String key);
@SystemApi
public static String get(String key, String def);
@hide
public static void set(String key, String val);
Copy the code
Native layer can be used:
android::base::GetProperty(key, "");
android::base::SetProperty(key, val);
Copy the code
For each process in the system:
Reading property values
There is no restriction on any process and it is read directly from the shared area by the processModifying property values
Must passThe Init process
At the same timeThe Init process
You also need to check that the process that initiated the request has the appropriate permissions
After the property value is successfully modified, the Init process checks whether the init.rc file already defines a trigger matching the property value. Execute the command under trigger if defined. Such as:
on property:ro.debuggable=1
start console
Copy the code
When the property ro.debuggable is set to 1, the start console command is executed to start the console
Android system-level applications and underlying modules rely heavily on the attribute system, often relying on attribute values to determine their behavior.
In the Android system Settings application, many functions are turned on and off by a specific system attribute value. This also means that arbitrarily changing the value of an attribute can seriously affect the operation of the system, so the modification of the value of an attribute must have specific permissions. SELinux now controls the setting of permissions.
The startup process of the property service
Let’s take a look at the overall process of starting a property service:
int main(int argc, char** argv) {...// Attribute service initializationproperty_init(); .// Start the properties servicestart_property_service(); . }Copy the code
In the main() function of init.cpp, property_init(); To initialize the property service
void property_init(a) {
// create a folder with permission 711. Only owner can set this folder
mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH);
// Read some property files and store the property values in a collection
CreateSerializedPropertyInfo();
if (__system_property_area_init()) {// Create properties shared memory space (this function is part of the liBC library)
LOG(FATAL) << "Failed to initialize property area";
}
if(! property_info_area.LoadDefaultPath()) {// Load the properties on the default path to the shared area
LOG(FATAL) << "Failed to load serialized property info file"; }}Copy the code
Then start the service with the start_property_service() function:
void start_property_service(a) {
// Omit SELinux operations. property_set("ro.property_service.version"."2");
// Create the socket corresponding to prop Service and return the socket fd
property_set_fd = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, false.0666.0.0.nullptr);
// omit exception judgment for failed creation.// Set maximum number of connections to 8
listen(property_set_fd, 8);
// Register epolls event to listen for property_set_fd
// Call handle_property_set_fd when listening for data changes
register_epoll_handler(property_set_fd, handle_property_set_fd);
}
Copy the code
socket
The descriptorproperty_set_fd
After being created, useepoll
To listen toproperty_set_fd
- when
property_set_fd
When the data comes in,The init process
Will callhandle_property_set_fd()
Function to process
Let’s look again at the handle_property_set_fd() function
static void handle_property_set_fd(a) {
static constexpr uint32_t kDefaultSocketTimeout = 2000; /* ms */
// Omit some exception judgments.uint32_t cmd = 0;
// Omit CMD read operations and some exception judgments.switch (cmd) {
case PROP_MSG_SETPROP: {
char prop_name[PROP_NAME_MAX];
char prop_value[PROP_VALUE_MAX];
// omit the character data read assembly operation.uint32_t result =
HandlePropertySet(prop_name, prop_value, socket.source_context(), cr, &error);
// omit exception handling.break;
}
case PROP_MSG_SETPROP2: {
std: :string name;
std: :string value;
// Omit the string data read operation.uint32_t result = HandlePropertySet(name, value, socket.source_context(), cr, &error);
// omit exception handling.break;
}
default:
LOG(ERROR) << "sys_prop: invalid command " << cmd;
socket.SendUint32(PROP_ERROR_INVALID_CMD);
break; }}Copy the code
After receiving CMD to set the properties, the Init process executes the handler HandlePropertySet().
uint32_t HandlePropertySet(const std: :string& name, const std: :string& value,
const std: :string& source_context,
const ucred& cr, std: :string* error) {
// Determine whether the property name to be set is valid
// this is equivalent to a naming rule check
if(! IsLegalPropertyName(name)) {// Invalid direct return
return PROP_ERROR_INVALID_NAME;
}
// If it starts with CTL, it is a control class attribute
if (StartsWith(name, "ctl.")) {
// Check whether you have the corresponding control permission.// Execute the corresponding control instruction after permission passes
// Start /stop/start/stop
HandleControlMessage(name.c_str() + 4, value, cr.pid);
return PROP_SUCCESS;
}
const char* target_context = nullptr;
const char* type = nullptr;
// Gets the context and data type of the property to be set
// Compare target_context with type
property_info_area->GetPropertyInfo(name.c_str(), &target_context, &type);
// Check if you have the set permission for the current attribute
if(! CheckMacPerms(name, target_context, source_context.c_str(), cr)) {// There is no direct return
return PROP_ERROR_PERMISSION_DENIED;
}
// Determine the type of the attribute and the type of data to be written
// The CheckType function has only one string type...
if (type == nullptr| |! CheckType(type, value)) {// Invalid, return directly
return PROP_ERROR_INVALID_VALUE;
}
// If it is the sys.powerctl attribute, some special processing is required
if (name == "sys.powerctl") {
// Add some extra printing. }if (name == "selinux.restorecon_recursive") {
// Special attributes, special handling
return PropertySetAsync(name, value, RestoreconRecursiveAsync, error);
}
return PropertySet(name, value, error);
}
Copy the code
With the exception of some special properties, the function that actually sets the property is PropertySet
static uint32_t PropertySet(const std: :string& name, const std: :string& value, std: :string* error) {
size_t valuelen = value.size();
// Check whether the attribute name is valid
if(! IsLegalPropertyName(name)) { *error ="Illegal property name";
return PROP_ERROR_INVALID_NAME;
}
// Determine whether the length of the data to be written is valid
// Check whether the property is read-only (ro)
if(valuelen >= PROP_VALUE_MAX && ! StartsWith(name,"ro.")) {
*error = "Property value too long";
return PROP_ERROR_INVALID_VALUE;
}
// Determine the encoding format of the data to be written
if (mbstowcs(nullptr, value.data(), 0) = =static_cast<std: :size_t> (- 1)) {
*error = "Value is not a UTF8 encoded string";
return PROP_ERROR_INVALID_VALUE;
}
// Get the property object stored in the system according to the property name
prop_info* pi = (prop_info*) __system_property_find(name.c_str());
if(pi ! =nullptr) {
// ro.* properties are actually "write-once".
// Ro attributes can only be written once
if (StartsWith(name, "ro.")) {
*error = "Read-only property was already set";
return PROP_ERROR_READ_ONLY_PROPERTY;
}
// If the property already exists and is not read-only, the property update function is executed
__system_property_update(pi, value.c_str(), valuelen);
} else {
// If no attribute exists in the system, execute the add attribute add function
int rc = __system_property_add(name.c_str(), name.size(), value.c_str(), valuelen);
if (rc < 0) {
*error = "__system_property_add failed";
returnPROP_ERROR_SET_FAILED; }}// Don't write properties to disk until after we have read all default
// properties to prevent them from being overwritten by default values.
// If it is a persistent attribute, persist it
if (persistent_properties_loaded && StartsWith(name, "persist.")) {
WritePersistentProperty(name, value);
}
// Add property changes to the Action queue
property_changed(name, value);
return PROP_SUCCESS;
}
Copy the code
The Init process waits for and processes requests for the epoll attribute socket.
- Call if a request comes in
handle_property_set_fd
To process the request - in
handle_property_set_fd
Function, first check the requester’suid/gid
Check whether you have the permission, and if so, adjustproperty_service.cpp
In thePropertySet
Function.
In the PropertySet function
- It looks for the property and doesn’t have it, and if it does, it changes the property. If not, add a new attribute.
- It will also determine if it is correct when it changes
ro
Property, if it is, it cannot be changed. - If it is
persist
I’ll write it again/data/property/<name>
In the.
Finally, it calls the property_CHANGED function to hang the event to the queue
- If someone registers this property (e.g
init.rc
In theon property:ro.kernel.qemu=1
), will eventually trigger it
ueventd
andwatchdogd
Introduction to the
ueventd
process
The main purpose of the ueventd daemon is to receive uEventD to create and delete device nodes in the dev directory of the device
The ueventd process and Init process are not the same process, but their binaries are the same, with different startup parameters resulting in a different program execution flow.
In the init.rc file
on early-init
start ueventd
## Daemon processes to be run by init.
##
service ueventd /sbin/ueventd
class core
critical
seclabel u:r:ueventd:s0
shutdown critical
Copy the code
The Init process starts ueventD when action eraly-init is executed.
watchdogd
process
The watchdogd and Ueventd types are separate from the Init process, but the code stays with the Init process. The Watchdogd is designed to work with a hardware watchdog.
When the watchdog function is enabled on a hardware system, the software running on the hardware system must send a signal to the watchdog at a specified interval. This behavior is simply called feed Dog, in case the watchdog times out and causes a system reboot.
The Watchdogd process is rarely seen on today’s systems, but it’s a good model to follow