Skip to content

Project 02. Light weight Process (milestone3)

Yooniversal edited this page Jul 26, 2022 · 1 revision

Implementation : Interaction with other services in xv6

Interaction with system calls in xv6

exit

void
exit(void)
{
  ...

  acquire(&ptable.lock);

  // clear all threads of current process
  if(curproc->is_thread == 0) {
	  for(p=ptable.proc; p<&ptable.proc[NPROC]; ++p) {
		if(!p || p->is_thread == 0 || p->pid != curproc->pid)
			continue;

		deallocuvm(p->pgdir, p->stack + 2*PGSIZE, p->stack);

		kfree(p->kstack);
		p->kstack = 0;
		p->sz = 0;
		p->state = UNUSED;
		p->pid = 0;
		p->parent = 0;
		p->killed = 0;
		p->name[0] = 0;

		p->tickets = 0;
		p->lev = 0;
		p->in_mlfq = 0;

		stack_st[p->stack_id] = 0;

		p->is_thread = 0;
		p->tid = 0;
		p->master = 0;
		p->stack = 0;
		p->stack_id = 0;
		p->used_all_time = 0;

		p->ret_val = 0;

		--thd_cnt;
		p->master->tcnt--;
	  }
  }

  // Parent might be sleeping in wait().
  wakeup1(curproc->parent);

  // Pass abandoned children to init.
  for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){
    if(p->parent == curproc){
      p->parent = initproc;
      if(p->state == ZOMBIE)
        wakeup1(initproc);
    }
  }

  // Jump into the scheduler, never to return.
  curproc->state = ZOMBIE;
  sched();
  panic("zombie exit");
}
  • If a process which isn't a thread calls exit(), then clean up all threads of the process
    Otherwise, do the same mechanism of original exit()

fork

  • fork() makes only master process but not thread
    Thread is created by thread_create() which has simliar way with fork() but makes the thread having same address and pid
  • Because of the reason above, code of fork() is not modified

exec

int
exec(char *path, char **argv)
{
  ...

  // Initalize all threads of process
  // Threads of current process must be cleaned up
  acquire(&ptable.lock);

  for(p=ptable.proc; p<&ptable.proc[NPROC]; p++) {
	if(!p || p->is_thread == 0 || p->pid != curproc->pid)
		continue;

	release(&ptable.lock);

	for(fd=0; fd<NOFILE; ++fd)
		if(p->ofile[fd]) {
			fileclose(p->ofile[fd]);
			p->ofile[fd] = 0;
		}

	begin_op();
	iput(p->cwd);
	end_op();
	p->cwd = 0;

	acquire(&ptable.lock);

	deallocuvm(p->pgdir, p->stack + 2*PGSIZE, p->stack);

	kfree(p->kstack);
	p->kstack = 0;
	p->sz = 0;
	p->state = UNUSED;
	p->pid = 0;
	p->parent = 0;
	p->killed = 0;
	p->name[0] = 0;

	p->tid = 0;
	p->master = 0;
	p->stack = 0;
	p->stack_id = 0;
	p->ret_val = 0;
  }

  release(&ptable.lock);

  // Commit to the user image.
  oldpgdir = curproc->pgdir;
  curproc->pgdir = pgdir;
  curproc->sz = sz;
  curproc->tf->eip = elf.entry;  // main
  curproc->tf->esp = sp;
  switchuvm(curproc);
  freevm(oldpgdir);
  return 0;

 bad:
  ...
}
  • For using new process, clean up all threads of current process And clean up current process, too

sbrk

int
growproc(int n)
{
  uint sz;
  struct proc *curproc = myproc();

  // Make multiple threads do not influence each other by locking
  acquire(&ptable.lock);

  sz = curproc->sz;
  if(n > 0){
    if((sz = allocuvm(curproc->pgdir, sz, sz + n)) == 0)
      return -1;
  } else if(n < 0){
    if((sz = deallocuvm(curproc->pgdir, sz, sz + n)) == 0)
      return -1;
  }

  release(&ptable.lock);

  curproc->sz = sz;
  switchuvm(curproc);
  return 0;
}
  • To prevent working sbrk() simultaneously from multiple threads, use lock at start point
  • Because sys_sbrk(void) use growproc(), added some codes in growproc()

kill

  • This function kills the process who has same pid with parameter pid
  • In thread_create(), thread has same pid with master process
    So if kill(int pid) is called, then all threads of master process having pid must be killed
  • No addtional code in here

sleep

  • If a thread calls sleep(), then sleep() changes state of the thread to SLEEPING
    But It doesn't influence to master process
  • It works enough with original code of sleep(), so no additional code in here

Interaction with the MLFQ and Stride schedulers

// in proc.c
void
sched(void)
{
  ...
  intena = mycpu()->intena;

  if(p->master->used_all_time) {
	p->master->used_all_time = 0;
	swtch(&p->context, mycpu()->scheduler);
  } else {
	if(p->is_thread) {
		np = select_thread(p->master);
		if(!np)
			swtch(&(myproc()->context), mycpu()->scheduler);
		else {
			// run next thread but not go though scheduler
			// switch directly
			mycpu()->proc = np;
			switchuvm(np);
			np->state = RUNNING;

			swtch(&(myproc()->context), np->context);
		}
	} else {
		swtch(&(myproc()->context), mycpu()->scheduler);
	}
  }

  mycpu()->intena = intena;
}

// in newfunc.c
int
set_cpu_share(int share)
{
  ...
  // Initialize process
  p->tickets = cur_t;
  p->lev = 3;
  p->used_time = 0;
  p->in_mlfq = 0;
  p->pass = get_min_pass();
  p->stride = TOTAL_TICKETS / cur_t;
  enqueue_to_sq(p);

  mlfq.tickets = mlfq_sum;
  sq.tickets = sq_sum;

  mlfq.cnt--;

  // Initialize threads of current process
  struct proc *thd;
  for(thd=ptable.proc; thd<&ptable.proc[NPROC]; ++thd) {
	if(!thd || thd->is_thread == 0 || thd->pid != p->pid)
		continue;

	thd->lev = 3;
	thd->in_mlfq = 0;
	thd->tickets = p->tickets;
  }

  release(&ptable.lock);

  return 0;
}
  • In scheduler(), it selects a process by whether MLFQ or Stride scheduling corresponding to which it belongs to
    It doesn't pick thread in scheduler()
  • After master process is selected, work with threads belongs to master process until it meets the time criteria
    Switching next thread doesn't go through scheduler() but do directly in sched() by calling swtch()
  • Because time quantum of thread is 1 ticks, it calls yield() whenever reaches in trap() after running
    And update used_time and pass of master process
  • When master process calls set_cpu_share(), all threads of master process will be managed by stride scheduling

Result

test_thread

P2_result.png

  • Passed racingtest, basictest, jointest1, jointest2
  • Failed to pass stresstest

test_thread2

P3_result.png

  • Passed racingtest, basictest, jointest1, jointest2
  • Failed to pass stresstest