Nachos(1)

#1

KThread.java의 389행의 run함수를 고쳐서 각 Thread가 10번 실행되도록 할 수 있다.

public void run() {
	for (int i=0; i<10; i++) {
		System.out.println("*** thread " + which + " looped "
		+ i + " times");
		currentThread.yield();
	}
}

실행 결과

nachos practice execution1

#2

KThread.fork()

코드

    public void fork() {
		Lib.assertTrue(status == statusNew);
		Lib.assertTrue(target != null);
		
		Lib.debug(dbgThread,
			  "Forking thread: " + toString() + " Runnable: " + target);
	
		boolean intStatus = Machine.interrupt().disable();
	
		tcb.start(new Runnable() {
			public void run() {
			    runThread();
			}
		    });
	
		ready();
		
		Machine.interrupt().restore(intStatus);
    }

동작 개요

fork()메소드는 두 개의 thread가 병렬적으로 실행되도록 동작한다. TCB에 Runnable객체를 추가하여 ready queue에 삽입하고, ready상태로 만든다.

조건 확인

디버깅 메시지 출력

지역 변수 선언

Thread ready

이전 상태 복원

KThread.runThread()

코드

    private void runThread() {
		begin();
		target.run();
		finish();
    }

동작 개요

KThread클래스 내부에서 사용되는 private method이다. 바로 직전에 다룬 Kthread.fork()메소드에서 호출하는 것을 확인한 바 있다. 현재 thread의 실행 준비, 실행, 실행 종료 과정을 순서대로 수행하도록 동작한다.

begin() 호출(실행 준비)

thread 실행

finish() 호출(실행 종료)

KThread.yield()

코드

public static void yield() {
	Lib.debug(dbgThread, "Yielding thread: " + currentThread.toString());
	Lib.assertTrue(currentThread.status == statusRunning);
	boolean intStatus = Machine.interrupt().disable();
	currentThread.ready();
	runNextThread();
	Machine.interrupt().restore(intStatus);
}

동작 개요

현재 thread가 ready 상태인 다른 thread가 있을 경우 cpu점유권을 양보하도록 한다. 이때 현재 thread는 destroy되는 것이 아닌 ready상태로 전이하며, readyQueue에 삽입되어 다시 스케줄링 될 수 있도록 한다.

디버깅 메시지 출력 및 조건 확인

지역 변수 선언

현재 thread 상태 전이(running -> ready)

다음 thread 실행

이전 상태 복원

KThread.sleep()

코드

public static void sleep() {
	Lib.debug(dbgThread, "Sleeping thread: " + currentThread.toString());
	Lib.assertTrue(Machine.interrupt().disabled());

	if (currentThread.status != statusFinished)
		currentThread.status = statusBlocked;

	runNextThread();
}

동작 개요

이전에 알아본 runThread()메소드 내부에서 호출한 finish()메소드에서 사용한 적 있는 메소드이다. sleep()메소드는 현재 thread가 finish 또는 block되었을 경우 현재 thread를 sleep시켜 readyQueue에 삽입되도록 하거나 destroy되도록 하는 역할을 한다.

디버깅 메시지 출력 및 조건 확인

상황 판단

다음 thread실행

KThread.join()

코드

본 실습에서 제공된 join()메소드의 코드는 조건 검사를 제외한 아무런 동작도 하지 않아 이상함을 느끼고 직접 nachos프로젝트의 저장소를 찾아가 join()구현부를 따로 찾아보았다. join()메소드의 동작 과정과 소스 코드 분석은 github의 코드를 바탕으로 수행하였다.

public void join() {
	Lib.debug(dbgThread, "Joining to thread: " + toString());
	Lib.assertTrue(this != currentThread);
}
public void join() {
	Lib.debug(dbgThread, "Joining to thread: " + toString());
	Lib.assertTrue(this != currentThread);
	
	if(this.status==statusFinished){
		return;
	}
	
	boolean inStatus=Machine.interrupt().disable();
	
	if(KThread.currentThread.isJoined);
	else{
		joinQueue.waitForAccess(currentThread);
		isJoined=true;
		sleep();
	}
	
	Machine.interrupt().restore(inStatus);
}

동작 개요

join()메소드는 현재 객체의 thread를 다른 thread에서 기다리도록 하는 method이다. 당연하지만 현재 객체의 thread는 현재 실행 중인 thread일 수 없다.

디버깅 메시지 출력 및 조건 확인

본 객체의 thread가 이미 finished 상태인 경우에 대한 처리

지역 변수 선언

join여부 확인

이전 상태 복원

KThread.runNextThread()

코드

private static void runNextThread() {
	KThread nextThread = readyQueue.nextThread();
	if (nextThread == null)
		nextThread = idleThread;
	
	nextThread.run();
}

동작 개요

readyQueue의 다음 thread를 실행시킨다.

다음 thread load

load된 nextThread실행

KThread.run()

코드

private void run() {
	Lib.assertTrue(Machine.interrupt().disabled());
	Machine.yield();
	currentThread.saveState();
	
	Lib.debug(dbgThread, "Switching from: " + currentThread.toString()
		  + " to: " + toString());
	currentThread = this;
	tcb.contextSwitch();
	currentThread.restoreState();
}

동작 개요

현재 실행중인 thread를 yeild하고 새로운 thread를 실행시킨다. currentThread를 run()메소드를 호출한 객체의 thread로 갱신한다. 이후 context switching을 수행하고 새로운 thread를 실행시킨다.

실행 조건 확인

run() 수행 준비

디버깅 메시지 출력

context switching

thread실행 준비

KThread.restoreState()

코드

protected void restoreState() {
	Lib.debug(dbgThread, "Running thread: " + currentThread.toString());
	
	Lib.assertTrue(Machine.interrupt().disabled());
	Lib.assertTrue(this == currentThread);
	Lib.assertTrue(tcb == TCB.currentTCB());

	Machine.autoGrader().runningThread(this);
	
	status = statusRunning;

	if (toBeDestroyed != null) {
		toBeDestroyed.tcb.destroy();
		toBeDestroyed.tcb = null;
		toBeDestroyed = null;
	}
}

동작 개요

현재 thread가 실행될 준비를 하고, 상태를 running으로 전이시킨다. 이전 thread의 종료로 인한 tcb의 destroy리스트가 남아 있다면 destroy를 수행한다.

디버깅 메시지 출력

실행 조건 확인

현재 thread실행

toBeDestroyed 확인

KThread.saveState()

코드

/**
* Prepare this thread to give up the processor. Kernel threads do not
* need to do anything here.
*/
protected void saveState() {
	Lib.assertTrue(Machine.interrupt().disabled());
	Lib.assertTrue(this == currentThread);
}

동작 개요

현재 thread가 프로세스 점유를 넘기는 것을 준비하는 메소드이다. 주석의 내용에 따르면, 커널 메소드는 이 부분에서 아무 것도 구현할 필요가 없다고 되어 있어 메소드의 구현부에는 조건 검사밖에 없는 상황이다.

실행 조건 확인

추측

커널 메소드가 아닌 경우에서 svaeState()메소드를 호출한 경우를 가정해 보자. saveState라는 메소드 명에 맞게, 본 메소드는 현재 thread의 context정보를 다시 불러올 수 있도록 저장하는 역할을 할 것이다. 저장된 context는 TCB 자료 구조로 메모리에 저장될 것이다.

#3

private static class mainThread implements Runnable {
	mainThread(){}

	public void run() {
		for (int j = 0; j < 10; j++) {
			KThread.currentThread().yield();
		}
	}
}
public void selfTest() {
	mainThread mt = new mainThread();
	new KThread(new PingTest(1)).setName("forked thread").fork();
	mt.run();
}