Tsinghua operating system experiment ucore_lab3

Posted by xentrix on Sun, 12 Dec 2021 04:35:28 +0100

lab3

Exercise 0: fill in existing experiments

Files to be modified:

default_pmm.c:

static struct Page *default_alloc_pages(size_t n) {
    assert(n > 0);
    if (n > nr_free) {  //If the total size of all free pages is not enough, NULL is returned directly
        return NULL;
    }
    list_entry_t *le, *len;
    le = &free_list;    //Start with the header pointer of the free block linked list
    while((le=list_next(le)) != &free_list) {//Look down in turn until you return to the head pointer, that is, you have traversed it once
      struct Page *p = le2page(le, page_link);//Structure for converting addresses to pages
      if(p->property >= n){ //Since it is first fit, the first block greater than N encountered can be selected
        int i;
        for(i=0;i<n;i++){//Recursively initializes each page structure in the selected free block linked list
          len = list_next(le);
          struct Page *pp = le2page(le, page_link);
          SetPageReserved(pp);
          ClearPageProperty(pp);
          list_del(le);//Delete the two-way linked list pointer from the free page linked list
          le = len;
        }
        if(p->property>n){
          (le2page(le,page_link))->property = p->property - n;//If the first consecutive block selected is greater than N, only the block with size n is taken
        }
        ClearPageProperty(p);
        SetPageReserved(p);
        nr_free -= n;//Number of currently free pages minus n
        return p;
      }
    }
    return NULL;//If there is no consecutive free page block greater than or equal to n, null is returned
}
//#################################################################################
static struct Page *default_alloc_pages(size_t n) {
    assert(n > 0);
    if (n > nr_free) {  //If the total size of all free pages is not enough, NULL is returned directly
        return NULL;
    }
    list_entry_t *le, *len;
    le = &free_list;    //Start with the header pointer of the free block linked list
    while((le=list_next(le)) != &free_list) {//Look down in turn until you return to the head pointer, that is, you have traversed it once
      struct Page *p = le2page(le, page_link);//Structure for converting addresses to pages
      if(p->property >= n){ //Since it is first fit, the first block greater than N encountered can be selected
        int i;
        for(i=0;i<n;i++){//Recursively initializes each page structure in the selected free block linked list
          len = list_next(le);
          struct Page *pp = le2page(le, page_link);
          SetPageReserved(pp);
          ClearPageProperty(pp);
          list_del(le);//Delete the two-way linked list pointer from the free page linked list
          le = len;
        }
        if(p->property>n){
          (le2page(le,page_link))->property = p->property - n;//If the first consecutive block selected is greater than N, only the block with size n is taken
        }
        ClearPageProperty(p);
        SetPageReserved(p);
        nr_free -= n;//Number of currently free pages minus n
        return p;
      }
    }
    return NULL;//If there is no consecutive free page block greater than or equal to n, null is returned
}
//#############################################################################
static void default_free_pages(struct Page *base, size_t n) {
    assert(n > 0);
    assert(PageReserved(base));
    list_entry_t *le = &free_list;
    struct Page *p;
    //Find base location
    while((le=list_next(le)) != &free_list){
        p = le2page(le, page_link);
        if(p>base)
            break;
    }
    //Insert page into free page
    for(p = base; p<base+n; p++){
        p->flags = 0;
        set_page_ref(p, 0);
        ClearPageReserved(p);
        ClearPageProperty(p);
        list_add_before(le, &(p->page_link));
    }
    base->property = n;
    SetPageProperty(base);
    //High order address merging
    p = le2page(le,page_link) ;
    if(base+n == p){
        base->property += p->property;
        p->property = 0;
    }
    //Low order address merging
    le = list_prev(&(base->page_link));
    p = le2page(le, page_link);
    if(p==base-1){
        while (le != &free_list) {
            if (p->property) {
                p->property += base->property;
                base->property = 0;
                break;
            }
            le = list_prev(le);
            p = le2page(le, page_link);
        }
    }
    nr_free += n;
    return ;
}
//####################################################################

pmm.c:

static inline void page_remove_pte(pde_t *pgdir, uintptr_t la, pte_t *ptep)
{
 if (*ptep & PTE_P) {         //If page table entry exists               
        struct Page *page = pte2page(*ptep);    	//Page table entry found
        page_ref_dec(page);                     
        if (page->ref == 0) {    //If only the current process references
            free_page(page);       //Release page             
        }
        *ptep = 0;           	//Set page directory entry to 0                  
        tlb_invalidate(pgdir, la);     //The modified page table is the page table being used by the process, making it invalid 
    }
    }

kdebug.c:

void print_stackframe(void) {
uint32_t t_ebp = read_ebp();
	uint32_t t_eip = read_eip();
	int i,j;
	for(i = 0;i< STACKFRAME_DEPTH && t_ebp!=0;i++)
	{
		cprintf("ebp=%08x,eip=%08x,args:",t_ebp,t_eip);
		uint32_t *args = (uint32_t *)t_ebp +2;
		for(j = 0;j<4;j++)
		{
			cprintf("0x%08x",args[j]);
		}
		cprintf("\n");
		print_debuginfo(t_eip-1);
		t_eip = ((uint32_t *)t_ebp)[1];
		t_ebp = ((uint32_t *)t_ebp)[0];
	}
	}

trap.c:

ticks ++; //Each clock signal will increase the variable ticks by 1
        if (ticks==TICK_NUM) {//TICK_NUM has been predefined to 100, and print is called every 100_ The ticks() function prints
            ticks=0;
            print_ticks();
        } ticks ++; //Each clock signal will increase the variable ticks by 1
        if (ticks==TICK_NUM) {//TICK_NUM has been predefined to 100, and print is called every 100_ The ticks() function prints
            ticks=0;
            print_ticks();
        }
}
//###############################################################
void idt_init(void) {
extern uintptr_t __vectors[];
     int i;
     //Initialize idt
     for(i=0;i<256;i++)
     {
         SETGATE(idt[i],0,GD_KTEXT,__vectors[i],DPL_KERNEL);
     }
     SETGATE(idt[T_SWITCH_TOK],0,GD_KTEXT,__vectors[T_SWITCH_TOK],DPL_USER);
     SETGATE(idt[T_SWITCH_TOU],0,GD_KTEXT,__vectors[T_SWITCH_TOU],DPL_KERNEL);
     lidt(&idt_pd);

Exercise 1: Map physical pages to unmapped addresses

requirement:

Complete do_pgfault (mm/vmm.c) function to map physical pages to unmapped addresses. When setting access permissions, you need to refer to the permissions of the VMA where the page is located. At the same time, you need to operate the page table specified by the memory control structure rather than the page table of the kernel when mapping physical pages.

1. VMA analysis:

    struct vma_struct {  
        // the set of vma using the same PDT  
        struct mm_struct *vm_mm;  
        uintptr_t vm_start;      // start addr of vma  
        uintptr_t vm_end;      // end addr of vma  
        uint32_t vm_flags;     // flags of vma  
        //linear list link which sorted by start addr of vma  
        list_entry_t list_link;  
    }; 
/*
vm_start And VM_ End describes a reasonable address space range (that is, strictly ensure the relationship between vm_start < vm_end);
list_link Is a two-way linked list, in order from small to large, using vma_ The virtual memory space represented by struct is linked, and these linked Vmas are also required_ Structs should be disjoint, that is, there is no intersection in the address space between Vmas;
vm_flags Represents the properties of this virtual memory space. The current properties include
*/

#define VM_READ 0x00000001 / / read only
#define VM_WRITE 0x00000002 / / read / write
#define VM_EXEC 0x00000004 / / executable

/*vm_mm Is a pointer. If vma describes and manages a series of virtual memory block structures, mm is used to describe which application uses these Vmas, which are connected through the member mm in vma. Mm also has five members:*/
struct mm_struct {  
        // linear list link which sorted by start addr of vma  
        list_entry_t mmap_list;  
        // current accessed vma, used for speed purpose  
        struct vma_struct *mmap_cache;  
        pde_t *pgdir; // the PDT of these vma  
        int map_count; // the count of these vma  
        void *sm_priv; // the private data for swap manager  
    };  
/* mmap_list It is a bidirectional chain header that links all virtual memory spaces belonging to the same page directory table
mmap_cache Is a pointer to the virtual memory space currently in use
pgdir It points to mm_ Page table maintained by struct data structure
map_count Record MMAP_ VMA linked in list_ Number of structs
sm_priv Points to the chain header used to link and record page access*/

2. Function implementation:

Implementation idea & & function analysis:

The three incoming parameters are: application virtual storage header mm and error code error_code and the specific virtual address addr.

The function is used to handle and report errors when page access errors may occur.

Judge the virtual address. If the range of the virtual address exceeds the limit or the virtual address cannot be found, it can be said that the address is illegal and an illegal access is made, then an error can be reported directly.

Judge the permission of the target access page, such as writing to a read-only page or reading an unreadable page, you can directly report an error at this time. The lower 2 bits of the error code are: P flag (bit 0) lowest bit: indicates whether the current error is caused by the absence of a page (0) or the violation of access rights (1). W / R flag (bit 1): indicates whether the current error is caused by read operation (0) or write operation (1).

If the above legitimacy judgment can be successfully passed, the virtual memory access is considered legal. At this time, the reason for the abnormal page access is that the legal virtual page has no mapping corresponding to the physical page. Therefore, the next step is to establish this mapping. Query whether there is a corresponding secondary page table through the primary page table and virtual address pointed to by the current application mm_ The PTE function will search. If not, get_ The PTE function creates a new secondary page table. If the creation fails, a NULL will be returned and an error will be reported.

If it is the newly created secondary page table above, * ptep will be 0, which means that the page table is empty. At this time, pgdir is called_ alloc_ Page, initialize it and establish a mapping relationship. If it is not 0, it means that there is a corresponding mapping and the mapping needs to be replaced.

int do_pgfault(struct mm_struct *mm, uint32_t error_code, uintptr_t addr) {
    int ret = -E_INVAL; 
 //#############Judge the virtual address##################
    //Try to find a vma that includes addr
    struct vma_struct *vma = find_vma(mm, addr);
    pgfault_num++;
    //Determine whether addr is in the range of vma
    if (vma == NULL || vma->vm_start > addr) {
        cprintf("not valid addr %x, and  can not find it in vma\n", addr);
        goto failed;
    }
  //############Permission judgment of target access page###############
    switch (error_code & 3) {
    default:
            /* error code flag : default is 3 ( W/R=1, P=1): write, present */
    case 2: /* error code flag : (W/R=1, P=0): write, not present */
        if (!(vma->vm_flags & VM_WRITE)) {
            cprintf("do_pgfault failed: error code flag = write AND not present, but the addr's vma cannot write\n");
            goto failed;
        }
        break;
    case 1: /* error code flag : (W/R=0, P=1): read, present */
        cprintf("do_pgfault failed: error code flag = read AND present\n");
        goto failed;
	//If it cannot be read, an error is reported directly
    case 0: /* error code flag : (W/R=0,read, not present */
 
        if (!(vma->vm_flags & (VM_READ | VM_EXEC))) {
            cprintf("do_pgfault failed: error code flag = read AND not present, but the addr's vma cannot read or exec\n");
            goto failed;
        }
    }
    uint32_t perm = PTE_U;//prem: an intermediate variable that grants permissions to a physical page
    if (vma->vm_flags & VM_WRITE) {
        perm |= PTE_W;
    }
    addr = ROUNDDOWN(addr, PGSIZE);
    ret = -E_NO_MEM;
    pte_t *ptep=NULL;
/*##################Modified part#####################*/
    //Find the page directory. If it does not exist, it will fail
    if ((ptep = get_pte(mm->pgdir, addr, 1)) == NULL) {
        cprintf("get_pte in do_pgfault failed\n");
        goto failed;
    }
    if (*ptep == 0) { // If it is a newly created secondary page table.
        //Initialize to establish the mapping relationship between the virtual address and the physical page
        if (pgdir_alloc_page(mm->pgdir, addr, perm) == NULL) {
            //Initialization fails with an error and exits.
            cprintf("pgdir_alloc_page in do_pgfault failed\n");
            goto failed;
        }
    }
/*#################Exercise 2 implementation##################*/    
   //The page table item is not empty. Try to switch to the page
    else { 
   
        if(swap_init_ok) {
            struct Page *page=NULL;
	    //According to the mm structure and addr address, try to change the contents of the hard disk into the page
            if ((ret = swap_in(mm, addr, &page)) != 0) {
                cprintf("swap_in in do_pgfault failed\n");
                goto failed;
            }    
            page_insert(mm->pgdir, page, addr, perm);
	    //Establish the corresponding relationship between the virtual address and the physical address, and perm sets the physical page permissions to ensure the consistency with its corresponding virtual page permissions
            swap_map_swappable(mm, addr, page, 1);//Make this page exchangeable and also add it to the order queue maintained by the algorithm
	    page->pra_vaddr = addr;		//Set the virtual address corresponding to the page
        }
        else {
            cprintf("no swap_init_ok but ptep is %x, failed\n",*ptep);
            goto failed;
        }
   }
/*#################Modified part#######################*/
   ret = 0;
failed:
    return ret;
}

3. Please describe the potential use of Pag Director Entry and Page Table Entry for ucore to implement the page replacement algorithm

The implementation of paging mechanism ensures the corresponding relationship between virtual address and physical address. On the one hand, whether the virtual address exists in the primary and secondary page table can be found to be legal; At the same time, the page replacement operation can be realized by modifying the mapping relationship. On the other hand, page replacement involves switching in and out: when switching in, you need to read the contents of a page of the disk corresponding to a virtual address into memory, and when switching out, you need to write the contents of a virtual page to a location in the disk. The page table entry can record the location of the virtual page in the disk, provide disk location information for switching in and out, and the page directory entry is used to index the corresponding page table. At the same time, we know that both PDE and PTE reserve some bits for the operating system, which can be applied to the page replacement algorithm. When the present bit is 0, the CPU does not use the content on the PTE. at this time, these bits will be idle. The idle bits can be used to save other information, such as the location of the physical page replaced by the page replacement algorithm in the exchange partition. At the same time, note the dirty bit. The operating system can judge whether to write through the page data according to the dirty bit.

4. If the page missing service routine of ucore accesses memory during execution and page access exceptions occur, what should the hardware do?

First, call the interrupt mechanism to cause an interrupt
Save on site: save the linear address with error in cr2 register; And press in the interrupt stack in turn
EFLAGS,CS, EIP. And save the access exception code error code to the interrupt stack;
Query the ISR of the corresponding page access exception according to the interrupt descriptor table, jump to the corresponding ISR for execution, and realize the page missing service routine

Exercise 2: supplement and complete the page replacement algorithm based on FIFO (programming required)

1. Perfect do_pgfault function

​ swap_init_ok is a flag bit, which indicates that the exchange initialization is successful and the replacement process can be started. First, a page is declared, and then swap is called with the structure mm, virtual address and the empty page_ In function. This function first allocates initialization for the incoming empty page, and then obtains the secondary page table corresponding to the MM primary page table through swapfs_read attempts to change the contents of the hard disk into a new page. Finally, establish the corresponding relationship between the virtual address and the physical address of the page, and then set it to be exchangeable. The virtual address of the page is set to the incoming address. So far, do_pgfault ends and a new mapping relationship is established. There will be no exceptions in the next access.

2. FIFO replacement algorithm:

First in first out (FIFO) page replacement algorithm: this algorithm always eliminates the pages that reside in memory for the longest time. Just link the pages that have been transferred into memory during the execution of an application into a queue in order. The queue head points to the page with the longest residence time in memory, and the queue tail points to the page recently transferred into memory. In this way, when pages need to be eliminated, it is easy to find the pages to be eliminated from the queue header.

3,_ fifo_map_swappable function

Function function:

Add the most recently used pages to the order queue maintained by the algorithm.

Implementation idea:

Create a pointer to the most recently used page, and then create a new null pointer in the list to add the most recently used page to the end of the order queue

static int _fifo_map_swappable(struct mm_struct *mm, uintptr_t addr, struct Page *page, int swap_in) {        
        list_entry_t *head=(list_entry_t*) mm->sm_priv;   
        list_entry_t *entry=&(page->pra_page_link);          
        assert(entry != NULL && head != NULL);   
        list_add(head, entry); //Add recently used pages to the end of the order queue  
        return 0;   
    } 

4,_ fifo_swap_out_victim function

Function function:

Query which page needs to be swapped out and perform the swappout operation

Implementation idea:

Use the linked list operation to delete the page that entered the earliest, and give the page to the incoming parameter PTR according to the comments_ page.

static int  _fifo_swap_out_victim(struct mm_struct *mm, struct Page ** ptr_page, int in_tick) {       
    list_entry_t *head=(list_entry_t*) mm->sm_priv;          
    assert(head != NULL);      
    assert(in_tick==0);
    list_entry_t *le = head->prev;   // Take out the chain header, that is, the earliest physical page
    assert(head!=le);  // Ensure that the linked list is not empty
    struct Page *p = le2page(le, pra_page_link);
    //Find the Page structure of the corresponding physical Page
    list_del(le);      //Delete the oldest page from the queue      
    assert(p !=NULL);       
    *ptr_page = p; //Store the address of this page in PTR_ In page
    return 0; 
}

Result verification:

[the external link image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-Efvr4G2d-1639279481645)(E:\Typore image \ 1638683145806.png)]

5. What are the characteristics of the page to be swapped out?

The page that has been paged in at the earliest and has not been accessed recently

6. How to judge a page with such characteristics in ucore?

By judging whether it has been accessed, the FIFO can be used for the physical page that has not been accessed

7. When do I exchange in and out?

When the page to be called is not in the page table and the page table is full, a PageFault will be triggered. At this time, the check in and check out operations * * are required** Specifically, when the memory saved in the disk needs to be accessed, it needs to be swapped in; When the memory in the physical page box is selected by the page replacement algorithm, it needs to be swapped out.

Extended exercise Challenge: implement the extended clock page replacement algorithm for identifying dirty bit (programming required)

Code implementation:

If the status is (0, 0), it means that the current data is invalid and has not been modified. Just go up and down the physical page from the linked list, and the physical page is recorded as a page out, and it does not need to be written to the swap partition; If the status is (1, 0), set the access bits in the PTE of the virtual page corresponding to the physical page to 0, and then the pointer jumps to the next physical page; If the status is (0, 1), change the dirty bit in the PTE of the virtual page corresponding to the physical page to 0, write the physical page to external memory, and then the pointer jumps to the next physical page; If the status is (1, 1), the access in the PTE of all corresponding virtual pages of the physical page is set to 0, and then the pointer jumps to the next physical page;

static int
_extend_clock_swap_out_victim(struct mm_struct *mm, struct Page ** ptr_page, int in_tick)
{
    list_entry_t *head=(list_entry_t*) mm->sm_priv;
        assert(head != NULL);
    assert(in_tick==0);

    // Find for the first time! PTE_A & ! PTE_ D. Reset the PTE of the current page at the same time_ A
    // Second search! PTE_ A & ! PTE_D. Reset the PTE of the current page at the same time_ D
    // The third time, I'm sure I can find it
    for(int i = 0; i < 3; i++)
    {
        list_entry_t *le = head->prev;
        assert(head!=le);
        while(le != head)
        {
            struct Page *p = le2page(le, pra_page_link);
            pte_t* ptep = get_pte(mm->pgdir, p->pra_vaddr, 0);
            // If these two conditions are met, they will be allocated directly
            if(!(*ptep & PTE_A) && !(*ptep & PTE_D))
            {
                list_del(le);
                assert(p !=NULL);
                *ptr_page = p;
                return 0;
            }
            // If a used PTE is accessed in the first lookup, it is marked as unused.
            if(i == 0)
                *ptep &= ~PTE_A;
            // If a modified PTE is accessed in the second lookup, it is marked as unmodified.
            else if(i == 1)
                *ptep &= ~PTE_D;

            le = le->prev;
            // After traversal, the flag bit must be modified, so the TLB should be refreshed
            tlb_invalidate(mm->pgdir, le);
        }
    }
    // According to the previous assert and if, it is impossible to execute here, so return -1
    return -1;
}

struct swap_manager swap_manager_fifo =
{
     .name            = "extend_clock swap manager",
     .init            = &_fifo_init,
     .init_mm         = &_fifo_init_mm,
     .tick_event      = &_fifo_tick_event,
     .map_swappable   = &_fifo_map_swappable,
     .set_unswappable = &_fifo_set_unswappable,
     .swap_out_victim = &_extend_clock_swap_out_victim,
     .check_swap      = &_fifo_check_swap,
};

Experimental summary

1. I feel I have gained a lot from the whole experiment. I have deeply learned the mechanism of virtual memory management by searching for information on the Internet, and I am even more impressed by the experiment.
2. Many problems were encountered in the experiment, such as the relationship between some structures involved in the management of process virtual memory. Because there are many kinds, it is easy to be confused. The relationship between them was sorted out through the column diagram.
3. The course of operating system is relatively basic and involves a lot of contents. Sometimes it will be difficult to learn, but this course is very important and is the cornerstone of professional courses in the future. I hope to leave more time for experiments in the future, so as to further understand and master the operating system.

Topics: Operating System