- BIOS boot to real mode
- GDT and protection mode
- On virtual memory
- Load and enter the kernel
- Display and print
- Global descriptor table GDT
- Interrupt processing
- Virtual memory perfection
- Implement heap and malloc
- First kernel thread
- Multithread switching
- Lock and multithreading synchronization
- Enter user status
- Process implementation
- system call
- Simple file system
- Load executable
- Keyboard drive
- Run shell
Kernel virtual memory overview
NextGDT and protection mode, this article will be the focus of loader. First, we need to create virtual memory in kernel space. If you are not familiar with the principle of virtual memory, please be sure to learn by yourself first. Here is an examplefileFor reference.
So far, we have always operated on physical memory, to be exact
1MBLow address space operation, all this is very simple and direct. However, the loader is about to prepare for loading the kernel, and we need to do it in a broader context
4GBPlan data and code on virtual memory space.
Following the Linux system, we will use
3GBThe above high address space is used as the kernel space to carry out all subsequent work. For example, basically, the current physical low address 1MB will be mapped to the virtual address
0 ~ 1MBAnd more than 3gb of space
0xC0000000 ~ (0xC0000000 + 1MB)Division:
After entering the kernel, access to low 1MB space will be used
0xC0000000 ~ (0xC0000000 + 1MB)The virtual address mainly includes the stack currently used and the memory mapping corresponding to the display:
So the video memory base address will be from the virtual address
0xC00B8000At first, however, there is no need to delve into it at present. Later, it will be explained in detail in the article of display and printing.
In addition to the basic low 1MB Memory space, the loader needs to further
0xC0000000Opening up territory in the above virtual space mainly includes two parts:
- The page directory used by the kernel（
page directory）And page table（
- Reading kernel binary image and loading code and data;
The following shows the to be built in the whole loader stage
virtual-to-physicalMemory mapping diagram:
This figure is the most important global figure in this article. The second line is the “distorted” scale of the first line. We will reduce the user space below 3gb. At present, we only focus on the kernel space above 3gb (BOLD box). Because it is a virtual address space, our space division can be more casual and “luxurious”. We take 4MB as the unit, from
0xC0000000Start cutting in the virtual space and divide into the following areas:
- The first 4MB is reserved, in which the lower 1MB 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 of the kernel
- The third 4MB (green), i.e. from
0xC0800000Start as loading and storage
kernelCode and data space, that is to say
kernelAddress from there;
Here I would like to say that there is no fixed way to implement an OS. The above is only my personal implementation. In fact, the memory planning is very flexible, just like the name of this project
scrollSimilarly, the memory is a picture volume, and the CPU is a brush. It can play freely on the premise of following certain rules.
Let’s start with the orange part, the kernel
page tablesEstablishment of.
Create kernel virtual memory
Before we begin this paragraph, let’s review the page directory（
page directory）And page table（
page table）Relevant principles of.
There are some key numbers to remember:
page）The size of is 4096;
- Page directory entry
pde (page directory entry)And page table entries
pte (page table entry), essentially the same structure, with a size of
page direcotryThere are 1024 items in total, pointing to a total of 1024
page table, total
page tableThere are 1024 items, pointing to 1024
1024 * 4KB = 4MBVirtual space;
- So each
OK, let’s start to build the page table of the kernel space. Give the code link according to the Convention: the relevant code in this part is from the functionsetup_pageStart, for your reference.
From here on, following the terminology convention, I will use the virtual page
pageExpression, and the physical page will use
Create page directory
First, we need to come up with one
frame, used as
page directory。 Back to the diagram of physical memory distribution, at present, the parts below 1MB have been occupied, and the parts we can use are from 1MB, i.e
0x100000 + 4KB, i.e
0x100000Second after2Frame as
page directoryOf course, this is entirely a personal choice;
0x100000After the first frame, I choose it as the first one
Again, this is my personal choice; The choice of frames is very free. You can use them as long as they are not occupied. Of course, you should remember which frames you have used, be reasonable and compact, and plan and use them as “beautiful” as possible.
Map 1MB low memory space
It is worth noting that the 0 and 768
pdeAll point to the same one
page table, we will use this page table to map
0 ～ 1MBLow memory, that is, 1MB of 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 will be idle. However, it doesn’t matter. If it is idle, it will be idle. Anyway, this is virtual space.
The following figure shows how low 1MB Memory is mapped in the page table:
pdeThe lowest 4MB of the virtual space is managed. The starting 1MB is mapped to the lower 1MB of physical. This is a one-to-one mapping. The virtual address is completely equal to the physical address. In this way, after opening paging, our access to 1MB of low memory will use the virtual address, which is the same as the previous physical address access, No change will be perceived.
0xC0000000That is, the first 4MB space from 3gb is returned to the first figure at the beginning of this article, and the initial 1MB is also mapped to the low 1MB Memory. After opening paging and entering the kernel, we will use
0xC0000000 ～0xC0000000 + 1MBSpace access low 1MB Memory:
Map the page directory and the page tables themselves
Here are the key points and difficulties of this section. We know
page tablesAll the pages pointed to are physical pages. Once the paging mode is turned on, all memory accesses will be through the virtual address, and the physical address can no longer be operated directly. So the question is, how do we access and modify it
page tablesWhat about itself?
One way, of course, is to turn off paging when necessary and directly access the physical address. The previously recommended tutorialJamesM’s kernel development tutorialsThis is done in many places, but it is not a good practice for the following reasons:
- After entering the complex kernel, the code execution will involve a lot of memory access such as stack, heap and other global variables. These are all the virtual addresses of the kernel space. If paging is suddenly closed at this time, their access will not be possible. You must be very careful to arrange your code’s access to memory, otherwise there will be unpredictable consequences, but this is actually very difficult to do;
- Once multithreading is enabled, 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 the virtual address. Obviously, the result is disastrous;
A more reasonable approach is that we will
page tablesItself is also mapped to virtual space so that they can be accessed like other normal memory. In essence
page tablesIt is nothing more than some pages, which can be treated equally with other memory access. The question is, how should this mapping be established? Look at the following figure:
page directoryThe frame itself. such
page direcotryIn fact, it also acts as a
page table, it manages 1024 page tables themselves, a total of 4MB. One of the 1024 page tables is
page direcotryIt itself.
Is it a little windy? In other words, because
page directoryIt itself, so
0xC0400000 ～ 0xC0800000This 4MB virtual space is now mapped to 1024 pieces
page tablesAnd even better, their virtual addresses are completely continuous and closely arranged in this 4MB space.
Therefore, the above problem has been solved. The virtual address space corresponding to page tables is:
0xC0400000 ~ 0xC0800000
This is the second in 4GB space
769Four 4MB spaces (1024 4MB spaces in total, forming 4GB).
And we also got
page directoryIts own virtual address is:
0xC0400000 ～ 0xC0800000This is the second in 4MB space
769A page, isn’t it clever:)
The core idea here is,
page directoryIn fact, it is a special one in essence
page table, it and others
page tableSimilarly, they manage 4MB of space.
If you still feel a little windy, you might as well verify it in reverse. Start from the virtual address given above and deduce where the physical address actually points to. I think we can sort out the logic soon.
If you think further, you will find that this is not the only way to achieve it. You have no choice
pdeInstead, use other virtual spaces to map page tables, such as
pdeIt’s also OK. In this way, the virtual space corresponding to all page tables becomes
0xC0800000 ～ 0xC0C00000。 use
pdeIt’s just my personal choice because it’s
0xC0000000After the second 4MB space, the use of virtual space can be more compact and tidy.
Mapping other areas of kernel space
So far, PDE 768 and 769 have been used, i.e
0xC0000000 ~ 0xC0400000and
0xC0400000 ~ 0xC0800000These two 4MB spaces have been requisitioned. The rest
pde ～ pdeCorresponding 254
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 establish the kernel space (3gb ~ 4GB)
page tables, manage this 1GB of space.
We’ll the one at the beginning of this chapter
virtual-to-physicalThe orange part in the memory mapping diagram is extracted and enlarged to show the memory distribution of 256 page tables in the kernel:
Note that we only allocate the kernel space, that is, more than 3gb of page tables, a total of 256, covering an area of 1MB. They also map
0xC0400000 ~ 0xC0800000The last quarter of the space is
0xC0700000 ~ 0xC0800000； Page tables are not allocated for user space below 3gb at this time, because we do not use it at present.
These 256 kernel page tables (one of which is
page directoryPage tables itself) is the core page tables when we write the kernel, and it is established in the page directory
pde ~ pdeAll 256 table entries point to these page tables.
In fact, except for the first two page tables, the last 254 are empty and have not been used. We just arranged a frame for them. A full 1MB of physical memory is used here, which seems a bit extravagant. After all, the total physical memory in the project configuration is only 32 MB (see
bochsrc.txtOf course, today’s computer memory is far more than 32 MB, which is no longer a problem). A very important reason for this is that the 256 kernel page tables will be used by all processes（
process）Sharing, that is, for the user process, the space below 3gb is isolated, while the space of the kernel above 3gb is shared, which is also a matter of course. Otherwise, multiple kernels will run independently in memory.
forkCreate a new process, its
page directoryThe last quarter of 768 ~ 1023 items will be directly copied to the kernel
page directory768 ~ 1023 items of the total, pointing to these 256
kernel page tables。 So we asked for 256
framesIt is fixed from the beginning and will not change later, so as to achieve the effect of sharing all processes.
page tablesWhen everything is ready, you can open it
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 setting
CR3Register so that it points to
page directoryFrame (note the physical address) and open it
CR0Paging bit switch on register.
At this point, the loader phase ends with the initialization of the kernel virtual memory. This section of code is not long, the core is justsetup_pageThis is a function, but the principle behind it is very profound and complex. Initially established in the loader stage
virtual memoryThis lays a good foundation for memory management after entering the kernel.
At the current stage, all our virtual to physical memory allocation and mapping are planned in advance, pre allocated and reused, and each physical frame is manually arranged. This is actually not fully played out
virtual memoryThe role of. After entering the kernel later, we will further improve it
virtual memoryRelated work, which will include missing page exceptions
(page fault)Processing, process
page directoryReplication, etc.
virtual memoryThe processing of is the bottom core work throughout the implementation and operation of the kernel, which must be absolutely correct and stable. Once an error occurs, the system will immediately make all kinds of unpredictable strange errors or even crash, and debugging is very difficult.
In the next article, we will load the real
kernelGo to memory and go to the kernel to start executing code, which will be the last level before entering the kernel.