Linux Kernel and Kernel Programming Part 3 Components of the Linux Kernel

3.3 The composition of the Linux kernel

3.3.1 Directory structure of Linux kernel source code

The Linux kernel source code contains the following directories.

arch: Contains code related to hardware architecture, each platform occupies a corresponding directory, such as i386, arm, arm64, powerpc, mips, etc. The Linux kernel currently supports about 30 architectures. In the arch directory, the support for Linux kernel process scheduling, memory management, interrupts, etc. of each platform and the chips of each platform is stored, as well as the board-level support code of each specific SoC and circuit board.

block: Block device driver I/O scheduling.

crypto: Commonly used encryption and hashing algorithms (such as AES, SHA, etc.), as well as some compression and CRC check algorithms.

documentation: General explanations and comments for various parts of the kernel.

drivers: device drivers, each different driver occupies a subdirectory, such as char, block, net, mtd, i2c, etc.

fs: Various file systems supported, such as EXT, FAT, NTFS, JFFS2, etc.

include: header files, system-related header files are placed in the include/linux subdirectory.

init: Kernel initialization code. The famous start_kernel() is located in the init/main.c file.

ipc: Code for inter-process communication.

kernel: The core part of the kernel, including process scheduling, timers, etc., and part of the code related to the platform is placed in the arch/*/kernel directory.

lib: library file code.

mm: memory management code, and part of the code related to the platform is placed in the arch/*/mm directory.

net: Network-related code that implements various common network protocols.

scripts: Script files used to configure the kernel.

security: mainly a SELinux module.

sound: The driver core code and common device drivers for ALSA and OSS audio devices.

usr: implements cpio for packaging and compression, etc.

include: Kernel API level header files.

The kernel generally needs to separate the software architecture of drivers and arch. The driver does not contain board-level information, so that the driver is cross-platform. At the same time, the general part of the kernel (such as kernel, fs, ipc, net, etc.) is separated from the specific hardware (arch and drivers).

3.3.2 Components of the Linux Kernel

    As shown in Figure 3.3, the Linux kernel is mainly composed of process scheduling (SCHED), memory management (MM), virtual file system (VFS),

Network interface (NET) and inter-process communication (IPC) are composed of 5 subsystems.


Figure 3.3 Components and relationships of the Linux kernel

1. Process scheduling

    Process scheduling controls the access of multiple processes in the system to the CPU, so that multiple processes can be executed in "micro-serial, macro-parallel" in the CPU. Process scheduling is at the center of the system, and other subsystems in the kernel depend on it, because each subsystem needs to suspend or resume processes.

    As shown in Figure 3.4, a Linux process switches between several states.


Figure 3.4 Linux process state transition

    In device driver programming, when the requested resources cannot be satisfied, the driver will generally schedule other processes to execute and put the process into a sleep state. It will not be woken up and enter the ready state until the requested resources are released. Sleep is divided into interruptible sleep and uninterruptible sleep. The difference between the two is that interruptible sleep wakes up when a signal is received.

    Processes that are completely in the TASK_UNINTERRUPTIBLE state cannot even be "killed", so the kernel after Linux 2.6.26 also has a TASK_KILLABLE state, which is equal to "TASK_WAKEKILL|TASK_UNINTERRUPTIBLE" and can respond to fatal signals.

    In the Linux kernel, the task_struct structure (include/linux/sched.h) is used to describe the process, which contains pointers describing the memory resources, file system resources, file resources, tty resources, signal processing, etc. of the process. Linux threads are implemented using a lightweight process model. When a thread is created in the user space through the pthread_create() API, the kernel essentially just creates a new task_struct and points all the resource pointers of the new task_struct to the one that created it. Resource pointer for task_struct.

The vast majority of processes (and multiple threads within a process) are created by applications in user space, and enter kernel space through system calls     when they have underlying resource and hardware access requirements . Sometimes, in kernel programming, if you need several tasks to execute concurrently, you can start kernel threads, which have no user space . The function to start the kernel thread is: pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags);

2. Memory management

    The main role of memory management is to control the safe sharing of the main memory area by multiple processes . Linux memory management completes the conversion from virtual memory to physical memory for each process when the CPU provides a memory management unit (MMU). Linux 2.6 introduced support for MMUless CPUs.

    As shown in Figure 3.5, in general, each process of Linux with a 32-bit processor enjoys 4GB of memory space, 0~3GB belongs to user space, and 3~4GB belongs to kernel space. Memory and high-end memory are handled differently. The specific boundary between kernel space and user space can be adjusted. Under the kernel configuration option Kernel Features→Memory split, the boundary can be set to 2GB or 3GB.


Figure 3.5 Linux process address space

    As shown in Figure 3.6, the overall memory management of the Linux kernel is relatively large, including the underlying Buddy (partner) algorithm, which is used to manage the occupancy of each page, the slab allocator in the kernel space, and the secondary C library in the user space. manage. In addition, the kernel also provides support for page caching, using memory to cache the disk, and the per backing device info flusher thread is used to flush the dirty page cache to the disk. Kswapd (swap process) is a kernel thread for page reclamation (including file-backed pages and anonymous pages) in Linux. It uses the least recently used (LRU) algorithm for memory reclamation.


3. Virtual file system

    As shown in Figure 3.7,


Figure 3.7 Linux virtual file system

    The Linux virtual file system hides the specific details of various hardware and provides a unified interface for all devices. Moreover, it is independent of each specific file system and is an abstraction of various file systems. It provides unified vfs_read(), vfs_write() and other interfaces for upper-layer applications, and calls the member functions of the file_operations structure implemented in the specific underlying file system or device driver.

4. Network interface

    The network interface provides access to various network standards and support for various network hardware. As shown in Figure 3.8, the network interface in Linux can be divided into network protocols and network drivers. The network protocol part is responsible for implementing every possible network transmission protocol, and the network device driver is responsible for communicating with hardware devices. Hardware devices have corresponding device drivers.

    

Figure 3.8 Linux network architecture

    There are many types of protocol stacks supported by the Linux kernel, such as Internet, UNIX, CAN, NFC, Bluetooth, WiMAX, IrDA, etc. The upper-layer applications use the socket interface uniformly.

5. Interprocess communication

    Interprocess communication supports communication between processes. Linux supports a variety of communication mechanisms between processes, including semaphores, shared memory, message queues, pipes, UNIX domain sockets, etc. These mechanisms can assist multiple processes and multiple resources. Mutually exclusive access, synchronization between processes, and message passing. In actual Linux applications, people tend to use UNIX domain sockets more than mechanisms such as message queues in System V IPC. The Android kernel has added a Binder inter-process communication method.

    The dependencies between the 5 components of the Linux kernel are as follows.

    The relationship between process scheduling and memory management: These two subsystems depend on each other. In a multi-program environment, to run a program, a process must be created for it, and the first thing to do when creating a process is to load the program and data into memory.

    The relationship between inter-process communication and memory management: The inter-process communication subsystem relies on memory management to support a shared memory communication mechanism, which allows two processes to access a common memory area in addition to their own private space.

    The relationship between the virtual file system and the network interface: The virtual file system uses the network interface to support the Network File System (NFS), and also uses the memory management to support the RAMDISK device.

    The relationship between memory management and virtual file system: memory management uses virtual file system to support swap, and the swap process is regularly scheduled by the scheduler, which is why memory management depends on process scheduling. When a memory map accessed by a process is swapped out, the memory manager makes a request to the virtual file system and, at the same time, suspends the currently running process.

    In addition to these dependencies, all subsystems in the kernel depend on some common resources. These resources include APIs used by all subsystems, such as functions for allocating and freeing memory space, functions for outputting warning or error messages, and debugging interfaces provided by the system.

3.3.3 Linux Kernel Space and User Space

    Modern CPUs often implement different operating modes (levels), and different modes have different functions. High-level programs often cannot access low-level functions and must switch to low-level modes in some way.

For example, ARM processors are divided into 7 working modes.

User mode (usr): Most applications run in user mode. When the processor is running in user mode, some protected system resources cannot be accessed.

Fast Interrupt Mode (fiq): For high-speed data transfer or channel processing.

External interrupt mode (irq): for general interrupt handling.

Admin Mode (svc): The protected mode used by the operating system.

Data access abort mode (abt): This mode is entered when data or instruction prefetch is aborted, which can be used for virtual storage and storage protection.

System Mode (sys): Runs privileged operating system tasks.

Undefined instruction abort mode (und): This mode is entered when an undefined instruction is executed, which can be used to support software emulation of hardware coprocessors.

The principle of ARM Linux system call implementation is to use swi software interrupt to fall into management mode (svc) from user (usr) mode .

    For another example, the x86 processor contains 4 different privilege levels, called Ring 0~Ring 3. Under Ring0, privilege-level instructions can be executed, access to any I/O device, etc., while Ring3 is restricted to many operations.

    Linux systems can take advantage of this hardware feature of the CPU, but it only uses two levels. In a Linux system, the kernel can do anything, while applications are prohibited from direct access to hardware and unauthorized access to memory. For example, with an x86 processor, user code runs at privilege level 3 and system kernel code runs at privilege level 0.

    Kernel space and user space are used to distinguish two different states of program execution, and they use different address spaces. Linux can only transfer control from user space to kernel space through system calls and hardware interrupts .

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326684989&siteId=291194637