Stack Growth
In project 2, the stack was a single page starting from the USER_STACK
, and
programs' executions were limited to this size. Now, if the stack grows past its
current size, we allocate additional pages as necessary.
Allocate additional pages only if they "appear" to be stack accesses. Devise a heuristic that attempts to distinguish stack accesses from other accesses.
User programs are buggy if they write to the stack below the stack pointer, because typical real OSes may interrupt a process at any time to deliver a "signal," which modifies data on the stack. However, the x86-64 PUSH instruction checks access permissions before it adjusts the stack pointer, so it may cause a page fault 8 bytes below the stack pointer.
You will need to be able to obtain the current value of the user program's stack
pointer. Within a system call or a page fault generated by a user program, you
can retrieve it from the rsp
member of the struct intr_frame
passed to
syscall_handler()
or page_fault()
, respectively. If you depend on page
faults to detect invalid memory access, you will need to handle another case,
where a page fault occurs in the kernel. Since the processor only saves
the stack pointer when an exception causes a switch from user to kernel mode,
reading rsp
out of the struct intr_frame
passed to page_fault()
would
yield an undefined value, not the user stack pointer. You will need to arrange
another way, such as saving rsp into struct thread
on the initial transition
from user to kernel mode.
Implement stack growth functionalities.
To implement this, you first modify vm_try_handle_fault
in vm/vm.c
to
identify the stack growth. After identifying the stack growth, you should make a
call to vm_stack_growth
in vm/vm.c
to grow the stack. Implement the
vm_stack_growth
.
bool vm_try_handle_fault (struct intr_frame *f, void *addr,
bool user, bool write, bool not_present);
This function is called in
page_fault
inuserprog/exception.c
while handling the page fault exception. In this function, you need to check whether the page fault is a valid case for a stack growth or not. If you have confirmed that the fault can be handled with a stack growth, callvm_stack_growth
with the faulted address.
void vm_stack_growth (void *addr);
Increases the stack size by allocating one or more anonymous pages so that
addr
is no longer a faulted address. Make sure you round down theaddr
to PGSIZE when handling the allocation.
Most Oses impose some absolute limit on stack size. Some OSes make the limit
user-adjustable, e.g. with the ulimit
command on many Unix systems. On many
GNU/Linux systems, the default limit is 8 MB. For this project, you should limit
the stack size to be 1MB at maximum.
Now, all the stack-growth test cases should be passed.