An overview of the
The blog link
Note: This article is based on Android 9.0 source code, for the sake of the article’s brevity, the source code may be deleted.
This paper mainly analyzes the relationship between the Android system services and the startup logic of each key process after the startup of the Android system. The startup process of the Android system is briefly as follows (picture source network, summarized very well) :
System startup
When the power is pressed, the boot chip code is executed from a predefined location (solidified in ROM), the boot program BootLoader is loaded into RAM, and then executed.
Boot program BootLoader
It is a small program before the Android operating system starts running, mainly to pull up the operating system OS and carry on.
Linux kernel startup
When the kernel starts, the cache is set up, the driver is loaded, and so on. In addition, the Kernel swapper process (pid = 0) and kthreadd process (pid = 2) are also started:
- Swapper process: Also known as idle process. It is the first process initiated by the Kernel during system initialization. It is used for initialization process management, memory management, and loading Binder drivers, Display, Camera drivers and other related work.
- The kthreadd process is the ancestor of all kernel processes in Linux. It creates the kernel worker thread kworkder, soft interrupt thread Ksoftirqd, and thermal daemon processes.
When the kernel is done setting up the system, it first looks for the init.rc file on the system and starts the init process. The init process is the first user-level process started by the kernel. Its process number is 1 and its parent process ID is 0. The init process, the granddaddy of all user Spaces, starts servicemanager(binder servicemanager for query and registration services) and Zygote (ancestor of Java processes). Zygote creates system_server and various app processes.
The init process
init.cpp
Entrance to the function of the init process is the system/core/init/init. The main function of the CPP, its code is shown below:
int main(int argc, char** argv) {
add_environment("PATH", _PATH_DEFPATH);
// Phase 1: kernel state
// Phase 2: User mode
bool is_first_stage = (getenv("INIT_SECOND_STAGE") = =nullptr);
if (is_first_stage) {
// Clear the umask
umask(0);
// 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);
// ...
InitKernelLogging(argv);
LOG(INFO) << "init first stage started!";
if (!DoFirstStageMount()) {
LOG(ERROR) << "Failed to mount required partitions early ...";
panic(a); }// Set up SELinux(Security-Enhanced Linux), loading the SELinux policy.
selinux_initialize(true);
// We're in the kernel domain, so re-exec init to transition to the init domain now
// that the SELinux policy has been loaded.
// Reset init file properties as required by selinux policy
if (restorecon("/init") = =- 1) {
// If it fails, reboot
PLOG(ERROR) << "restorecon failed";
security_failure(a); }setenv("INIT_SECOND_STAGE"."true".1);
setenv("INIT_STARTED_AT".StringPrintf("%" PRIu64, start_ms).c_str(), 1);
// Call init main again to start the user init process
char* path = argv[0];
char* args[] = { path, nullptr };
execv(path, args);
// execv() only returns if an error happened, in which case we
// panic and never fall through this conditional.
PLOG(ERROR) << "execv(\"" << path << "\") failed";
security_failure(a); }// Phase 2: User mode
InitKernelLogging(argv);
LOG(INFO) << "init second stage started!";
property_init(a);// Initialize the property service
// Execute the kernel command
process_kernel_dt(a);process_kernel_cmdline(a);export_kernel_boot_props(a);// Set the properties
property_set("ro.boottime.init".getenv("INIT_STARTED_AT"));
property_set("ro.boottime.init.selinux".getenv("INIT_SELINUX_TOOK"));
// Reset some of the previously used environment variables
unsetenv("INIT_SECOND_STAGE");
unsetenv("INIT_STARTED_AT");
unsetenv("INIT_SELINUX_TOOK");
unsetenv("INIT_AVB_VERSION");
// Now set up SELinux for second stage.
selinux_initialize(false);
selinux_restore_context(a);signal_handler_init(a);// Enable the properties service
start_property_service(a);/ / parsing init. Rc
// ...
while (true) {
// It will sleep by default until an event wakes it up
int epoll_timeout_ms = - 1;
if(! (waiting_for_prop || ServiceManager::GetInstance().IsWaitingForExec())) {
am.ExecuteOneCommand(a); }// Restart some stalled processes, such as Zygote
if(! (waiting_for_prop || ServiceManager::GetInstance().IsWaitingForExec())) {
restart_processes(a);// If there's a process that needs restarting, wake up in time for that.
if(process_needs_restart_at ! =0) {
epoll_timeout_ms = (process_needs_restart_at - time(nullptr)) * 1000;
if (epoll_timeout_ms < 0) epoll_timeout_ms = 0;
}
// If there's more work to do, wake up again immediately.
if (am.HasMoreCommands()) epoll_timeout_ms = 0;
}
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)(); }}return 0;
}
Copy the code
Stage 1:
- Identify and add environment variables
- Create a file system directory and mount the associated file system
- Redirects the I/O/kernel Log system
- Mount some partition devices
- Complete SELinux related work
- Is_first_stage ending
Stage 2:
- Initialize the property field and empty the environment variables
- Complete SELinux related work
- Start the Properties service
- The init.rc configuration file is parsed to perform the actions of the phases, one of which is where the Zygote creation takes place.
- Init enters an infinite loop and waits for something to happen.
Here is init.rc (Mi Max3–MIUI 10–Android 9) :
on early-init on init on property:sys.boot_from_charger_mode=1 on load_persist_props_action on firmware_mounts_complete on late-init on post-fs // mount file system start logd mount rootfs rootfs / ro remount mount rootfs rootfs / shared rec mount none /mnt/runtime/default /storage slave bind rec // ... / /... On post-fs-data // mount /data/ // start logd start logd // Start vold, a background process for managing Android external storage media, including inserting and removing SD cards start vold // / /... on boot // ... class_start coreCopy the code
Start the service
In the init.zygote64_32.rc file:
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote class main priority -20 user root group root readproc reserved_disk socket zygote stream 660 root system onrestart write /sys/android_power/request_state wake onrestart write /sys/power/state on onrestart restart audioserver onrestart restart cameraserver onrestart restart media onrestart restart netd onrestart restart wificond writepid /dev/cpuset/foreground/tasks service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary --enable-lazy-preload class main priority -20 user root group root readproc reserved_disk socket zygote_secondary stream 660 root system onrestart restart zygote writepid /dev/cpuset/foreground/tasksCopy the code
The entire service is parsed through init_parser. CPP, which will not be covered here.
Execve (SVC ->args[0], (char**) SVC ->args, (char**) ENV), Enter the main() function of app_main.cpp. So Zygote was created with fork and execv.
The process is as follows:
When the init child exits, a SIGCHLD signal is generated and sent to the init process, data is passed through the socket socket, and wait_for_one_process() is called to decide whether to restart or abort the child process, depending on whether it is oneshot. If surfaceFlinger, Servicemanager, Zygote, or System_server processes are killed, zygote restarts.
Property services
As we know, there’s something called a registry on the Windows platform. The registry can store key-value pairs like keys/values. Generally speaking, the system or some applications will store some of their properties in the registry, even if the next system restart or application restart, it can also be based on the properties set in the registry, corresponding initialization.
The Android platform also provides a type mechanism, called a property Service. This property mechanism allows applications to query or set properties. When process A changes the property value, the init process checks the access permission. When the property value meets the requirement, the init process changes the corresponding property value. If the property value changes, the corresponding trigger (i.e. the statement starting with on in the RC file) is triggered.
Property service initialization:
void property_init(void)
{
init_property_area(a);// Initializes the properties storage area
// Load the default.prop file
load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT);
}
Copy the code
In the properyty_init function, the init_property_area function is called to create a storage area (shared memory) for storing properties, and then the contents of the default.prop file are loaded.
Access method:
- Native:
property_get/property_set
- Java:
SystemProperties
- Shell:
adb shell getprop
The zygote process
An overview of the
Zygote is created by the init process by parsing init.zygote.rc. The executable app_process corresponding to Zygote is app_main. CPP.
When Zygote is started, the main() method of the frameworks/base/ CMDS /app_process/app_main.cpp file is executed. The whole call process:
Start AndroidRuntime.startVM AndroidRuntime.startreg zygoteinit. main (first entry into the Java world) registerServerSocketFromEnv preload forkSystemServer runSelectLoopCopy the code
The Zygote process creates a Java virtual machine and registers JNI methods to become the mother of Java processes, which are used to incubate Java processes. After creating the system_server process, Zygote retired, calling runSelectLoop() to stand by and wake up when a request to create a new process is received. The Zygote process does several things:
- Create AppRuntime and call its start method to start the Zygote process.
- Create a DVM (ART) and register JNI for the DVM.
- Enter Zygote’s Java framework layer by calling ZygoteInit’s main function from JNI.
- By registerServerSocketFromEnv function to create a server Socket, and through waiting ActivityManagerService runSelectLoop function request to create a new application process.
- Start the SystemServer process.
app_main.cpp
Zygote itself is a Native application that has nothing to do with drivers, kernels, etc. Zygote is created by the init process based on configuration items in the init.rc file. Zygote was originally named “app_process,” which was specified in the Android.mk file, but app_Process changed its name to “Zygote” during its run through a PCTRL system call under Linux, So the process name we see through the ps command is “zygote”.
The source file for zygote’s prototype app_process is Framework /base/ CMDS /app_process/app_main.cpp, and the code is as follows:
int main(int argc, const char* const argv[])
{
AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
// --zygote : Start in zygote mode
// --start-system-server : Start the system server.
// --application : Start in application (stand alone, non zygote) mode.
// --nice-name : The nice name for this process.
bool zygote = false;
bool startSystemServer = false;
bool application = false;
String8 niceName;
String8 className;
while (i < argc) {
const char* arg = argv[i++];
if (strcmp(arg, "--zygote") = =0) {
zygote = true;
niceName = ZYGOTE_NICE_NAME; // zygote64 or zygote
} else if (strcmp(arg, "--start-system-server") = =0) {
startSystemServer = true;
} else if (strcmp(arg, "--application") = =0) {
application = true;
} else if (strncmp(arg, "--nice-name=".12) = =0) {
niceName.setTo(arg + 12);
} else if (strncmp(arg, "--".2) != 0) {
className.setTo(arg);
break;
} else {
--i;
break;
}
}
Vector<String8> args;
if(! className.isEmpty()) {
// We're not in zygote mode
// The only argument that needs to be passed to RuntimeInit is the application argument, and the remaining args is passed to the startup class main
args.add(application ? String8("application") : String8("tool"));
runtime.setClassNameAndArgs(className, argc - i, argv + i);
} else {
// We're in zygote mode.
maybeCreateDalvikCache(a);if (startSystemServer) {
args.add(String8("start-system-server"));
}
char prop[PROP_VALUE_MAX];
if (property_get(ABI_LIST_PROPERTY, prop, NULL) = =0) {
LOG_ALWAYS_FATAL("app_process: Unable to determine ABI list from property %s.",
ABI_LIST_PROPERTY);
return 11;
}
String8 abiFlag("--abi-list=");
abiFlag.append(prop);
args.add(abiFlag);
// In zygote mode, pass all remaining arguments to the zygote
// main() method.
for (; i < argc; ++i) {
args.add(String8(argv[i])); }}if(! niceName.isEmpty()) {
runtime.setArgv0(niceName.string(), true /* setProcName */);
}
if (zygote) {
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
} else if (className) {
runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
} else {
fprintf(stderr, "Error: no class name or --zygote supplied.\n");
app_usage(a);LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied."); }}Copy the code
The AppRuntime class, declared and implemented in app_main. CPP, is derived from the AndroidRuntime class. The above function uses the base AndroidRuntime class start.
AndroidRuntime.start
// frameworks/base/core/jni/AndroidRuntime.cpp
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
const char* rootDir = getenv("ANDROID_ROOT");
if (rootDir == NULL) {
rootDir = "/system";
if (!hasDir("/system")) {
LOG_FATAL("No root directory specified, and /android does not exist.");
return;
}
setenv("ANDROID_ROOT", rootDir, 1);
}
/* start the virtual machine */
JniInvocation jni_invocation;
jni_invocation.Init(NULL);
JNIEnv* env;
if (startVm(&mJavaVM, &env, zygote) ! =0) {
return;
}
onVmCreated(env);
// Since some of the functions used in the Java world are implemented in native mode, they must be registered in advance
if (startReg(env) < 0) {
ALOGE("Unable to register all android natives\n");
return;
}
// Encapsulate the parameters in strArray
/* * Start VM. This thread becomes the main thread of the VM, and will * not return until the VM exits. */
char* slashClassName = toSlashClassName(className ! =NULL ? className : "");
jclass startClass = env->FindClass(slashClassName);
if (startClass == NULL) {
ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
} else {
// Find jMethodId for static main of ZygoteInit class
jmethodID startMeth = env->GetStaticMethodID(startClass, "main"."([Ljava/lang/String;)V");
if (startMeth == NULL) {
ALOGE("JavaVM unable to find main() in '%s'\n", className);
} else {
After calling zygoteinit. main, Zygote enters the Java world
env->CallStaticVoidMethod(startClass, startMeth, strArray); }}free(slashClassName);
}
Copy the code
ZygoteInit.main
CallStaticVoidMethod will eventually call com. Android. Internal. OS. ZygoteInit main function, the code is as follows:
// frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
public static void main(String argv[]) {
ZygoteServer zygoteServer = new ZygoteServer(a);try {
boolean startSystemServer = false;
String socketName = "zygote";
String abiList = null;
boolean enableLazyPreload = false;
for (int i = 1; i < argv.length; i++) {
if ("start-system-server".equals(argv[i])) {
startSystemServer = true;
} else if ("--enable-lazy-preload".equals(argv[i])) {
enableLazyPreload = true;
} else if (argv[i].startsWith(ABI_LIST_ARG)) {
abiList = argv[i].substring(ABI_LIST_ARG.length());
} else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
socketName = argv[i].substring(SOCKET_NAME_ARG.length());
} else {
throw new RuntimeException("Unknown command line argument: "+ argv[i]); }}// Create a Zygote Socket interface to communicate with AMS etc
zygoteServer.registerServerSocketFromEnv(socketName);
if(! enableLazyPreload) {// Preload some classes and resources, which is one of the causes of slow Android startup
// All applications are hatched from Zygote. All applications inherit from Zygote. If these classes and resources are loaded when Zygote is started,
// These incubated applications inherit Zygote's classes and resources, which makes it much faster to start referencing applications without loading classes and resources.
preload(bootTimingsTraceLog);
}
// Do an initial gc to clean up after startup
gcAndFinalize(a);if (startSystemServer) {
Runnable r = forkSystemServer(abiList, socketName, zygoteServer);
// Child process (system_server)
if(r ! = null) {/ / call com. Android. Server SystemServer. The main method
r.run(a);return;
}
}
Log.i(TAG, "Accepting command socket connections");
// The select loop returns early in the child process after a fork and
// loops forever in the zygote.
caller = zygoteServer.runSelectLoop(abiList);
} catch (Throwable ex) {
Log.e(TAG, "System zygote died with exception", ex);
throw ex;
} finally {
zygoteServer.closeServerSocket(a); }// We're in the child process and have exited the select loop. Proceed to execute the command.
if(caller ! = null) { caller.run();
}
}
Copy the code
There are three important calls above:
- RegisterServerSocketFromEnv Socket interface is used to create a Zygote, for communication and AMS
- ForkSystemServer forks the system_server process.
- RunSelectLoop is used to handle client connections and requests, including AMS requests to create app processes.
System_server process
An overview of the
SyetemServer does the following when it starts:
- Start a Binder thread pool so that you can communicate with other processes.
- Create SystemServiceManager To create, start, and manage system services.
- Start various system services. AMS, PMS, and WMS are all threads that run in the system_server process.
ZygoteInit.forkSystemServer
In the previous section, the zygoteinit. main method first entered the Java world, and then the forkSystemServer method was called to create the system_server process.
private static Runnable forkSystemServer(String abiList, String socketName, ZygoteServer zygoteServer) {
/* Hardcoded command line to start the system server */
String args[] = {
"--setuid=1000"."--setgid=1000"."- setgroups = 1001100 2100 3100 4100 5100 6100 7100 8100 9101 0101 8102 1102 3102 4103 2106 5300 1300 2300 3300 6300 7, 30 09301. ""."--capabilities=" + capabilities + "," + capabilities,
"--nice-name=system_server"."--runtime-args"."--target-sdk-version=" + VMRuntime.SDK_VERSION_CUR_DEVELOPMENT,
"com.android.server.SystemServer"}; ZygoteConnection.Arguments parsedArgs =null;
int pid;
try {
parsedArgs = new ZygoteConnection.Arguments(args);
ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);
boolean profileSystemServer = SystemProperties.getBoolean(
"dalvik.vm.profilesystemserver".false);
if (profileSystemServer) {
parsedArgs.runtimeFlags |= Zygote.PROFILE_SYSTEM_SERVER;
}
/* Request to fork the system server process */
pid = Zygote.forkSystemServer(
parsedArgs.uid, parsedArgs.gid,
parsedArgs.gids,
parsedArgs.runtimeFlags,
null,
parsedArgs.permittedCapabilities,
parsedArgs.effectiveCapabilities);
} catch (IllegalArgumentException ex) {
throw new RuntimeException(ex);
}
/* For child process */
if (pid == 0) {
if (hasSecondZygote(abiList)) {
waitForSecondaryZygote(socketName);
}
zygoteServer.closeServerSocket();
return handleSystemServerProcess(parsedArgs);
}
return null;
}
Copy the code
Set uid, GID and groups for system_server process, nice-name, etc., with two important calls:
- The Zygote. ForkSystemServer () function is used to fork a new process. If pid==0, it indicates that the SystemServer child has entered the Zygote socket. Because the system server process system_server also inherits the Socket, you don’t need to close it.
- Call the handleSystemServerProcess () method, return to a Runnable object to ZygoteInit. The main and calls, is actually the call to the com. Android. The server SystemServer. The main method.
Zygote.forkSystemServer
First look at the Zygote. ForkSystemServer method, which calls the nativeForkSystemServer method:
// frameworks/base/core/jni/com_android_internal_os_Zygote.cpp
static jint com_android_internal_os_Zygote_nativeForkSystemServer(
JNIEnv* env, jclass, uid_t uid, gid_t gid, jintArray gids,
jint runtime_flags, jobjectArray rlimits, jlong permittedCapabilities,
jlong effectiveCapabilities) {
pid_t pid = ForkAndSpecializeCommon(env, uid, gid, gids,
runtime_flags, rlimits,
permittedCapabilities, effectiveCapabilities,
MOUNT_EXTERNAL_DEFAULT, NULL.NULL.true.NULL.NULL.NULL.NULL);
if (pid > 0) {}return pid;
}
static pid_t ForkAndSpecializeCommon(/ *... * /) {
SetSignalHandlers(a);pid_t pid = fork(); // fork the child process
UnsetChldSignalHandler(a); }static void SetSignalHandlers(a) {
struct sigaction sig_chld = {};
sig_chld.sa_handler = SigChldHandler;
if (sigaction(SIGCHLD, &sig_chld, NULL) < 0) {
ALOGW("Error setting SIGCHLD handler: %s".strerror(errno));
}
struct sigaction sig_hup = {};
sig_hup.sa_handler = SIG_IGN;
if (sigaction(SIGHUP, &sig_hup, NULL) < 0) {
ALOGW("Error setting SIGHUP handler: %s".strerror(errno)); }}// Sets the SIGCHLD handler back to default behavior in zygote children.
static void UnsetChldSignalHandler(a) {
struct sigaction sa;
memset(&sa, 0.sizeof(sa));
sa.sa_handler = SIG_DFL;
if (sigaction(SIGCHLD, &sa, NULL) < 0) {
ALOGW("Error unsetting SIGCHLD handler: %s".strerror(errno)); }}static void SigChldHandler(int /*signal_number*/) {
pid_t pid;
int status;
while ((pid = waitpid(- 1, &status, WNOHANG)) > 0) {
if (pid == gSystemServerPid) {
ALOGE("Exit zygote because system server (%d) has terminated", pid);
kill(getpid(), SIGKILL); }}}Copy the code
There is a logic in ForkAndSpecializeCommon that if the SystemServer process stops working, first get the Zygote process PID by getPid () and then kill it by calling the kill function. The Zygote process commits suicide, and then Init has an infinite loop in its main() function that restarts its child Zygote if it stops working.
handleSystemServerProcess
private static Runnable handleSystemServerProcess(ZygoteConnection.Arguments parsedArgs) {
ClassLoader cl = createPathClassLoader(systemServerClasspath, parsedArgs.targetSdkVersion);
Thread.currentThread().setContextClassLoader(cl);
return ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl);
}
public static final Runnable zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader) {
// Perform the initialization of the Binder driver so that the system_server process can communicate with the Binder process
ZygoteInit.nativeZygoteInit();
return RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
}
// frameworks/base/core/java/com/android/internal/os/RuntimeInit.java
protected static Runnable applicationInit(int targetSdkVersion, String[] argv,
ClassLoader classLoader) {
final Arguments args = new Arguments(argv);
return findStaticMain(args.startClass, args.startArgs, classLoader);
}
/ / by reflection for com. Android. Server SystemServer main method
protected static Runnable findStaticMain(String className, String[] argv, ClassLoader classLoader) { Class<? > cl = Class.forName(className,true, classLoader);
Method m = cl.getMethod("main".new Class[] { String[].class });
return new MethodAndArgsCaller(m, argv);
}
static class MethodAndArgsCaller implements Runnable {
/** method to call */
private final Method mMethod;
/** argument array */
private final String[] mArgs;
public MethodAndArgsCaller(Method method, String[] args) {
mMethod = method;
mArgs = args;
}
public void run(a) {
mMethod.invoke(null.newObject[] { mArgs }); }}Copy the code
The zygoteinit. main method is called forkSystemServer(abiList, socketName, zygoteServer). R return is MethodAndArgsCaller, call r.r UN method in the child process is called com. Android. Server SystemServer. The main method.
SystemServer#main
// SystemServer.java
public final class SystemServer {...public static void main(String[] args) {
// Initialize the SystemServer object before calling its run() method
newSystemServer().run(); }}private void run(a) {
Looper.prepareMainLooper();// Prepare the main thread looper
/ / load android_servers. So library, the library contains the source code in the frameworks/base/services/directory
System.loadLibrary("android_servers");
createSystemContext(); // Initialize the system context
// Create system service management
mSystemServiceManager = new SystemServiceManager(mSystemContext);
LocalServices.addService(SystemServiceManager.class, mSystemServiceManager);
// Start various system services
try {
startBootstrapServices(); // Start the boot service
startCoreServices(); // Start the core service
startOtherServices(); // Start other services
} catch (Throwable ex) {
throw ex;
}
// execute the loop
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
Copy the code
Call relationship:
SystemServer.main
SystemServer.run
Looper.prepareMainLooper();
createSystemContext
startBootstrapServices(a);
startCoreServices();
startOtherServices();
Looper.loop();
Copy the code
LocalServices uses the static Map variable sLocalServiceObjects to store the Map structure with the service class named key and the specific service object as value.
Start with the method startBootstrapServices:
private void startBootstrapServices(a) {
// Block waiting to establish socket channel with instalLD
Installer installer = mSystemServiceManager.startService(Installer.class);
// In some cases after launching an app we need to access device identifiers,
// therefore register the device identifier policy before the activity manager.
mSystemServiceManager.startService(DeviceIdentifiersPolicyService.class);
mActivityManagerService = mSystemServiceManager.startService(
ActivityManagerService.Lifecycle.class).getService();
mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
mActivityManagerService.setInstaller(installer);
mPowerManagerService = mSystemServiceManager.startService(PowerManagerService.class);
mActivityManagerService.initPowerManagement();
mSystemServiceManager.startService(RecoverySystemService.class);
// Now that we have the bare essentials of the OS up and running, take
// note that we just booted, which might send out a rescue party if
// we're stuck in a runtime restart loop.
RescueParty.noteBoot(mSystemContext);
mSystemServiceManager.startService(LightsService.class);
// Package manager isn't started yet; need to use SysProp not hardware feature
if (SystemProperties.getBoolean("config.enable_sidekick_graphics".false)) {
mSystemServiceManager.startService(WEAR_SIDEKICK_SERVICE_CLASS);
}
mDisplayManagerService = mSystemServiceManager.startService(DisplayManagerService.class);
// We need the default display before we can initialize the package manager.
mSystemServiceManager.startBootPhase(SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY);
// Run only core apps while the device is encrypting
String cryptState = SystemProperties.get("vold.decrypt");
if (ENCRYPTING_STATE.equals(cryptState)) {
Slog.w(TAG, "Detected encryption in progress - only parsing core apps");
mOnlyCore = true;
} else if (ENCRYPTED_STATE.equals(cryptState)) {
Slog.w(TAG, "Device encrypted - only parsing core apps");
mOnlyCore = true;
}
// Start the package manager.mPackageManagerService = PackageManagerService.main(mSystemContext, installer, mFactoryTestMode ! = FactoryTest.FACTORY_TEST_OFF, mOnlyCore); mFirstBoot = mPackageManagerService.isFirstBoot(); mPackageManager = mSystemContext.getPackageManager(); mSystemServiceManager.startService(UserManagerService.LifeCycle.class); AttributeCache.init(mSystemContext);// Set up the Application instance for the system process and get started.
mActivityManagerService.setSystemProcess();
mDisplayManagerService.setupSchedulerPolicies();
mSystemServiceManager.startService(new OverlayManagerService(mSystemContext, installer));
// Start sensor service
startSensorService();
Copy the code
The service created by this method: ActivityManagerService, PowerManagerService, LightsService, DisplayManagerService, PackageManagerService, UserManagerService, Sensor service, etc.
Then start the core service startCoreServices:
private void startCoreServices(a) {
// Start BatteryService, which counts battery power. LightService is required.
mSystemServiceManager.startService(BatteryService.class);
// Start the UsageStatsService to collect statistics on application usage
mSystemServiceManager.startService(UsageStatsService.class);
mActivityManagerService.setUsageStatsManager(
LocalServices.getService(UsageStatsManagerInternal.class));
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_WEBVIEW)) {
// Start service WebViewUpdateService
mWebViewUpdateService = mSystemServiceManager.startService(WebViewUpdateService.class);
}
BinderCallsStatsService.start();
}
Copy the code
Start services BatteryService, UsageStatsService, WebViewUpdateService, etc.
The startOtherServices method for starting other services is longer, mainly to start a series of services:
private void startOtherServices(a) {
// ...
ServiceManager.addService("sec_key_att_app_id_provider".new KeyAttestationApplicationIdProviderService(context));
mSystemServiceManager.startService(KeyChainSystemService.class);
ServiceManager.addService("scheduling_policy".new SchedulingPolicyService());
mSystemServiceManager.startService(TelecomLoaderService.class);
telephonyRegistry = new TelephonyRegistry(context);
ServiceManager.addService("telephony.registry", telephonyRegistry);
mEntropyMixer = new EntropyMixer(context);
mContentResolver = context.getContentResolver();
mSystemServiceManager.startService(ACCOUNT_SERVICE_CLASS);
mSystemServiceManager.startService(CONTENT_SERVICE_CLASS);
// Prepare the window, power, package, display serviceswm.systemReady(); mPowerManagerService.systemReady(...) ; mPackageManagerService.systemReady(); mDisplayManagerService.systemReady(...) ; mActivityManagerService.systemReady(...) ;// ...
}
Copy the code
At this point, the System_server main thread is finally started and enters the looper.loop () state, waiting for other threads to send messages to the main thread via handler.
Android-application: android-application: android-application: android-application: android-application
App process
AMS send request
ActivityManagerService is also created by SystemServer. If you start a new Activity with startActivity and the Activity is attached to a process that has not yet been started, a new process will be started. The Process that calls startActivity calls ActivityManagerService with Binder, and then calls the process.start () method through several layers (see Android-Activity startup), as shown below:
public static final ProcessStartResult start(final String processClass,
final String niceName,
int uid, int gid, int[] gids,
int runtimeFlags, int mountExternal,
int targetSdkVersion,
String seInfo,
String abi,
String instructionSet,
String appDataDir,
String invokeWith,
String[] zygoteArgs) {
return zygoteProcess.start(processClass, niceName, uid, gid, gids,
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, invokeWith, zygoteArgs);
}
Copy the code
Parameter processClass here is “android. App. ActivityThread”, it is in the first parameter, namely program initialization process to load the master file of Java classes. When the application process starts, the class is loaded into the process and its main() method is called as the entry point to the application process. The start() method of ZygoteProcess calls the startViaZygote() method of ZygoteProcess.
private Process.ProcessStartResult startViaZygote(final String processClass, final String niceName,
final int uid, final int gid, final int[] gids, int runtimeFlags, int mountExternal,
int targetSdkVersion, String seInfo, String abi, String instructionSet,String appDataDir,
String invokeWith, String[] extraArgs) throws ZygoteStartFailedEx {
ArrayList<String> argsForZygote = new ArrayList<String>();
argsForZygote.add("--runtime-args");
argsForZygote.add("--setuid=" + uid);
argsForZygote.add("--setgid=" + gid);
/ /...
synchronized(mLock) {
return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote); }}Copy the code
Start by setting its values, including uid, GID, and so on. Then call openZygoteSocketIfNeeded () method to connect “zygote” Socket, link Socket after success, is called zygoteSendArgsAndGetResult () method to further processing.
Let’s start with the openZygoteSocketIfNeeded() method:
private ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx {
if (primaryZygoteState == null || primaryZygoteState.isClosed()) {
try{ primaryZygoteState = ZygoteState.connect(mSocket); }}Copy the code
The value of the mSocket in the method is “zygote”, which is connected to the “zygote” Socket through the connect() method. Then look at zygoteSendArgsAndGetResult () method:
private static Process.ProcessStartResult zygoteSendArgsAndGetResult( ZygoteState zygoteState, ArrayList
args)
throws ZygoteStartFailedEx {
try {
final BufferedWriter writer = zygoteState.writer;
final DataInputStream inputStream = zygoteState.inputStream;
writer.write(Integer.toString(args.size()));
writer.newLine();
for (int i = 0; i < sz; i++) {
String arg = args.get(i);
writer.write(arg);
writer.newLine();
}
writer.flush();
}
Copy the code
The arguments passed in are written to the stream writer through the Socket, which is listened on by the runSelectLoop() method of the ZygoteServer class. After writing this data, the ZygoteServer class’s runSelectLoop() method is listened on.
Respond to the request
void setForkChild(a) {
mIsForkChild = true;
}
Runnable runSelectLoop(String abiList) {
ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
fds.add(mServerSocket.getFileDescriptor());
peers.add(null);
while (true) {
StructPollfd[] pollFds = new StructPollfd[fds.size()];
for (int i = 0; i < pollFds.length; ++i) {
pollFds[i] = new StructPollfd();
pollFds[i].fd = fds.get(i);
pollFds[i].events = (short) POLLIN;
}
try {
Os.poll(pollFds, -1);
} catch (ErrnoException ex) {
throw new RuntimeException("poll failed", ex);
}
for (int i = pollFds.length - 1; i >= 0; --i) {
if ((pollFds[i].revents & POLLIN) == 0) {
continue;
}
if (i == 0) {
ZygoteConnection newPeer = acceptCommandPeer(abiList);
peers.add(newPeer);
fds.add(newPeer.getFileDesciptor());
} else {
try {
ZygoteConnection connection = peers.get(i);
final Runnable command = connection.processOneCommand(this);
// Use the mIsForkChild variable to control the parent process in an infinite loop
if (mIsForkChild) {
// We're in the child. We should always have a command to run at this
// stage if processOneCommand hasn't called "exec".
if (command == null) {
throw new IllegalStateException("command == null");
}
return command;
} else {
// We're in the server - we should never have any commands to run.
if(command ! =null) {
throw new IllegalStateException("command ! = null");
}
if(connection.isClosedByPeer()) { connection.closeSocket(); peers.remove(i); fds.remove(i); }}}catch (Exception e) {
if(! mIsForkChild) { ZygoteConnection conn = peers.remove(i); conn.closeSocket(); fds.remove(i); }else {
throwe; }}finally {
mIsForkChild = false;
}
}
}
}
}
Copy the code
After entering the ZygoteConnection class’s processOneCommand() method:
Runnable processOneCommand(ZygoteServer zygoteServer) {
pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
parsedArgs.runtimeFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.instructionSet,
parsedArgs.appDataDir);
try {
if (pid == 0) {
// in child
zygoteServer.setForkChild();
zygoteServer.closeServerSocket();
IoUtils.closeQuietly(serverPipeFd);
serverPipeFd = null;
returnhandleChildProc(parsedArgs, descriptors, childPipeFd); }}}Copy the code
- The zygote.forkandWte () is used to fork the new application, and zygote.forkSystemServer () is used to fork the SystemServer process.
- Here by handleChildProc () method of handling, and before made with handleSystemServerProcess () to deal with.
After forking the new application process and returning PID equal to 0 to enter the child process, the handleChildProc() method is called to further process:
private Runnable handleChildProc(Arguments parsedArgs, FileDescriptor[] descriptors,FileDescriptor pipeFd) {
// ZygoteInit. Binder thread pools are created in ZygoteInit
// Each process must contain at least one Binder thread, regardless of whether it contains any activities or other components
return ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs,null /* classLoader */);
}
Copy the code
Java (); SystemServer (); systemServer.java (); The main() function is activityThread.java. The main() function is activityThread.main.
The only thing left to do is to parse the Forkandwpecte method:
public static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags,
int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir) {
// Stop the 4 Daemon child threads of Zygote, wait and make sure Zygote is single thread (for more efficient forking),
// Wait for these threads to stop and initialize the GC heap
VM_HOOKS.preFork();
// Resets nice priority for zygote process.
resetNicePriority();
// call fork() to create a new process, set the main thread ID of the new process, reset gc performance data, and set the signal handling function
int pid = nativeForkAndSpecialize(
uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
fdsToIgnore, startChildZygote, instructionSet, appDataDir);
// Start Zygote's 4 Daemon threads, Java heap defragment, etc.
VM_HOOKS.postForkCommon();
return pid;
}
Copy the code
The nativeForkandwis () method is a Native method, Final call is frameworks/base/core/jni/com_android_internal_os_Zygote cpp# com_android_internal_os_Zygote_nativeForkAndSpecialize () method, which in turn calls the ForkAndSpecializeCommon method:
static const char kZygoteClassName[] = "com/android/internal/os/Zygote";
int register_com_android_internal_os_Zygote(JNIEnv* env) {
gZygoteClass = MakeGlobalRefOrDie(env, FindClassOrDie(env, kZygoteClassName));
gCallPostForkChildHooks = GetStaticMethodIDOrDie(env, gZygoteClass, "callPostForkChildHooks"."(IZZLjava/lang/String;) V");
return RegisterMethodsOrDie(env, "com/android/internal/os/Zygote", gMethods, NELEM(gMethods));
}
static void SetSignalHandlers(a) {
struct sigaction sig_chld = {};
sig_chld.sa_handler = SigChldHandler;
}
// Sets the SIGCHLD handler back to default behavior in zygote children.
static void UnsetChldSignalHandler(a) {
struct sigaction sa;
memset(&sa, 0.sizeof(sa));
sa.sa_handler = SIG_DFL;
}
static void SigChldHandler(int /*signal_number*/) {
while ((pid = waitpid(- 1, &status, WNOHANG)) > 0) {
// If the just-crashed process is the system_server, bring down zygote
// so that it is restarted by init and system server will be restarted
// from there.
if (pid == gSystemServerPid) {
ALOGE("Exit zygote because system server (%d) has terminated", pid);
kill(getpid(), SIGKILL); }}}static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray javaGids, jint runtime_flags,
jobjectArray javaRlimits, jlong permittedCapabilities, jlong effectiveCapabilities,
jint mount_external, jstring java_se_info, jstring java_se_name,
bool is_system_server, jintArray fdsToClose, jintArray fdsToIgnore,
bool is_child_zygote, jstring instructionSet, jstring dataDir) {
// Sets the signal handler for the child process
SetSignalHandlers(a);pid_t pid = fork();
if (pid == 0) {
PreApplicationInit(a);// Clean up all descriptors that must be closed immediately
if (!DetachDescriptors(env, fdsToClose, &error_msg)) {
fail_fn(error_msg);
}
// Re-open all remaining open file descriptors so that they aren't shared
// with the zygote across a fork.
if(! gOpenFdTable->ReopenOrDetach(&error_msg)) {
fail_fn(error_msg);
}
MountEmulatedStorage(uid, mount_external, use_native_bridge, &error_msg);
// If this zygote isn't root, it won't be able to create a process group,
// since the directory is owned by root.
if(! is_system_server &&getuid() = =0) {
int rc = createProcessGroup(uid, getpid());
}
// selinux context
rc = selinux_android_setcontext(uid, is_system_server, se_info_c_str, se_name_c_str);
if (se_name_c_str == NULL && is_system_server) {
se_name_c_str = "system_server";
}
if(se_name_c_str ! =NULL) {
SetThreadName(se_name_c_str);
}
// Unset the SIGCHLD handler, but keep ignoring SIGHUP (rationale in SetSignalHandlers).
UnsetChldSignalHandler(a);/ / equivalent to invoke the zygote. CallPostForkChildHooks ()
env->CallStaticVoidMethod(gZygoteClass, gCallPostForkChildHooks, runtime_flags,
is_system_server, is_child_zygote, instructionSet);
}
return pid;
}
Copy the code
Then enter the zygote. CallPostForkChildHooks () method:
private static void callPostForkChildHooks(int runtimeFlags, boolean isSystemServer,
boolean isZygote, String instructionSet) {
/ / call ZygoteHooks. PostForkChild ()
VM_HOOKS.postForkChild(runtimeFlags, isSystemServer, isZygote, instructionSet);
}
// ZygoteHooks.java
public void postForkChild(int runtimeFlags, boolean isSystemServer, boolean isZygote,
String instructionSet) {
nativePostForkChild(token, runtimeFlags, isSystemServer, isZygote, instructionSet);
// Set the seed of the new process Random number to the current system time, that is, at the moment when the process is created, it determines the future Random number situation, that is, pseudo-random.
Math.setRandomSeedInternal(System.currentTimeMillis());
}
Copy the code
NativePostForkChild through JNI calls eventually call the following method: art/runtime/native/dalvik_system_ZygoteHooks cc# ZygoteHooks_nativePostForkChild () :
static void ZygoteHooks_nativePostForkChild(JNIEnv* env, jclass, jlong token, jint debug_flags, jstring instruction_set) {
// The token keeps track of the current thread
Thread* thread = reinterpret_cast<Thread*>(token);
// Set the main thread ID of the new process
thread->InitAfterFork(a);if(instruction_set ! =nullptr && !is_system_server) {
ScopedUtfChars isa_string(env, instruction_set);
InstructionSet isa = GetInstructionSetFromString(isa_string.c_str());
Runtime::NativeBridgeAction action = Runtime::NativeBridgeAction::kUnload;
if(isa ! = InstructionSet::kNone && isa ! = kRuntimeISA) { action = Runtime::NativeBridgeAction::kInitialize; } Runtime::Current() - >InitNonZygoteOrPostFork(env, is_system_server, action, isa_string.c_str());
} else {
Runtime::Current() - >InitNonZygoteOrPostFork(env, is_system_server,
Runtime::NativeBridgeAction::kUnload, nullptr, profile_system_server); }}Copy the code
Art /runtime/runtime.c #InitNonZygoteOrPostFork
void Runtime::InitNonZygoteOrPostFork(JNIEnv* env, bool is_system_server,
NativeBridgeAction action, const char* isa, bool profile_system_server) {
is_zygote_ = false;
if (is_native_bridge_loaded_) {
switch (action) {
case NativeBridgeAction::kUnload:
UnloadNativeBridge(a);// Uninstall the bridge library for cross-platform
is_native_bridge_loaded_ = false;
break;
case NativeBridgeAction::kInitialize:
InitializeNativeBridge(env, isa); // Initialize the bridge library for cross-platform
break; }}// Create a thread pool for Java heap processing
heap_->CreateThreadPool(a);// Reset THE GC performance data to ensure that the GCs of the process before creation is not calculated to the current app.
heap_->ResetGcPerformanceInfo(a);if(! safe_mode_ && (jit_options_->UseJitCompilation() || jit_options_->GetSaveProfilingInfo()) && jit_ == nullptr) {
/ / create the JIT
CreateJit(a); }// Set the signal handler function
StartSignalCatcher(a); }Copy the code
conclusion
The init process (pid=1) is the first user space process in Linux.
- Initialize environment variables and mount files;
- Parse and execute the init.rc file;
- Handling the exit of child processes (signal mode);
- Create a shared memory space for the properties server and start the properties server process.
- Enter the infinite loop state and execute the following process:
- Check whether a process needs to be restarted and restart it.
- It enters the wait state until the system property changes event (property_set changes the property value), or receives SIGCHLD signal from the child process, or keychord keyboard input event, etc. Then it exits the wait state and executes the corresponding callback function.
As you can see, the core work of the init process after startup is responding to property change events, reclaiming zombie processes, and restarting processes.
- When a process changes a system property value, the system sends an event notification to the init process over the socket, and the init process triggers the corresponding callback.
- Reclaim zombie processes. In the Linux kernel, if the parent process exits without waiting for the end of its child process, the child process becomes a zombie process and occupies system resources. To do this, init processes have a SIGCHLD receiver installed. When some child processes exit and its parent exits, it sends SIGCHLD signals to the init process, and the init process reclaims zombie child processes.
Call flow of Zygote startup process:
- Parses the parameters in init.zygote.rc, creates AppRuntime and calls the appruntime.start () method;
- Call AndroidRuntime’s startVM() method to create the VM, and then call startReg() to register the JNI function.
- Call zygoteinit.main () in JNI to enter the Java world for the first time;
- RegisterServerSocketFromEnv () method to establish a socket channel, zygote as the communication server, responds to client requests;
- Preload () preloads general classes, related resources, shared libraries, etc., to improve app startup efficiency;
- Next, through startSystemServer(), fork system_server process, is also the upper framework running carrier;
- Zygote calls the runSelectLoop() method to wait, wake up and do the work when it receives a request to create a new process.
Zygote preloads some resources to fork system_server, so that system_server can directly use these loaded resources such as JNI functions, common methods, shared libraries, resources, etc. In addition, the zygote process was chosen to fork the application instead of system_server because system_server has its own work to do with many threads such as AMS, WMS, PKMS, etc. This type of multithreaded parent fork may cause a deadlock.
In Linux, fork copies the parent’s user-space data to the child (copy-on-write) and then copies the current thread to the child, while all other threads in the parent evaporate. If, before the fork, one thread holds a lock, and another thread calls the fork to create a child process, the thread holding the lock in the child process “evaporates”, the lock is “permanently” locked from the child process’s point of view because its owner “evaporates”. If any thread in the child process locks the already held lock, a deadlock occurs.
The whole process can be summarized as follows: