Introduction to Cgroup v2 on Android
For the uninitiated, Control Groups (Cgroups) are a Linux kernel feature that allows for the organization of processes into hierarchical groups, enabling resource allocation and management. Cgroup v2, the second iteration, offers a unified hierarchy and a cleaner, more intuitive interface compared to its predecessor. On Android, Cgroups are fundamental to how the operating system manages resources, distinguishing between foreground and background applications, system services, and various other process types to optimize performance and battery life.
For Android power users, understanding and interacting with Cgroup v2 opens up a new realm of fine-grained control over device resources. This deep dive will equip you with the knowledge and tools to monitor and script Cgroup v2 behaviors, allowing for custom performance profiles, advanced battery management, and system-level debugging.
Understanding the Cgroup v2 Hierarchy on Android
Accessing the Cgroup v2 hierarchy is typically done via the `adb shell` (requiring root privileges). The primary mount point for the unified Cgroup v2 hierarchy is `/sys/fs/cgroup`. Within this directory, you’ll find various subdirectories and files that represent the controllers and their states.
Key Cgroup v2 controllers you’ll encounter on Android include:
- `cpu`: Controls CPU bandwidth.
- `memory`: Manages memory usage.
- `io`: Governs I/O bandwidth and operations.
- `pid`: Limits the number of processes in a group.
Android’s `init.rc` and `task_profiles.rc` scripts are responsible for setting up the initial Cgroup structure and assigning processes. For instance, foreground applications might reside in a Cgroup path like `/sys/fs/cgroup/top-app`, while background services might be in `/sys/fs/cgroup/background`. The exact paths can vary slightly between Android versions and device manufacturers.
Identifying Process Cgroups
To determine which Cgroup a specific process belongs to, you can inspect its `/proc//cgroup` file. First, identify the Process ID (PID) of your target application or service using `top`, `ps -A`, or `pidof`:
adb shell pidof com.android.chrome
Let’s assume the PID is `12345`. Now, check its Cgroup:
adb shell cat /proc/12345/cgroup
The output will typically show a single line for Cgroup v2, indicating the path within the unified hierarchy:
0::/uid_10069/pid_12345
This tells us that `com.android.chrome` is in `/sys/fs/cgroup/uid_10069/pid_12345`. Knowing this path is crucial for monitoring and manipulation.
Basic Monitoring with Cgroup v2 Files
Each Cgroup directory contains control files that allow you to read statistics and modify parameters. Let’s look at some essential ones.
CPU Controller Monitoring
The `cpu.stat` file provides detailed CPU usage statistics for a Cgroup:
adb shell cat /sys/fs/cgroup/uid_10069/cpu.stat
Typical output:
usage_usec 1234567890system_usec 123456user_usec 987654nr_periods 1234nr_throttled 5nr_bursts 1throttled_usec 500000burst_usec 100000
- `usage_usec`: Total CPU time consumed by the Cgroup’s tasks.
- `system_usec`: CPU time spent in kernel mode.
- `user_usec`: CPU time spent in user mode.
- `nr_periods`, `nr_throttled`, `throttled_usec`: Relate to CPU throttling based on `cpu.max` settings.
You can also inspect `cpu.max` (format: `MAX PERIOD`) and `cpu.weight` to understand current limits and priorities. `cpu.weight` is a relative priority, where a higher value gives more CPU bandwidth when contention occurs (default is 100).
Memory Controller Monitoring
The `memory.stat` file offers a comprehensive view of memory usage:
adb shell cat /sys/fs/cgroup/uid_10069/memory.stat
Output includes many metrics like:
anon 123456789file 987654321sock 123456kernel_stack 456789...
- `anon`: Anonymous memory (e.g., heap, stack).
- `file`: File-backed memory (e.g., mmap’d files, shared libraries).
- `sock`: Socket memory usage.
- `kernel_stack`: Kernel stack usage by tasks in the Cgroup.
`memory.current` shows the total memory currently in use by the Cgroup, and `memory.max` sets a hard limit on total memory.
I/O Controller Monitoring
The `io.stat` file provides I/O statistics, often broken down by device:
adb shell cat /sys/fs/cgroup/uid_10069/io.stat
Example output:
8:0 rbytes=123456789 wbytes=987654321 rios=1234 wios=5678
- `8:0`: Represents a device (e.g., `major:minor` for `/dev/root`).
- `rbytes`: Bytes read from the device.
- `wbytes`: Bytes written to the device.
- `rios`: Read I/O operations.
- `wios`: Write I/O operations.
This is useful for identifying apps or services causing excessive disk activity.
Building a Cgroup v2 Scripting Toolkit
The true power of Cgroup v2 comes from its scriptability. By writing values to Cgroup files, you can dynamically manage system resources. Remember, these operations require root access.
Goal: Dynamic Resource Management
- Improve foreground app responsiveness by boosting its resources.
- Reduce background battery drain by throttling resource-hungry services.
- Prevent runaway processes from monopolizing system resources.
Example 1: Prioritizing a Foreground Application
Let’s say you want to ensure your game or browser gets maximum CPU priority. You can adjust its `cpu.weight` and `cpu.max` (if the controller is enabled for writing and not constrained by Android’s top-level groups).
#!/system/bin/sh# Target package nameTARGET_PACKAGE="com.mygame.awesome"# Find PID and then Cgroup pathPID=$(pidof $TARGET_PACKAGE)if [ -z "$PID" ]; then echo "Process $TARGET_PACKAGE not found." exit 1fiCGROUP_PATH=$(cat /proc/$PID/cgroup | grep "^0::" | cut -d: -f3)if [ -z "$CGROUP_PATH" ]; then echo "Cgroup path for $TARGET_PACKAGE not found." exit 1fiFULL_CGROUP_DIR="/sys/fs/cgroup${CGROUP_PATH}"echo "Found Cgroup: ${FULL_CGROUP_DIR}"# Set CPU weight (default is 100, higher is more priority)echo 200 > "${FULL_CGROUP_DIR}/cpu.weight"# Set CPU max to 80% (80000 out of 100000 microseconds per period)echo "80000 100000" > "${FULL_CGROUP_DIR}/cpu.max"echo "Prioritized ${TARGET_PACKAGE} (PID: ${PID}) in Cgroup ${FULL_CGROUP_DIR}"
Save this as `priority_app.sh`, push to device, `chmod +x`, and run with `su`.
Example 2: Throttling Background Services
To curb a background service that’s consuming too many resources, you can lower its Cgroup parameters. Assume `com.example.background_hog` is the culprit.
#!/system/bin/sh# Target package name to throttleBACKGROUND_PACKAGE="com.example.background_hog"PID=$(pidof $BACKGROUND_PACKAGE)if [ -z "$PID" ]; then echo "Process $BACKGROUND_PACKAGE not found." exit 1fiCGROUP_PATH=$(cat /proc/$PID/cgroup | grep "^0::" | cut -d: -f3)if [ -z "$CGROUP_PATH" ]; then echo "Cgroup path for $BACKGROUND_PACKAGE not found." exit 1fiFULL_CGROUP_DIR="/sys/fs/cgroup${CGROUP_PATH}"echo "Found Cgroup: ${FULL_CGROUP_DIR}"# Reduce CPU weight (default 100, lower is less priority)echo 50 > "${FULL_CGROUP_DIR}/cpu.weight"# Set memory max to 100MB (100 * 1024 * 1024 bytes)echo "104857600" > "${FULL_CGROUP_DIR}/memory.max"echo "Throttled ${BACKGROUND_PACKAGE} (PID: ${PID}) in Cgroup ${FULL_CGROUP_DIR}"
Example 3: Monitoring for Runaway Processes
A more advanced script could periodically check `cpu.stat` or `memory.current` and take action if thresholds are exceeded.
#!/system/bin/sh# Monitor a specific process for high CPU usageTARGET_PACKAGE="com.example.runaway_candidate"THRESHOLD_CPU_USAGE=500000 # 0.5 seconds of CPU usage in a period while true; do PID=$(pidof $TARGET_PACKAGE) if [ -z "$PID" ]; then echo "Process $TARGET_PACKAGE not running. Waiting..." sleep 10 continue fi CGROUP_PATH=$(cat /proc/$PID/cgroup | grep "^0::" | cut -d: -f3) FULL_CGROUP_DIR="/sys/fs/cgroup${CGROUP_PATH}" if [ -z "$FULL_CGROUP_DIR" ]; then echo "Cgroup path not found for $TARGET_PACKAGE. Waiting..." sleep 10 continue fi # Get current CPU usage in microseconds CURRENT_USAGE=$(cat "${FULL_CGROUP_DIR}/cpu.stat" | grep usage_usec | awk '{print $2}') echo "Current CPU usage for ${TARGET_PACKAGE}: ${CURRENT_USAGE} usec" if [ "$CURRENT_USAGE" -gt "$THRESHOLD_CPU_USAGE" ]; then echo "WARNING: ${TARGET_PACKAGE} exceeding CPU threshold. Killing PID ${PID}." kill -9 $PID fi sleep 5 # Check every 5 secondsdone
Implementation Considerations and Best Practices
- Root Access Required: Manipulating Cgroup files necessitates root privileges.
- Persistence: Changes made directly to Cgroup files are often ephemeral, lost upon device reboot. For persistence, integrate your scripts into Android’s `init.rc` or a Magisk module that runs on boot.
- System Stability: Exercise extreme caution. Incorrect Cgroup settings can lead to system instability, crashes, or unresponsiveness. Always test changes incrementally and understand their potential impact.
- Android Specifics: Android’s resource management is complex. Many core processes and applications are already finely tuned. Understand existing `cgroup.rc` and `task_profiles.rc` policies before overriding them. Overriding critical system Cgroups can brick your device.
- Error Handling: Robust scripts should include checks for file existence, successful writes, and valid PIDs to prevent unexpected behavior.
Conclusion
Cgroup v2 offers an unparalleled level of control over process resource allocation on Android devices. By leveraging the shell and understanding the hierarchical nature of Cgroups, power users can transcend the default system behaviors. Whether you’re optimizing for peak gaming performance, extending battery life, or diagnosing resource contention, building your own Cgroup v2 monitoring and scripting toolkit empowers you to truly customize and master your Android experience. While requiring careful implementation and a solid understanding of Linux internals, the rewards in terms of system control and optimization are substantial.
Android Mobile Specs & Compare Directory
Are you researching mobile hardware properties, processor SoCs, GPU chipsets, or RAM configurations? Access our complete specs catalog to compare up to 5 devices side-by-side!
Compare Devices Specs →