By Joe Knapka
Colyli
The initialization process of the memory management system is divided into three basic stages:
Activation page memory management
Initialize the page table of the kernel in swapper_pg_dir.
Initializes a series of kernel data related to memory management.
Turning On Paging (i386)
Enable paging (i386)
The Kernel code is loaded to the physical address 0x100000 (1 MB), and is remapped
Position of PAGE_OFFSET + 0x100000 (PAGE_OFFSET is 3 GB on IA32, that is, the user in the Process Virtual Address
The division between the space and the kernel space ). This is done by ing the physical address to the compiled page table (in
Arch/i386/kernel/head. S) 0-8 MB as well as PAGE_OFFSET-PAGE_OFFSET + 8 Mb implementation. Then
Jump to start_kernel in init/main. c. This function is located at PAGE_OFFSET + an address.
This looks a bit tricky. Note that the code that starts the paging mechanism in head. S is implemented by making it
Address space is no longer effective to achieve this; therefore, 0-4 MB is mapped (not clear: hence the 0-4 MB
Identity mapping .). Before the paging mechanism is enabled, start_kernel will not be called.
Set the position where he runs in PAGE_OFFSET + somewhere. Therefore, the page table in head. S must also map the kernel code.
The address used to jump to the staert_kernel. Therefore, PAGE_OFFSET is mapped (unknown ).
White: hence the PAGE_OFFSET mapping .).
The following is some amazing code when the paging mechanism is started in head. S:
/*
* Enable Paging
*/
3:
Movl $ swapper_pg_dir-_ page_offset, % eax
Movl % eax, % H6/* set the page table pointer ..*/
Movl % Cr0, % eax
Orl $0x80000000, % eax
Movl % eax, % Cr0/* .. and set paging (PG) bit */
JMP 1f/* flush the prefetch-queue */
1:
Movl $ 1f, % eax
JMP * % eax/* Make sure EIP is relocated */
1:
The code between two 1 labels loads the address of the second label 1 to eax and jumps to it. This
The EIP of the instruction pointer register points to the physical address of 1 MB + a value. The virtual addresses of labels in the kernel are empty.
(Page_offset + A location), so this code will be effective EIP from the physical address space to the virtual
Address space.
The start_kernel function initializes all kernel data and then starts the init kernel thread. Start_kernel
One of the first few things is to call the setup_arch function, which is a setup letter related to the specific architecture.
Number of calls to the lower-layer initialization details. For the X86 platform, these functions are
ARCH/i386/kernel/setup. C.
The first thing related to memory in setup_arch is to calculate low-end memory (low-memory) and high-end memory.
(High-memory) Number of valid pages; each memory type (each memory type) maximum number of pages respectively
Save it in the global variables highstart_pfn and highend_pfn. High-end memory is not directly mapped to the kernel virtual
Memory (VM); this will be discussed later.
Next, setup_arch calls the init_bootmem function to initialize the memory distributor (BOOT-time
Memory Allocator ). The bootmem memory distributor is used only in the System Boot Process and is the permanent number of kernels.
Data Distribution page. Therefore, we will not involve much of it. Remember that the bootmem distributor (bootmem
Allocator) provides pages during kernel initialization. These pages are reserved for the kernel, as if they are from the kernel scene
They do not participate in any memory management activities after the system starts.
Initialize the kernel page table
Then, setup_arch calls the paging_init function in arch/i386/MM/init. C. This function has done some
Events. First, it calls the pagetable_init function to map the entire physical memory, or at page_offset to 4 GB
As much physical memory as possible, starting from page_offset.
In the pagetable_init function, we precisely set up the kernel page table in swapper_pg_dir and map it
The entire physical memory as of page_offset.
This is a simple arithmetic activity that fills correct values in the page Directory and page table. The ing is created in
In swapper_pg_dir, that is, the kernel page directory. This is also the page directory used during page initialization. (When Using
When a 4 MB page table is created, the virtual address of the next 4 MB boundary will not be mapped here, but there is nothing to do with it,
Because we will not use this memory, there is no problem ). If the remaining physical memory is not mapped,
That is, memory larger than the 4GB-PAGE_OFFSET range. These memories are only after the CONFIG_HIGHMEM option is set.
To use (that is, use memory larger than 4 GB ).
Close to the end of the pagetable_init function, we call fixrange_init as a fixed virtual memory during compilation.
Map the reserved page table. These tables are hard-coded to the virtual addresses in the Kernel for ing, but they are not added
Part of the kernel data. The Fixmap table is mapped to the physical memory by calling the set_fixmap function at runtime.
After fixmap is initialized, if CONFIG_HIGHMEM is set, we need to allocate some page tables to kmap.
Distributor. Kmap allows the kernel to map any page of the physical address to the virtual address space of the kernel for temporary use.
This is useful, for example, ing physical memory that cannot be directly mapped in pagetable_init.
Fixmap and kmap page tables occupy part of the top of the kernel virtual space. Therefore, these addresses cannot be
PAGE_OFFSET ing is permanently mapped to the physical page. For this reason
MB is reserved (vmalloc still uses the address in this range ). (I do not know the following sentence.
Any physical pages that wowould otherwise be mapped into
Page_offset mapping in the 4 GB-128 MB range are instead (if config_highmem is
Specified) Authorized ded in the high memory zone, accessible to the kernel only
Kmap (). If config_higmem is not set, these pages are completely unavailable. This only applies to a large number of configurations.
Storage machine (more than 900 MB or more ). For example, if page_offset = 3 GB and the machine has 2 gb ram,
Therefore, only the first 1 gb-128mb physical memory can be mapped to the page_offset and fixmap/kmap address ranges.
Perimeter. The remaining pages are unavailable-in fact, for user process ing, they can be directly mapped pages
-- But the kernel cannot directly access them.
Return to paging_init. We can call the kmap_init function to initialize the kmap system.
The first kmap_pagetable [in tlb?] is cached for a single request. Then, we calculate the zone size and call
Free_area_init creates mem_map, initializes freelist, and initializes the zone distributor. All
Freelist is initialized as null, and all pages are marked as reserved (not accessible by the VM system );
In this case, it will be corrected.
After paging_init is complete, the physical memory is distributed as follows [note that this is not all true in the 2.4 kernel]:
0x00000000: 0-page
0x00100000: Kernel-Text
0x ???????? : Kernel_data
0x ???????? = _ End: whole-mem pagetables
0x ???????? : Fixmap pagetables
0x ???????? : Zone data (mem_map, zone_structs, freelists & c)
0x ???????? = Start_mem: free pages
This memory is mapped to PAGE_OFFSET by swapper_pg_dir and whole-mem-pagetables.
Further VM subsystem Initialization
Now let's go back to start_kernel. After paging_init is complete, we perform some operations for other subsystems of the kernel.
Additional configuration work, some of which use the bootmem distributor to allocate additional kernel memory. From the memory management perspective
Here, the most important thing is kmem_cache_init, which initializes the data of the slab distributor.
Shortly after calling kmem_cache_init, we called mem_init. This is done by clearing the idle physical page
The PG_RESERVED bit in the zone data completes the initialization of freelist at the beginning of free_area_init;
Clear the PG_DMA bit for pages that cannot be used as DMA, and then release all available pages to their respective zones. Last
Step by step, it is interesting to complete in the free_all_bootmem function in bootmem. c. He established a partner bitmap and
Freelist describes all existing unreserved pages. This is done by simply releasing them and letting free_page_ OK
Do the right thing. Once mem_init is called, The bootmem distributor is no longer used, so all of its
Page will also be released to the zone distributor world.
Segment
Segments are used to divide the linear address space into dedicated blocks. Linear Space is managed by the VM subsystem. X86 architecture from hard
The segment mechanism is supported on the instance. You can specify an address based on the offset of the segment + segment. Here the address is described
Linear (virtual address) of a range with specific attributes (such as protection attributes ). In fact, in the x86 architecture
You must use the segment mechanism. Therefore, we need to set four segments:
One kernel text Segment: from 0 to 4 GB
One kernel data segment: from 0 to 4 GB
One user text Segment: from 0 to 4 GB
One user data segment: from 0 to 4 GB
Therefore, we can use any valid segment selector to access the entire virtual address space.
Problem:
Where is the segment set?
Answer:
The Global Descriptor Table (GDT) is defined in the 450 rows of head. s. The GDT register is loaded in Row 3.
Problem:
Why do we split the kernel segment from the client. Are they all authorized to access the entire 4 GB range?
Answer:
This is because the kernel and user segment have different protection mechanisms:
. Quad 0x00cf9a000000ffff/* 0x10 kernel 4 GB code at 0x00000000 */
. Quad 0x00cf92000000ffff/* 0x18 kernel 4 GB data at 0x00000000 */
. Quad 0x00cffa000000ffff/* 0x23 user 4 GB code at 0x00000000 */
. Quad 0x00cff2000000ffff/* 0x2B user 4 GB data at 0x00000000 */
The segment register (CS, DS, etc.) contains a 13-bit index of the descriptor table. The index pointing to the descriptor tells the CPU to choose
The attribute of the selected segment. The low 3 bits of the segment selector are not used to index the descriptor table, but to store the descriptor type (full
Bureau or local) and the required privilege level. Therefore, the kernel segment selectors 0x10 and 0x18 use the privileged level 0 (RPL0 ),
User selectors 0x23 and 0x2B use the most privileged RPL 3.
Note that the kernel of the Third High-Order byte group is different from that of the User: for the kernel, the descriptor privilege level
(DPL) is 0, and the user DPL is 3. If you read Intel's documentation, you will see the exact meaning
Linux Kernel x86 protection does not involve much, so I will not discuss it too much.