Series catalog
- Preface
- Preparation work
- BIOS boot to real mode
- GDT and protected mode
- A Preliminary Study of Virtual Memory
- load and enter the kernel
- display and print
- Global Descriptor Table GDT
- Interrupt handling
- virtual memory perfect
- implements heap and malloc
- First kernel thread
- multi-thread switching
- lock and multi-thread synchronization
- enter user mode
- process
- system call
- Simple file system
- Load executable program
- keyboard driver
- run shell
Overview of kernel virtual memory
article 160fecd90b9315 GDT and protected mode , this article will be the focus of the loader. First, we need to create virtual memory in the kernel space. If you are not familiar with the principle of virtual memory, please be sure to first. Here you can provide a 160fecd90b9318 document for reference.
So far we have always operated on physical memory, to be precise in 1MB
, all of which are simple and straightforward. But then the loader is about to prepare for loading the kernel, we need to plan the data and code on the 4GB
3GB
Linux system, we will use the high address space above 060fecd90b9357 as the kernel space for all subsequent work. For example, the most basic, the current physical low address 1MB will be mapped to the virtual address 0 ~ 1MB
and the space above 3GB 0xC0000000 ~ (0xC0000000 + 1MB)
:
After entering the kernel, access to the lower 1MB space will use the 0xC0000000 ~ (0xC0000000 + 1MB)
virtual address, which mainly includes the currently used stack and the memory map corresponding to the display:
So the video memory base address will 0xC00B8000
, but you don't need to go into it for now, it will be explained in detail in the display and printing article later.
In addition to the most basic low 1MB memory space, loader also needs to further expand in 0xC0000000
, which mainly includes two parts:
- The page directory (
page directory
) and page table (page table
) used by the kernel; - Reading of kernel binary image, and loading of code and data;
virtual-to-physical
memory map to be built during the entire loader stage:
This picture is the most important overall picture in this article. The second line is the "distorted" scale icon in the first line. We have reduced the user space below 3GB and displayed it. The current focus is only on the kernel space above 3GB (coarse Box part). Since it is a virtual address space, our space division can be more arbitrary and "luxury", we use 4MB as a unit, starting from 0xC0000000
in the virtual space to divide the following areas:
- The first 4MB is reserved, of which the lower 1MB of space is mapped to the lower 1MB of the physical address, which has been explained above;
- The second 4MB (orange) is used to map all
page tables
kernel; - The third 4MB (green), starting from
0xC0800000
, is used askernel
code and data, which means thatkernel
starts addressing there;
I want to say here that there is no fixed way to implement an OS, the above is just my personal way of implementation. In fact, the memory planning is very flexible. Just like the name of this project, scroll
, the memory is a picture scroll, and the CPU is a paintbrush. Under the premise of following certain rules, you can play freely.
Below we first start the orange part, namely the establishment of the page directory
and page tables
Create kernel virtual memory
Before starting this paragraph, let's review the related principles of the page directory
) and the page table ( page table
There are some key figures to remember:
- The size of the page (
page
- Page directory entry
pde (page directory entry)
and page table entrypte (page table entry)
essentially the same structure, with a size of4 bytes
; page direcotry
a total of 1024 items, pointing to a total of 1024page table
, a total of4MB
;- Each
page table
has 1024 items, pointing to 1024pages
, which manages the virtual space1024 * 4KB = 4MB
- So each
pde
manages the virtual space4MB
Well, let's start to build the page table of the kernel space. The code link is given according to the convention: the related code of this part starts from the function setup_page for your reference.
From here on, following terminology conventions, I will use page
frame
for physical pages.
Create page directory
First of all, we need to take out a frame
and use it as page directory
. 0x100000
back to the picture of physical memory distribution, the part below 1MB is currently occupied, and the part we can use starts from 1MB, which is 060fecd90b9954.
I chose 0x100000 + 4KB
, namely 0x100000
after the first 2 a frame as page directory
, of course, this is entirely a personal choice; 0x100000
after the first one frame I chose it as the first page table
:
Again, this is my personal choice; the choice of frame is very free, as long as it is not occupied, you can use it, of course, you have to remember which frames you have used, reasonably compact and as "beautiful" as possible Plan to use.
Map 1MB low memory space
It is worth noting that the 0th and pde
points to the same page table
, this page table we will use it to map the 0 ~ 1MB
low memory, which is the 1MB memory space we are currently in. Of course, this page table can manage 4MB of space, we only mapped 1MB of it, and the remaining 3MB of virtual space is idle, but it doesn't matter. If it is idle, it will be idle. Anyway, this is virtual space.
The following figure shows how the lower 1MB of memory is mapped in the page table:
pde[0]
manages the lowest 4MB of virtual space, of which the first 1MB is mapped to the lower 1MB of the physical. This is a one-to-one mapping. The virtual address is exactly equal to the physical address. In this way, after opening paging, we have a low 1MB The memory access is changed to use the virtual address, which is the same as the previous physical address access, and no changes will be perceived.
pde[768]
manages 0xC0000000
, the first 4MB space from 3GB. Going back to the first picture at the beginning of this article, the first 1MB is also mapped to the lower 1MB of memory. After opening paging and entering the kernel, we will use 0xC0000000 ~0xC0000000 + 1MB
to access the lower 1MB memory:
Map page directory and page tables themselves
Here are the key points and difficulties of this section. We know that page directory
and page tables
point to physical pages, and once the paging mode is turned on, all of our future memory accesses will pass through the virtual address, and we can no longer directly manipulate the physical address. So the question is, how do we access and modify page directory
and page tables
itself?
One way of course is to turn off paging when needed and directly access the physical address. The previously recommended tutorial JamesM's kernel development tutorials is done in many places, but this is not a good practice for the following reasons :
- After entering the complex kernel, the execution of the code will involve a large number of memory accesses such as stack and heap, and other global variables. These are all virtual addresses in the kernel space. If paging is suddenly turned off at this time, their access will not be possible. You must arrange your code's access to memory very carefully, otherwise there will be unpredictable consequences, but this is actually very difficult to do;
- Once multithreading is turned on, if an interrupt occurs when paging is turned off, the CPU will perform some automatic stack operations and interrupt processing, all of which are operations on virtual addresses. Obviously, the results are also disastrous;
A more reasonable approach is to page directory
and page tables
to the virtual space, so that they can be accessed like other normal memory. In essence, page directory
and page tables
nothing more than pages, which can be treated the same as other memory accesses. The question is, how should this mapping be established? Look at the picture below:
We will pde[769]
pointing page directory
the frame itself. In this way, page direcotry
actually also serves as a page table
at the same time, it manages exactly 1024 page tables themselves, a total of 4MB. Of these 1024 page tables, one of them is page direcotry
itself.
Is it a bit convoluted? In other words, since pde[769]
pointed page directory
its own, so 0xC0400000 ~ 0xC0800000
this 4MB of virtual space, is now mapped to the 1024 page tables
on, but better is that their virtual address is completely continuous, tightly arranged in this 4MB space inside.
As a result, the above problem has been solved, and the virtual address space corresponding to page tables is:
0xC0400000 ~ 0xC0800000
769
4MB space in the 4GB space (a total of 1024 4MB spaces make up 4GB).
And we also got page directory
its own virtual address as:
0xC0701000
That 0xC0400000 ~ 0xC0800000
this space 4MB of 769
a page, is not very clever :)
The core idea here is that, page directory
in fact, is essentially a special page table
, it and other page table
as manages a space of 4MB.
If it still feels a bit convoluted, you might as well verify it in reverse. Starting from the virtual address given above, deduce where the physical address actually points to. I think the logic will be sorted out soon.
If you think further, you will find that this is not the only way to achieve it. You can not choose pde[769]
, using other virtual space mapped page tables, for example pde[770]
may be, so that all page tables corresponding to the virtual space becomes 0xC0800000 ~ 0xC0C00000
. Using pde[769]
is only my personal choice, because it is 0xC0000000
. With this arrangement, the use of virtual space can be more compact and tidy.
Map other areas of the kernel space
So far, pde 768 and 769 have been used, that is, the two 4MB spaces of 0xC0000000 ~ 0xC0400000
and 0xC0400000 ~ 0xC0800000
pde[770] ~ pde[1023]
corresponding to the remaining page tables
, we arrange frames for them in turn. In this way, we finally requisitioned 256 pages & frames, a total of 1MB of memory (virtual & physical), to create a kernel space (3GB ~ 4GB) of page tables
to manage this 1GB of space.
virtual-to-physical
memory map at the beginning of this chapter to show the memory distribution of the 256 page tables of the kernel:
Note that we only allocated page tables with a kernel space of more than 3GB, a total of 256 sheets, occupying 1MB, and they map the last quarter of the 0xC0400000 ~ 0xC0800000
0xC0700000 ~ 0xC0800000
; and the user space below 3GB is not allocated at this time. tables, because we have not used them currently.
These 256 kernel page tables (one of which is page directory
itself) is the most core page tables during our kernel compilation, and pde[768] ~ pde[1023]
are created in the page directory, pointing to these page tables.
In fact, except for the first two page tables, the last 254 are currently empty and are not used. We just arranged frames for them. A full 1MB of physical memory is used here, which looks a bit extravagant. After all, the total physical memory in this project configuration is only 32 MB (see bochsrc.txt
, of course, the current computer memory is far more than 32 MB, this is no longer a problem). There is a very important reason for this, that is, these 256 kernel page tables will be process
), which means that for user processes, the space below 3GB is isolated, and the kernel above 3GB The space is shared, which is also a matter of course, otherwise there will be multiple kernels running independently in memory.
Each time fork
creates a new process, page directory
, that is, 768-1023 items, will directly copy the page directory
items of the kernel 060fecd90ba001, and point to these 256 kernel page tables
. Therefore, we require the 256 page tables
corresponding to frames
to be fixed from the beginning, and will not change later, in order to achieve the shared effect of all processes.
Open paging
page tables
is ready, you can open paging
:
enable_page:
sgdt [gdt_ptr]
; move the video segment to > 0xC0000000
mov ebx, [gdt_ptr + 2]
or dword [ebx + 0x18 + 4], 0xC0000000
; move gdt to > 0xC0000000
add dword [gdt_ptr + 2], 0xC0000000
; move stack to > 0xC0000000
mov eax, [esp]
add esp, 0xc0000000
mov [esp], eax
; set page directory address to cr3 register
mov eax, PAGE_DIR_PHYSICAL_ADDR
mov cr3, eax
; enable paging on cr0 register
mov eax, cr0
or eax, 0x80000000
mov cr0, eax
The most important thing here is to set the CR3
register to point to page directory
(note the physical address), and then turn on the paging bit switch CR0
Summarize
At this point, the part about kernel virtual memory initialization in the loader phase is over. The code of this section is not long, the core is only setup_page this function, but the principle behind it is very profound and complicated. In the loader stage virtual memory
is initially established, which lays a good foundation for the memory management after entering the kernel.
At the current stage, all of our virtual-to-physical memory allocation and mapping are planned in advance, allocated before use, and each physical frame is manually arranged. In fact, this did not fully play the role of virtual memory
. After entering the kernel later, we will further improve the work related to virtual memory
, which will include the processing of (page fault)
, the process of copying page directory
virtual memory
is the core work that runs through the implementation and operation of the kernel, and must be absolutely correct and stable. Once an error occurs, the system will immediately appear various unpredictable strange errors or even crashes, and debugging is very difficult.
In the next article, we will load the real kernel
into the memory and go to the kernel to start the code execution. This will be the last level before entering the kernel.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。