Exercise 0: fill in existing experiments
This experiment relies on Experiment 1. Please fill in the code of Experiment 1 you did in the corresponding part with the comment "LAB1" in the code of this experiment. Tip: diff and patch tools can be used for semi-automatic merging, or some graphical comparison / merge tools can be used for manual merging, such as meld, diff/merge tool in eclipse, diff/merge tool in understand, etc.
In this part, use Meld Diff Viewer to Compare lab1 and lab2, and then Compare Right.
Exercise 1: implement the first fit continuous physical memory allocation algorithm (programming required)
When implementing the recycling function of the first fit memory allocation algorithm, the merging operation between free blocks with continuous addresses should be considered. Tip: when creating a free page block linked list, you need to sort according to the starting address of the free page block to form an orderly linked list. Default may be modified_ pmm. Default in C_ init,default_init_memmap,default_alloc_pages, default_free_pages and other related functions. Please check and understand default carefully_ pmm. Notes in C.
Please briefly describe your design and implementation process in the experimental report. Please answer the following questions:
- Does your first fit algorithm have room for further improvement
A: there should be room for improvement, because the current code is the sequential search of O(n) complexity, so you can consider doing some mapping.
The first fit algorithm starts from the head of the free partition chain until it finds a free partition that can meet its size requirements. Then, according to the size of the job, a piece of memory is drawn from the partition and allocated to the requester, and the remaining free partitions remain in the free partition chain.
Advantages: the algorithm tends to use the free area of the low address part of the memory, and the free area of the high address part is rarely used, thus retaining the large free area of the high address part. Obviously, it creates conditions for allocating large memory space for large jobs that arrive later.
Disadvantages: the low address part is continuously divided, leaving many difficult to use and small free areas. Each search starts from the low address part, which will increase the search overhead.
First, in memlayout It can be found in H
struct Page { int ref; // Reference count of page frames uint32_t flags; // Describes the status of the page frame unsigned int property; // The number of free page blocks is only set in the first block list_entry_t page_link; // free list link }; typedef struct { list_entry_t free_list; // the list header unsigned int nr_free; // # of free pages in this free list } free_area_t;
Then look at default in turn_ init,default_init_memmap,default_alloc_pages, default_free_pages these functions.
default_init
static void default_init(void) { list_init(&free_list); nr_free = 0; }
On the list List found in H_ Init Code:
static inline void list_init(list_entry_t *elm) { elm->prev = elm->next = elm; }
default_init_memmap
This function is used to initialize the free linked list. Initialize each free page, and then calculate the total number of free pages.
static void default_init_memmap(struct Page *base, size_t n) { assert(n > 0); struct Page *p = base; for (; p != base + n; p ++) { //Check whether it is a reserved page assert(PageReserved(p)); p->flags = p->property = 0; // Reference bit clear 0 set_page_ref(p, 0); } base->property = n; SetPageProperty(base); nr_free += n; list_add(&free_list, &(base->page_link)); // list_add <==> list_add_after }
In memlayout Find the relevant macro definition in H, continue to dig deeply, and find that it is an embedded assembly language.
#define PG_ Reserved 0 / / if the bit is 1, it has been reserved in the kernel and cannot be alloc/free_page, otherwise set to 0 #define PG_ Property 1 / / if the bit is 1, it indicates that the first page of the free page block can be used for alloc_pages, otherwise it cannot be used for alloc #define SetPageReserved(page) set_bit(PG_reserved, &((page)->flags)) #define ClearPageReserved(page) clear_bit(PG_reserved, &((page)->flags)) #define PageReserved(page) test_bit(PG_reserved, &((page)->flags)) #define SetPageProperty(page) set_bit(PG_property, &((page)->flags)) #define ClearPageProperty(page) clear_bit(PG_property, &((page)->flags)) #define PageProperty(page) test_bit(PG_property, &((page)->flags))
default_alloc_pages
static struct Page * default_alloc_pages(size_t n) { assert(n > 0); if (n > nr_free) { return NULL; } struct Page *page = NULL; list_entry_t *le = &free_list; while ((le = list_next(le)) != &free_list) { //Convert le to corresponding Page struct Page *p = le2page(le, page_link); if (p->property >= n) { page = p; break; } } if (page != NULL) { list_del(&(page->page_link)); if (page->property > n) { struct Page *p = page + n; p->property = page->property - n; list_add(&free_list, &(p->page_link)); } nr_free -= n; ClearPageProperty(page); } return page; }
default_free_pages
This is the original version, but it doesn't seem to be inserted in the right place. It needs to be modified to some extent.
static void default_free_pages(struct Page *base, size_t n) { assert(n > 0); struct Page *p = base; // First, make sure that the space to be released is legal for (; p != base + n; p ++) { assert(!PageReserved(p) && !PageProperty(p)); p->flags = 0; set_page_ref(p, 0); } base->property = n; SetPageProperty(base); //Find a place to insert list_entry_t *le = list_next(&free_list); while (le != &free_list) { p = le2page(le, page_link); le = list_next(le); if (base + base->property == p) { base->property += p->property; ClearPageProperty(p); list_del(&(p->page_link)); } else if (p + p->property == base) { p->property += base->property; ClearPageProperty(base); base = p; list_del(&(p->page_link)); } } nr_free += n; list_add(&free_list, &(base->page_link)); }
After modification:
static void default_free_pages(struct Page *base, size_t n) { assert(n > 0); struct Page *p = base; for (; p != base + n; p ++) { assert(!PageReserved(p) && !PageProperty(p)); p->flags = 0; set_page_ref(p, 0); } base->property = n; SetPageProperty(base); list_entry_t *le = list_next(&free_list); p = NULL; while (le != &free_list) { p = le2page(le, page_link); if (p > base) break; le = list_next(le); } //Empty linked list if (p == NULL) { list_add(&free_list, &(base->page_link)); } else if (p < base) {//Insert tail list_add(list_prev(le), &(base->page_link)); //Merge forward if (p + p->property == base) { p->property += base->property; ClearPageProperty(base); list_del(&(base->page_link)); } } else { //Insert middle list_entry_t *prev = list_prev(le); list_add_before(le, &(base->page_link)); //Backward merge if (base + base->property == p) { base->property += p->property; ClearPageProperty(p); list_del(le); } //Merge forward if (prev != &free_list) { p = le2page(prev, page_link); if (p + p->property == base) { node->property += base->property; ClearPageProperty(base); list_del(&(base->page_link)); } } } nr_free += n; }
Exercise 2: find the page table item corresponding to the virtual address (programming required)
By setting page table and corresponding page table entries, the corresponding relationship between virtual memory address and physical memory address can be established. Where get_pte function is an important step in setting page table items. This function finds the kernel virtual address of the secondary page table item corresponding to the virtual address. If the secondary page table item does not exist, a secondary page table containing this item will be allocated. This exercise needs to be completed_ PTE function in Kern / mm / PMM c. Realize its function. Please check and understand carefully_ Comments in the PTE function. get_ The call diagram of PTE function is as follows:
Figure 1 get_ Call graph of PTE function
Please briefly describe your design and implementation process in the experimental report. Please answer the following questions:
- Please describe the meaning of each component in Page Directory Entry and Page Table Entry and its potential use for ucore.
A: each page table entry (PTE) stores data by a 32-bit integer. Its structure is as follows
0 - Present: indicates whether the physical page pointed to by the current PTE resides in memory
1 - Writeable: indicates whether to allow reading and writing
2 - User: indicates the privilege level required for access to the page. That is, whether the User(ring 3) allows access
3 - pagewritethought: indicates whether to use the write through cache write policy
4 - pagecacheisable: indicates whether the page will not be cached
5 - Access: indicates whether the page has been accessed
6 - Dirty: indicates whether the page has been modified
7 - PageSize: indicates the size of the page
8 - MustBeZero: this bit must be left at 0
9-11 - Available: bits 9-11 are not used by kernel or interrupt, and can be reserved for OS.
12-31 - Offset: the last 20 bits of the destination address.
Because the target address takes 4k as the alignment standard, the lower 12 bits of the address are always 0, so the 12 bit space can be used to set the flag bit.
31-12 9-11 8 7 6 5 4 3 2 1 0 +--------------+-------+-----+----+---+---+-----+-----+---+---+---+ | Offset | Avail | MBZ | PS | D | A | PCD | PWT | U | W | P | +--------------+-------+-----+----+---+---+-----+-----+---+---+---+
- If a page access exception occurs when accessing memory during ucore execution, what should the hardware do?
Answer: ① the address that will cause the page access exception will be saved in the cr2 register; ② Set the error code; ③ Trigger Page Fault to replace the data in external memory into memory; ④ Perform context switching, exit the interrupt, and return to the state before the interrupt.
Here, PTE refers to the middle 10 bits, Page Table Entry, PDE refers to the top 10 bits, and Page Directory Entry
la is a linear address, 32 bits, from which PDE and PTE can be extracted
KADDR(pa): returns the virtual address corresponding to a physical address
page2pa(page): get the physical address of the page managed by page
In MMU It can be found in H
#define PDX(la) ((((uintptr_t)(la)) >> PDXSHIFT) & 0x3FF) #define PTX(la) ((((uintptr_t)(la)) >> PTXSHIFT) & 0x3FF)
Where the value of PDXSHIFT is 22, shift it to the right by 22 bits, and then perform and operation with 0x3FF to obtain Directory Entry
The value bit of PTXSHIFT is 12 bits, which is shifted to the right by 12 bits, and then and 0x3FF are used to obtain Table Entry
pte_t * get_pte(pde_t *pgdir, uintptr_t la, bool create) { //Try to get PDE first pde_t *pdep = &pgdir[PDX(la)]; //If the acquisition fails if (!(*pdep & PTE_P)) { struct Page *page; //Judge whether to create a secondary page table according to create if (!create || (page = alloc_page()) == NULL) { return NULL; } //Quote plus one set_page_ref(page, 1); //Get physical address uintptr_t pa = page2pa(page); //Use memset to set all the virtual addresses of the new page table to 0, because the virtual addresses represented by this page are not mapped. memset(KADDR(pa), 0, PGSIZE); //Get the page directory entry and set the upper PTE_U,PTE_W and PTE_P. The software representing the user status can read the physical memory page content of the corresponding address, the physical memory page content can be written, and the physical memory page exists. *pdep = pa | PTE_U | PTE_W | PTE_P; } //Return corresponding address return &((pte_t *)KADDR(PDE_ADDR(*pdep)))[PTX(la)]; }
Exercise 3: release the page where a virtual address is located and unmap the corresponding secondary page table entry (programming required)
When releasing a physical memory page containing a virtual address, the management data structure page corresponding to the physical memory page needs to be cleared to make the physical memory page idle; In addition, the secondary page table entries representing the corresponding relationship between virtual address and physical address need to be cleared. Please check and understand page carefully_ remove_ Comments in the PTE function. Therefore, it is necessary to fill in Kern / mm / PMM Page in C_ remove_ PTE function. page_ remove_ The call diagram of PTE function is as follows:
Figure 2 page_ remove_ Call graph of PTE function
Please briefly describe your design and implementation process in the experimental report. Please answer the following questions:
- Is there a corresponding relationship between each item of the global variable (actually an array) of the data structure Page and the Page directory item and Page table item in the Page table? If so, what is the corresponding relationship?
A: in fact, each Page item records the information of a physical Page, while each Page directory item records the information of a Page table, and each Page table item records the information of a physical Page. It can be said that the physical Page address (i.e. a Page table) saved by the Page directory item and the physical Page address saved by the Page table item correspond to a Page in the Page array. - If you want the virtual address to be equal to the physical address, how do you modify lab2 to complete this? It is encouraged to complete this problem through programming
A: virt addr = linear addr = phy addr + 0xC0000000. You can find that KERNBASE actually corresponds to 0xC0000000, so you can modify KERNBASE to 0 in memlayout H can be found in.
#define KADDR(pa) ({ \ uintptr_t __m_pa = (pa); \ size_t __m_ppn = PPN(__m_pa); \ if (__m_ppn >= npage) { \ panic("KADDR called with invalid pa %08lx", __m_pa); \ } \ (void *) (__m_pa + KERNBASE); \ })
At PMM Found in H
page_ref_dec(struct Page *page) { page->ref -= 1; return page->ref; }
static inline void page_remove_pte(pde_t *pgdir, uintptr_t la, pte_t *ptep) { if (*ptep & PTE_P) { //Confirm that the page exists //Get physical address struct Page *page = pte2page(*ptep); //If the number of references becomes 0, the page is released if (page_ref_dec(page) == 0) { free_page(page); } //Unmap secondary page table *ptep = 0; tlb_invalidate(pgdir, la); } } //Invalidates the TLB Entry, but only if the page table being edited is the page table currently in use by the processor. void tlb_invalidate(pde_t *pgdir, uintptr_t la) { if (rcr3() == PADDR(pgdir)) { invlpg((void *)la); } }