Android booting sequence Explained
Today we will be looking into the Android boot process. When the user press power button then the booting sequence starts.
First thing that is loaded while booting Android is BOOTLOADER . CPU has a hard coded address from where the first instruction is fetched. This address generally points to the chip which has BOOTLOADER programmed on it. The main function of bootloader is to :
- Initialize RAM
- Put basic HW in quiescent state
- Load kernel and RAM disk
- Jump to kernel
Now kernel has been loaded into the RAM the control passes to the Kernel. The initial kernel startup is very hardware dependent, but its purpose is to set things up so that the CPU can start executing C code as early as possible. Once that’s done, the kernel jumps to the architecture-independent start_kernel() function, initializes its
various subsystems, and proceeds to call the “init” functions of all built-in drivers. The majority of messages printed out by the kernel at startup come from these steps. The kernel then mounts its root filesystem and starts the init process.
That’s when Android’s init kicks in and executes the instructions stored in its /init.rc file to set up environment variables such as the system path, create mount points, mount filesystems, set OOM adjustments, and start native daemons.
These native daemons are started and they initialize their corresponding module. We will focus on special daemon called Zygote which plays a very crucial role in launching applications. Its functionality is centralized here in order to unify the components shared by all apps and to shorten their start-up time. The init doesn’t actually start the Zygote directly; instead it uses the app_process command to get Zygote started by the Android Runtime. The runtime then starts the first Dalvik VM of the system and tells it to invoke the Zygote’s main().
Zygote is active only when a new app needs to be launched. To achieve a speedier app launch, the Zygote starts by preloading all Java classes and resources that an app may potentially need at runtime. This effectively loads those into the system’s RAM. The Zygote then listens for connections on its socket (/dev/socket/zygote) for requests to start new apps. When it gets a request to start an app, it forks itself and launches the new app. The beauty of having all apps fork from the Zygote is that it’s a “virgin” VM that has all the system classes and resources an app may need preloaded and ready to be used. In other words, new apps don’t have to wait until those are loaded to start executing.
All of this works because the Linux kernel implements a copy-on-write (COW) policy for forks. As you may know, forking in Unix involves creating a new process that is an exact copy of the parent process. With COW, Linux doesn’t actually copy anything. Instead, it maps the pages of the new process over to those of the parent process and
makes copies only when the new process writes to a page. But in fact the classes and resources loaded are never written to, because they’re the default ones and are pretty much immutable within the lifetime of the system. So all processes directly forking from the Zygote are essentially using its own mapped copies. Therefore, regardless of the
number of apps running on the system, only one copy of the system classes and the resources is ever loaded in RAM.
Although the Zygote is designed to listen to connections for requests to fork new apps, there is one “app” that the Zygote actually starts explicitly: the System Server. This is the first app started by the Zygote, and it continues to live on as an entirely separate process from its parent. The System Server then starts initializing each system service it houses and registering it with the previously started Service Manager. One of the services it starts, the Activity Manager, will end its initialization by sending an intent of type Intent.CATEGORY_HOME. This starts the Launcher app, which then displays the home screen familiar to all Android users