Monday 3 October 2011

OS161 Fork

Lets make this one quick. It is late but I know lots of people will want this in the morning. Apologies for any oversights or errors. Also the formatting will probably suck.

How exactly do you “Fork”.

As the professor has said a lot has already been implemented for us.

We need to do 2 things. Make a sys_fork function that will be called from mips_syscall, make md_forkentry which is the first function that the newly forked thread will call.

sys_fork


Before we create a new thread me must get a pid for it.

EDIT: This can also be done in thread_fork. Including associating the thread with your tracking system.

Once that is done we can create a new thread, attach that thread structure to whatever tracking system you have involving pids, and return that pid to the parent. This may seem complicated but all it actually means is returning the value from your sys_fork function and letting mips_syscall take care of the rest. mips_syscall will put the return value into the parent threads trapframe, advance the PC and return. Execution then goes back into exception.S does some loading from the TF and returns to usermode.

What else do we do before we create a new thread? Copy the trapframe of course. The trapframe holds all of the information that was saved when we dropped into the kernel. Just do a memcpy into a temporary struct pointer. What else do we have to do? Copy the address space. Linux does this in a very smart way to make forking very light and only copies memory when a thread tries to write to it. This is actually fairly simple because this functionality is built into the VM and they just set some flags on the pages and it is all good. In our case we will just use as_copy and laugh as it does nothing exceptionally useful for now =D.

Okay so now we have done some stuff… right? What is next. We are going to call thread_fork and the argument we want to pass is the trapframe copy we made earlier. The function we would like it to call is md_forkentry (that name makes a little more sense now doesn’t it).

SIDENOTE: You have a decision to make. The new thread can attach its PID to your tracking system instead of the parent. This could be a smarter way of doing things and all you need to do is modify md_forkentry to take the pid as the second argument. This will depend on how your waitpid system is designed.

md_forkentry


Now this is where the magic happens. We have 2 threads with identical trapframes, same instruction spaces and interrupts are on. As stated above, the parent is simple. Just take the PID you got from get_new_pid (or whatever you called it) and return that from sys_fork. As for the child, they are in md_forkentry which is empy right now. What should it be doing?

This is where we need to do a couple of child specific things. We need to set our return value in the copied trapframe to 0 since we are the child which is done by modifying the v0 register. We also need the copy of the address space. The address space needs to be copied in the parent before thread_fork other wise it could have changed and not be what we want anymore. The question is how do we get its value to the new thread. It could be passed into thread_fork as the second argument to the function (an unsigned long, this seems a little dirty). Another option is to use the trapframe creatively. In the parent after a copy trapframe has been made we can use a0-a3 since we know that fork takes no arguments. This seems a little dirty as well.

That should be all of it. Of course there are some things left out but it should answer the big questions or a least point you on the right track.

100% there is a different way of approaching this, maybe in a more efficient way, hopefully it helped!
-FlounderingZ

4 comments:

  1. This comment has been removed by the author.

    ReplyDelete
  2. Hi, I'm now working on the sys_fork and suffering a weird bug.
    That is, when a forked child thread call fork again, the os just panic, saying "Unknown interrupt; cause register is 00000000".

    The test code is something like:
    pid1 = fork();
    pid2 = fork();

    In sys_fork, I do the following:
    - copy the parent trapframe into a kernel buffer (allocated using kmalloc)
    - copy parent's address space using as_copy
    - call thread_fork, passing with the kernel buffered trapframe, and the address space

    In child_forkentry, I do the following:
    - modify the trapframe's $v0, $a3, $epc
    - load the address space into curthread->t_addrspace
    - activate the address space using as_activate
    - declare a trapframe on stack and copy the content of the modified trapframe
    - call mips_usermode using the trapframe on stack

    Thanks for you reading, and hope I've make the problem clear.

    ReplyDelete
    Replies
    1. I figured out that this problem is not because I call fork in a forked child process, but because in the child, I call check() after the first fork. That is, the following code will trigger a crash:
      pid0 = fork();
      if (pid0 == 0){ // child process
      check();
      pid1 = fork(); // child fork
      printf("[%d]\n"), getpid()); // this won't be executed, sys161 crash
      }
      If child don't call check, then everything is OK. So I think there are some problem with child process's address space. But I can not find the fault. Any clue?

      Delete
  3. What do you copy the parents addrspace to before calling thread_fork? Just a temporary variable?

    ReplyDelete