After the system starts, memory allocation and release in the Linux system kernel is performed using the buddy system mechanism after the mem_init() function completes. This mechanism can allocate both high memory and low memory. In addition to the buddy system, Linux has several other methods for memory allocation and release, but they are all based on the buddy system. This means that they ultimately request physical memory from the buddy system and release physical memory back to the buddy system.
The slab allocator, also known as the high-end cache memory allocator, divides large blocks of memory allocated from the buddy system into a set of smaller blocks of the same size, which are then managed using some data structures. We refer to this managed physical memory as cache memory. This mechanism can only allocate memory for objects of a specific size, which can be a data structure object, a buffer, or a block of memory, etc. To allocate memory for a specific object using the slab allocator, a cache memory must first be created for that object, which involves creating a kmem_cache structure object.
Allocating non-contiguous physical addresses while ensuring that the linear address (virtual address) space is contiguous involves first requesting a block of target size from the high-end linear address space, aligning its size to the page frame size, and then requesting one page of physical memory from the buddy system for each page of linear address space. Finally, all these individually requested physical memory pages are mapped to their corresponding high-end linear address space page addresses.
Allocating user space for processes is similar to allocating non-contiguous physical addresses while ensuring that the linear address is contiguous. It first requests a block of target size from the user linear address space (for 32-bit systems, this is 0~3GB), and then prepares the corresponding size of secondary page table space for these address spaces. However, it does not immediately request the corresponding physical memory; instead, it waits until the system actually accesses a page of these address spaces. At that time, it requests one page of physical memory from the buddy system for the accessed user linear address space page and establishes a mapping relationship. This is known as the demand paging mechanism.
Mapping files or devices to user space is achieved through the same interface function (sys_mmap()) as allocating user space for processes. However, when mapping files or devices, the physical memory space of the device or file has already been allocated. It only needs to find a corresponding size of unmapped space in the user space and establish mappings for them one by one. The physical space of the files or devices mapped to user space will have a corresponding mapping space in both the user linear address space and the kernel linear address space. Typically, when mapping files/devices to user space, it is based on the mapping address of the file/device space in the kernel linear address space to find its corresponding secondary page descriptor, and then extract its physical page number from the secondary page descriptor to find its physical address and the corresponding struct page structure object. The function that implements this is vmalloc_to_page(va). This allows both user processes and the kernel to access the contents of these devices and files.
User processes map files or devices to user space for easier and faster access to the contents of the files or devices, as user processes cannot directly access kernel space. Various high-end linear mapping operations for low-end physical memory, high-end physical memory, or I/O physical space in the system involve requesting a corresponding size of linear address space within certain ranges of high-end linear address space for the low-end or high-end physical memory space or I/O physical space that has already been allocated from the buddy system, and then establishing mapping tables for them one by one. Depending on the area where the high-end linear address is located, the corresponding mapping functions and specific operations also differ. These functions include vmap(), ioremap(), kmap(), and kmap_atomic().