Android kernel features overview

    “But I’m washing the carburetor!”
    Anecdote


    Introduction

    In the kindergarten, like-minded people we dissected grasshoppers in the hope of understanding their structure. At school, the radio "Russia" was soldered. At the institute, the turn came to cars whose nuts were repeatedly rearranged. Interests have changed, but the desire to “disassemble” sometimes wakes up, and today it is aimed at Android.

    How many times have Android source codes helped you out? I can’t even count me. Android is an open project, but, unfortunately, we only have the ability to read; editing an Android code without being a Google employee is almost impossible. We will sadden over this moment and load the repository. How to do this is well described on the official website .



    General architecture

    The architecture of Android can be schematically depicted as follows:



    The original scheme does not contain information about the features of the kernel and does not focus on Binder and system services. But Binder is the “glue” that binds all components of the system.

    As a rule, books describe the upper left blue rectangle, that is, the API that is available to the application developer. We are interested in everything below. Today we will consider only the core.

    Kernel The

    kernel is the central part of any distribution called Linux. Despite the availability of “ clean”Kernels, many developers (Ubuntu, Fedora, SuSe, etc.) add their patches to it before being included in the distribution. Android goes the same way, only at the cost of losing direct compatibility: it will not start on a “clean” kernel. Currently, there are intentions to include “androidisms” in the main version of the kernel; in 2011, Linus Torvalds gave this process 4-5 years. Success has already been achieved as part of the inclusion of the wakelocks mechanism in kernel version 3.5.

    Let's consider “androidisms” in more detail.

    Wakelocks

    The history of this mechanism is epic, it will draw on the collection of articles “The way of wakelocks to Linux”: their discussion took about 2000 letters in the LKML mailing list .

    Desktop computers and laptops have an established system of energy modes (x86 processors have several): the computer runs “at full speed” when something is done, and goes into energy-efficient mode when the system is idle. Going into "sleep" mode occurs either after a rather long period of inactivity, or manually, for example, when closing the lid of a laptop.

    On phones, a different mechanism was required: the main state of the system was “hibernation”, exit from it is carried out only when necessary. Thus, the system can fall asleep, even if some application is active. Android implemented a wakelock mechanism: if an application (or driver) performs something important that should reach its logical end, it “captures” wakelock, preventing the device from falling asleep.

    Attempts to port the wakelock mechanism to the kernel caused resistance from many developers. Android programmers solved a specific problem, the solution of which was a certain mechanism. The conditions of the problem were very narrow. The target platform is ARM, so its features were used: ARM processors initially assumed a frequent change in the “sleep” and “wake” operating modes, unlike x86. On Android, applications communicate with the power management system through PowerManager , but what about Linux client applications?

    Android developers didn’t even try to find a common solution “for the future”, which then would seamlessly flow into the main core, did not consult the Linux kernel community on this issue. Can you blame them? Despite all the problems and discussions, as mentioned above, an API with identical autosleep functionality has appeared in the kernel .

    Android application programmers rarely have to deal with wakelock-s, as the platform and drivers process their obligations taking into account the "sleep" mode. However, the familiar PowerManager will help intervene in this process .. By the way, the author comes up with only one scenario: to prevent the phone from falling asleep when starting the service from BroadcastReceiver, which is decided by the auxiliary class from the Android Support Library WakefulBroadcastReceiver .

    Low Memory Killer

    In the standard Linux kernel, there is Out of Memory Killer , which, based on the badness parameter, determines the process to kill: Thus, the more the process consumes memory and the less it lives, the less lucky it is. All programmers who read

    badness_for_task = total_vm_for_task / (sqrt(cpu_time_in_seconds) *
    sqrt(sqrt(cpu_time_in_minutes)))




    documentation or interviews, they know that, firstly, the process can be “killed” and if there are free resources, secondly, the candidate for crowding out is selected according to other criteria: the presence of “live” Android component, visibility to the user, and so on .

    The mechanism is quite simple: each process is assigned a priority from -17 to 16, while the higher the priority, the higher the likelihood of killing the process, and, depending on the amount of free memory, the priority is selected, from which the processes will be completed. Priorities are described in ProcessList.java . Interestingly, the priority of the HOME_APP_ADJ home screen application is quite high, but I thought: why does it constantly restart?

    The arrays mOomAdj and mOomMinFreeLow / mOomMinFreeHigh just set the “when to clean what” rules:

    private final int[] mOomAdj = new int[] {FOREGROUND_APP_ADJ, VISIBLE_APP_ADJ, PERCEPTIBLE_APP_ADJ, BACKUP_APP_ADJ, CACHED_APP_MIN_ADJ, CACHED_APP_MAX_ADJ};
    private final long[] mOomMinFreeHigh = new long[] {49152, 61440, 73728,86016, 98304, 122880};


    Thus, the home screen application is forced out with the remaining free memory of 73728 KB on the phone with a screen of 1280x800 and RAM of 700 MB.
    The ProcessList passes the corresponding values ​​to the kernel , as can be seen in its updateOomLevels method.

    Priorities for the processes are set by the Activity Manager Service , one of the many system services that you can communicate with through the Activity Manager .

    Binder

    Binder , along with other solutions (Files, Sigmals, Sockets, Pipes, Semaphores, Shared Memory, etc.), solves the problem of interprocess communication. The legs of this solution grow from the OpenBinder project , the developers of which at one time switched to the Android team.

    Bionic(libc implementation) does not use System V IPC , since in the Android environment standard tools will lead to resource leaks.

    Features:
    1. Flow control (we all remember that a service that supports AIDL must work in a multi-threaded environment). The maximum number of threads is 15 ( ProcessState.c , open_driver method), so you should not block Binder threads in large numbers without unnecessary need.
    2. A death reporting mechanism for a process holding a Binder “ Link to Death ” object . For example, through it, Window Manager learns about the death of the application and removes the windows associated with it. Also, the LocationManager at the death of all its listeners ceases to interrogate the GPS receiver. Lowmemorykiller satisfied. :)
    3. 2 call modes: blocking and non-blocking (oneway). In the first case, the calling thread is blocked and waits for the method to be processed in the thread of the handler process. Programmers simply call methods through the point, the platform takes over the interaction of the threads.
    4. Pass UID and PID for security. Through them, system services determine whether the calling process has the right to perform the requested actions.
    5. For Java programmers, tools for creating Proxy and Stubs to convert calls to Java methods in a Binder transaction.


    Let's see how this works on the example of LocationManager.



    When we want to get GPS information, the following happens:
    1. Our application calls the appropriate method on the LocationManager.
    2. The LocationManager delegates a call to a proxy object that converts Java methods and objects into a Binder transaction (the proxy object for the LocationManager is mService).
    3. The transaction is sent to the kernel driver, which redirects it to the LocationManagerService inherited from .LocationManager.Stub.
    4. .LocationManager.Stub does the reverse: expands the transaction into a call to the Java method.
    5. .LocationManagerService processes the request (using, for example, the GPS driver).
    6. The Stub object packs the response into a transaction, and the process goes in the opposite direction.
    7. The driver forwards the response back.
    8. The proxy object decompresses the result of the method call into Java objects.


    As you can see, quite a lot of logic is hidden behind the call to system service methods.

    Ashmem

    Anonymous Shared Memory (ashmem) - a shared memory mechanism. In Linux, as a rule, this mechanism is implemented through POSIX SHM . Android developers considered it insufficiently protected, which could play into the hands of malware. The features of ashmem are a reference counter, upon resetting of which the shared memory can be freed (for example, the memory is freed when all processes using it are completed), and the reduction of the shared region when there is not enough memory in the system.

    A vivid example of using ashmem is the zygote process, in which the starting version of Dalvik VM is loaded with the base classes and resources loaded, and the rest of the applications simply reference this memory.

    Binder has a 1MB transaction size limit (otherwise, a TransactionTooLargeException will be thrown ). If we need to transfer a large amount of data from one process to another, we can just use Ashmem: create a MemoryFile and transfer the file descriptor to another process.

    Logger

    Regular distributions, as a rule, use two logging systems: the kernel log, accessible through the dmesg command, and system logs, usually located in the / var / log directory.

    The Android system includes several cyclic buffers for storing messages of user programs (which extends the lifetime of memory cards, since read-write cycles are not wasted) and does not have additional delays from working with sockets, which are used in the standard syslog .



    The diagram shows the general Android logging system. The logging driver provides access to each buffer through / dev / log / *. Applications do not have access to them directly, but through the liblog library. The liblog library communicates with the Log , Slog, and EventLog classes . The adb logcat command displays the contents of the “main” buffer.

    Conclusion

    In this article, we briefly examined some of the features of Android as a Linux system. Some other parts (pmem, RAM console, etc.) remained outside the brackets, as well as such important aspects of the platform as a whole, such as System Service, the system startup process, and others. If this topic is interesting, in the following articles we will consider them.

    Also popular now: