If a process is running a user program in user mode and needs a system service, such as reading
data from a file, it has to execute a "trap" instruction to transfer control to the OS.
When an interrupt signal occurs, there has to be some reasons for the occurrence.
The information needed to be handled are pushed onto the stack in user space.
The system handler is just a function in os161 that takes a pointer to trapframe as argument.
In os161, we define a structure called trapframe, which describe what is saved on the stack
during entry to the exception handler. The first 4 32-bit arguments are passed in the 4
argument registers a0-a3 by convention. 64-bit arguments are passed in aligned pairs of
registers, either a0/a1 or a2/a3. The system call number is passed in the v0 register.
On success, the return value is passed in the v0 register. a3 is set to 0.
For example, 0 is returned to the child process after fork() succeed.
On failure, error code is passed in v0 and a3 is set to 1.
Upon syscall return, the PC stored in the trapframe (tf_epc) must be incremented by 4 bytes,
which is the length of one instruction. Otherwise the return code restart at the same point.
Look closely inside syscall() in kern/arch/mips/syscall/syscall.c, we shall see how it works.
In the case of fork(), the OS will pass the trapframe pointer and PID into sys_fork.
For the parent process, the syscall function will examine the return value(a PID or error code)
of sys_fork to set the register values. Then the parent process shall proceed.
In sys_fork(), the parent process will allocate some memory in the heap and make a copy
of the trapframe on the stack for the child process. A pointer to this trapframe will be passed into thread_fork().
Later the child will free the memory allocated previously by the parent on the heap.
Then the thread_fork() is called to create the a new thread structure for the child process.
What thread_fork() will do first is to allocate a new stack, get a new PID of the child, do some
address space copy from its parent, and it also copies the working directory.
After the new thread structure is set up, it will initialize the switch frame for the new thread.
But what the hell is a switchframe? This is actually the PCB(process control block) of
a thread. The sf registers of this "switchframe" will hold the arguments from
thread_fork(): a function poiner "entrypoint", the trapframe copied from the address space
of the parent process to the child process's, and some garbage "data2".
Then we call thread_make_runnable() to make the new thread runnable.
The funnction "entrypoint" will be implemented in syscall.c.
The child process will start at this function and free the trapframe passed to it.
It will create its own trapframe on the stack in its address space and sets up the
return values in the registers. It finally increment the program counter by 4 bytes and
calls mips_usermode to warp to user space.
Eventually, we will see fork returns twice:
0 and PID of child process.
This is my understanding so far, after a night without sleep. The next thing I will do is to
implement execv(), which is quite similar to runprogram().
1 comment:
Testing...
http://lifes-eyes.blogspot.com/
Post a Comment