MIT 6.828 jos/xv6 LAB4-PARTB

Source: Internet
Author: User
Tags utext

This is the fork in the UNIX standard system call, and the core is, of course, the copy on write technology

As for why copy on write is used because the child process is likely to execute exec () immediately after it is created, the series of copies previously made is useless

So, when creating a new subprocess, it is only necessary to copy the parent process's memory Map (page table), and to mark all of the parent process's memory-mapped pages as read-only, so that when the child process or parent process attempts to read it is safe, and when trying to write, it will go page fault, In the page fault processing routine, a separate copy of the page (such as a stack) is written, and the corresponding mapping of the page table of the process that made the write behavior is modified.

So, the first step should be to define or establish a page fault processing routine.

Each process needs to register this processing routine with the kernel, just pass a function pointer to

Sys_env_set_pgfault_upcall function

Sets the page fault processing routine of the current process to the function that the Func points to

Normal and exception stacks in user environments

There is a third stack, the exception stack

In the normal operation of the user program, out of the normal user stack, the user stack at the top of the Ustacktop

The exception stack is created for the exception handling routines set above. When an exception occurs, and the user process registers the exception's processing routine, it goes to the exception stack and runs the exception-handling routine

There are three stacks in the current position:

    1. [Kstacktop, Kstacktop-kstksize]

Kernel-State system stack

    1. [Uxstacktop, Uxstacktop-pgsize]

User-state error handling stack

    1. [Ustacktop, Utext]

User state run Stack

    • For kernel-State system stacks

is the stack running kernel-related programs, after the interrupt is triggered, the CPU will automatically switch the stack to the kernel stack, and the kernel stack is set in the Kern/trap.c trap_init_percpu ()

You can see that the size of the kernel stack is fixed and is set when the system is initialized.

After the interrupt is triggered, enter Kern/trapentry. s code, the stack at this point has switched to the kernel stack, and a series of content is pressed into the kernel stack, manually forming a trapframe

    • User run-time stack

is the stack used in the user's run, which is initialized at the beginning of the user process creation.

The actual application process in the case of initialization is only one page size, if a stack overflow occurs, the page error will be triggered, but the user space in the page error is a handler, will be the bottom of the stack page map to the current memory space

So, the user runs the stack is on one side of the growth, while the kernel stack is fixed size, the error stack is also

    • User State Error Stack

After the user has registered their own interrupt handlers, the corresponding routines run at the stack

The process is as follows

    • First into the kernel, the stack position from the user run stack switch to the kernel stack, into the trap, interrupt processing distribution, into the Page_fault_handler ()
    • When the user program triggers the page fault (the kernel triggers a direct panic), it assigns a utrapframe size to the user error stack.
    • Switch the stack to the user error stack and run the response user interrupt Handler
    • The interrupt handler may trigger another interrupt of the same type, which will result in recursive processing
    • After processing is complete, return to the user run stack

Invoking the user page fault handler

When the user process runs out of error, and for this error, the user has defined their own exception handling routines, according to the previous argument, it is necessary to switch to the exception stack to perform

So how do you switch the past?

A user-defined user process can be treated as a function call, and when an error occurs, a function is called, but actually the current process is not changed.

So when switching to the exception stack, still running the current process, but just run the interrupt handler function, so that the stack pointer has changed, and the program counter EIP has changed, but also need to know is the address where the error is thrown. These are the messages that need to be passed when switching to the exception stack.

And before switching from the user stack to the kernel stack, this is done by constructing the struct on the stack, passing the pointer

Here, a new structure is defined to record the information when a user-defined error occurs utrapframe

There are more utf_fault_va here than Utrapframe, because the memory address that triggered the error is logged

At the same time also less es,ds,ss and so on. Because switching from the user state stack to the exception stack, or from the exception stack to switch back, is actually a user process, so does not involve the segment of the switch, no record

In practice, Trapframe is present as a struct that records the complete state of a process, and is passed as a function parameter, while Utrapframe only uses when handling user-defined errors.

Overall, when a page fault occurs during normal execution, the switch of the stack is

User Run Stacks---> Kernel stacks---> Exception stacks

If an error occurs in the exception handler, the switch to the stack is

Exception Stacks---> Kernel stacks---> Exception stacks

The next step is to implement the Page_fault_handler function

If you are already on the user error stack, then need to set aside 4 bytes, otherwise do not need, specific and jump mechanism is related

Simply put aside the space for saving Utrapframe at the top of the current error stack, and then copy the parameters from the TF.

Modify the program counter and stack pointer for the current process, and then restart the process, and the interrupt handler will be run on the user error stack.

Of course, after the interrupt handler has finished running, it needs to go back to the user run stack, which is what the exception handler needs to do.

Here's one more question.

What happens if the exception stack overflow?

Just look at memlayout.h and you'll know.

You can see that the user exception stack on the size of a page, once overflow, access is the kernel does not have access to the space, will occur in the Kernel Space page fault, this time will be directly panic, will not cause more serious consequences

The next step is to write a part of the assembler, the main implementation of the function is: After the user-defined processing function returned, how to return from the user error stack directly to the user run stack. This Part I have a headache, direct reference to Zhang Yishi Brother's code

There is also the Set_pgfault_handler () function, mainly for the process set processing process, while allocating the error stack

Next is the most important part: implementing the Copy-on-write Fork

Unlike the previous dumbfork, when a child process is forked, the first thing to do is to copy the entire map of the parent process's page table to the child process's address space.

This time the physical page will be mapped at the same time by two processes, but it should be isolated at the time of writing. The way to do this is to mark all the parts of the parent process space that can be written in the page table as readable when the child process is mapped, and cow

And when the parent process or the child process any of the time the write, because the page is not writable, so it will trigger an exception, into the page we set the fault processing routine, when it detects that the cow page is a write operation, you can write the contents of the page will be copied one copy, remap.

At first I was thinking that when you go to page fault to process a routine, after you copy it, you can mark the page as writable, so that another process does not trigger an exception when it is written. But I'm still naive, because the fork operation can be nested multiple layers, so I don't know how many processes have mapped to this page.

So the way to do this is, when the process triggers page fault, after the corresponding page has been copied, the mapping of the original page needs to be solved, the page_remove can be called, and the function will decide whether to recycle based on the reference count of the page, so there is no memory leak.

First look at page fault processing routines

This logic is still quite clear, here borrowed a certain will not be used in the location of Pftemp, dedicated to the occurrence of page fault when the copy content.

It is important to note that before the map must be unmap, otherwise, if the child process and the parent process are written to the same location, all copied a copy, then the original copy is useless, and without unmap operation, this page of the reference count will never return to zero.

And then look at the Duppage function.

The function is to map the physical page corresponding to the PN page of the current process to the PN page of Envid and mark the page as Cow

Fork function

The mapping needs to be copied in the past, where the range of addresses to consider is from Utext to Uxstacktop, and the scope above is because they are all the same, the Env_alloc has been set up, so there is no need to consider

The first step is to set the error-handling routine for the parent process, where the Set_pgfault_handler function is called because it is not currently known whether the parent process has established an error stack, and if it does not, it will create a

And Sys_env_set_pgfault_upcall doesn't build the wrong station.

Call Sys_exofork to prepare a child process with the same state as the parent process, and the state is temporarily set to Env_not_runnable

Then copy the part of the mapping, in the page table of the current process all the pages marked as pte_p need to be copied into the child process space

However, one exception is the need for a new page to copy the content, that is, the user error stack

Because the copy-on-write is to rely on user error stack implementation, so that this stack to fork when the completion of each process has one, so to hard copy over

It looks rather awkward here, the process is:

    1. Request a new physical page, mapped to the (uxstacktop-pgsize) location of the child process
    2. The pftemp location of the parent process is also mapped to the physical page of the child process's new request, so that the parent process can also access the page.
    3. In the parent process space, copy the user error stack to the error stack of the child process, which is the page you just requested.
    4. The parent process then de-pftemp the map

Finally, the state of the handle process is set to run.

MIT 6.828 jos/xv6 LAB4-PARTB

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.