Android is built on top of the Linux kernel. Therefore, every Android application or system daemon is fundamentally just a standard Linux process. To understand Android process management (like why the Low Memory Killer kills a specific app), you must first understand how the kernel views processes.
The task_struct Descriptor
In the eyes of the Linux kernel, a process is nothing more than a massive C data structure called task_struct (defined in include/linux/sched.h).
When you launch an Android app like Calculator, the kernel allocates a new task_struct in memory. This structure holds everything the kernel needs to know about the app. Here is a heavily simplified look at what this C structure looks like inside the kernel:
struct task_struct {
volatile long state; // -1 unrunnable, 0 runnable, >0 stopped
void *stack; // The process kernel stack
pid_t pid; // The unique Process ID
pid_t tgid; // Thread Group ID
struct task_struct __rcu *real_parent; // The parent process (e.g., Zygote)
struct mm_struct *mm; // Memory pointers to the app's RAM allocation
struct files_struct *files; // Array of open file descriptors
};
This struct tracks:
- PID (Process ID): A unique integer identifying the process.
- Memory Pointers (
mm_struct): Pointers to the virtual memory pages where the Calculator's code and variables reside. - Open Files (
files_struct): An array of file descriptors for any files, camera devices, or sockets the app has opened. - State: Is the app actively running on a CPU core, or is it asleep waiting for user input?
Process Creation (fork and clone)
Linux does not have a "create process from scratch" command. Instead, all new processes are created by duplicating an existing process.
fork(): This system call creates an exact duplicate of the parent process. The child gets a copy of the parent's memory, file descriptors, and CPU registers. In Android, the Zygote process usesfork()to instantly spawn new Android applications without having to reload the entire Java framework from disk.clone(): A highly configurable version offork(). It allows the parent to specify exactly which resources (like memory space or file descriptors) the child process should share. This is the underlying mechanism used to create threads.
// Example of a raw clone call in C
int clone_flags = CLONE_VM | CLONE_FS | CLONE_FILES | SIGCHLD;
pid_t child_pid = clone(child_func, child_stack, clone_flags, arg);
Note: The CLONE_VM flag tells the kernel to share the Virtual Memory, which makes this new process act like a thread!
Process States
A process does not continuously run on the CPU. The task_struct tracks its current state, which you can easily see if you run the top or ps command in an ADB shell.
- Running (R): The process is currently executing instructions on a physical CPU core, or it is in the run queue waiting for its turn.
- Sleeping (S/D): The process is paused, waiting for an event (like waiting for data to arrive over a network socket). Most background Android apps are in a sleeping state (
Sfor interruptible,Dfor uninterruptible disk sleep). - Zombie (Z): The process has terminated and released its memory, but its
task_structremains in the kernel table because its parent process has not yet acknowledged its death (via thewait()system call).
The Process Hierarchy
Because processes are created by duplicating parents, a strict hierarchy exists. Every process has a Parent PID (PPID).
If you trace the lineage of any Android process backward, you will always reach the exact same root ancestor: PID 1, which is the init process. The init process is spawned directly by the kernel during the boot sequence and is the ultimate parent of the entire Android user space.
Inspecting Processes via ADB
You can explore this hierarchy yourself. Connect an Android device and run:
adb shell ps -A -o USER,PID,PPID,STAT,ARGS
You will see output similar to this:
USER PID PPID STAT ARGS
root 1 0 S init
root 500 1 S zygote64
u0_a145 2345 500 S com.google.android.calculator
Notice how the Calculator app (2345) has a Parent PID of 500, which is the Zygote process! And Zygote's parent is init (PID 1).