Nachos 스레드 관리

14929 단어
Nachos 스레드 관리
 
Nachos의 스레드는 커널에서 thread 클래스의 개체로 구현됩니다.루틴 제어 블록은 클래스의 데이터 구성원의 방식으로 이루어진다.
 
//thread 클래스 소스 코드 - 정의
class Thread {
  private:
    // NOTE: DO NOT CHANGE the order of these first two members.
    // THEY MUST be in this position for SWITCH to work.
    int* stackTop;			 // the current stack pointer
    int machineState[MachineStateSize];  // all registers except for stackTop

  public:
    Thread(char* debugName);		// initialize a Thread 
    ~Thread(); 				// deallocate a Thread
					// NOTE -- thread being deleted
					// must not be running when delete 
					// is called

    // basic thread operations

    void Fork(VoidFunctionPtr func, int arg); 	// Make thread run (*func)(arg)
    void Yield();  				// Relinquish the CPU if any 
						// other thread is runnable
    void Sleep();  				// Put the thread to sleep and 
						// relinquish the processor
    void Finish();  				// The thread is done executing
    
    void CheckOverflow();   			// Check if thread has 
						// overflowed its stack
    void setStatus(ThreadStatus st) { status = st; }
    char* getName() { return (name); }
    void Print() { printf("%s, ", name); }

  private:
    // some of the private data for this class is listed above
    
    int* stack; 	 		// Bottom of the stack 
					// NULL if this is the main thread
					// (If NULL, don't deallocate stack)
    ThreadStatus status;		// ready, running or blocked
    char* name;

    void StackAllocate(VoidFunctionPtr func, int arg);
    					// Allocate a stack for thread.
					// Used internally by Fork()

#ifdef USER_PROGRAM
// A thread running a user program actually has *two* sets of CPU registers -- 
// one for its state while executing user code, one for its state 
// while executing kernel code.

    int userRegisters[NumTotalRegs];	// user-level CPU register state

  public:
    void SaveUserState();		// save user-level register state
    void RestoreUserState();		// restore user-level register state

    AddrSpace *space;			// User code this thread is running.
#endif
};

 
 
스레드의 상태는 ThreadStatus 형식의 status 데이터 구성원에 저장됩니다.ThreadStatus 정의는 다음과 같습니다.
 
enum ThreadStatus { JUST_CREATED, RUNNING, READY, BLOCKED };


 
라인의 상태는 반드시 상기 매거 유형 중의 하나여야 한다.라인의 상태가 바뀔 때status값이 상응하는 변경입니다.라인은 자신의 라인 창고와 레지스터가 있습니다.Nachos의 스레드 스택이 스레드 상태에서 JUSTCREATED가 RUNNING이 되면 신청됩니다.thread 클래스의 구조 함수는 threadname을 설정하고 스레드 상태를 JUST 로 설정합니다.CREATED.stack은 창고 밑을 가리키는 바늘입니다.StackTop은 스택 상단을 가리킵니다.PC를 포함한 기타 레지스터는 Machine State에 저장됨
그룹 내.배열의 크기는 Machine State Size로 결정됩니다.
 
Nachos에서 사용자 스레드는 코어 스레드에서 상속됩니다.
사용자 레지스터 배열은 사용자가 사용자 레지스터 값을 저장하는 배열입니다.크기는 NumTotalRegs에 의해 결정됩니다.
MachineState는 커널 상태에서 실행되는 스레드의 상태를 저장합니다.사용자 모드에서 실행되는 스레드 상태는 userRegisters 배열로 저장됩니다.
 
Nachos에서 사용자 스레드는 모두 내부 핵 스레드로 시작되며 사용자 프로그램을 불러오고 주소 공간을 만들면 핵 스레드는 사용자 스레드로 바뀐다.
 
준비 대기열은 준비 상태에 있는 모든 스레드나 프로세스를 저장하는 데 사용됩니다.입출력 장치와 관련된 대기열은 막힌 라인이나 프로세스를 저장하고 입출력 요청이 완료되기를 기다리고 있습니다.
 
스레드 스케줄러는 이 대기열 사이에서 스레드나 프로세스를 이동합니다.
Fork 메서드는 스레드를 실행하고 스레드 상태를 JUST 에서 실행합니다.CREATED가 RUNNING이 됩니다.
Yield 메서드는 루틴을 RUNNING에서 READY 상태로 변경합니다.(현재 타임라인을 포기) 이 방법은 현재 라인을 준비 대기열 끝에 추가하고 라인 상하문을 통해 준비 대기열의 한 라인에서 실행 상태로 전환합니다.준비 대기열이 비어 있을 때, Yield 방법은 어떤 조작도 하지 않고, 현재 라인은 계속 실행됩니다.
Sleep 메서드는 루틴 상태를 RUNNING에서 BLOCKED 상태로 전환합니다.준비 대기열에서 라인 실행을 선택하십시오.준비 대기열이 비어 있을 때, cpu는 라인이 준비될 때까지 빈 상태를 유지합니다.Sleep 메서드는 입출력 작업을 수행하거나 이벤트를 기다리는 동안 자주 호출됩니다.Sleep을 호출하기 전에, 스레드는 항상 IO 장치 대기 대기열에 직접 넣습니다.
Finish 메서드는 현재 스레드를 종료하는 데 사용됩니다.
 
Nachos에서 작업 스케줄러는 Scheduler 클래스의 객체로 수행됩니다.그것의 방법은 모든 라인이나 프로세스 스케줄링 기능을 제공했다.Scheduler 객체는 시스템이 시작되면 글로벌 변수 scheduler로 작성됩니다.
//Scheduler 클래스 정의:
 
20 class Scheduler {

21 public:

22 Scheduler(); // Initialize list of ready threads

23 ˜Scheduler(); // De-allocate ready list

24

25 void ReadyToRun(Thread* thread); // Thread can be dispatched.

26 Thread* FindNextToRun(); // Dequeue first thread on the ready

27 // list, if any, and return thread.

28 void Run(Thread* nextThread); // Cause nextThread to start running

29 void Print(); // Print contents of ready list

30

31 private:

32 List *readyList; // queue of threads that are ready to run,

33 // but not running

34 };


 
Scheduler의 유일한 데이터 멤버는 준비 대기열입니다.READY 상태의 모든 스레드를 저장합니다.
ReadyTorun 함수는 준비 대기열의 끝에 스레드를 추가합니다.
FindNextTorun이 팀의 첫 번째 스레드 포인터로 돌아갑니다.
Scheduler 클래스에서 가장 재미있는 방법은 Run입니다.이 방법은 어셈블리된 SWITCH 함수를 사용하여 현재 라인의 상하문을 다른 라인의 상하문으로 전환합니다.
Thread 클래스 구조 함수가 호출되면 구성원 변수를 초기화하여 스레드 상태를 JUST 로 변경합니다.CREATED 상태입니다.이 때 스레드는 실행할 수 없습니다. 스레드 창고가 분배되지 않았고 스레드 제어 블록이 초기화되지 않았기 때문에 더 중요한 PC 레지스터는 값을 부여하지 않아서 프로그램이 어디서부터 실행될지 모르겠습니다.
 
Fork(VoidFunctionPtr func, int arg)
Fork 방법은 스레드 창고의 분배를 책임진다.func는 스레드 함수 입구 주소이고arg는 스레드 함수이다.
Fork 함수 구현:
 
87 void

88 Thread::Fork(VoidFunctionPtr func, _int arg)

89 {

90 #ifdef HOST_ALPHA

91 DEBUG(’t’, "Forking thread \"%s\" with func = 0x%lx, arg = %ld
", 92 name, (long) func, arg); 93 #else 94 DEBUG(’t’, "Forking thread \"%s\" with func = 0x%x, arg = %d
", 95 name, (int) func, arg); 96 #endif 97 98 StackAllocate(func, arg); 99 100 IntStatus oldLevel = interrupt->SetLevel(IntOff); 101 scheduler->ReadyToRun(this); // ReadyToRun assumes that interrupts 102 // are disabled! 103 (void) interrupt->SetLevel(oldLevel); 104 } AllocateStack 。 MachineState 。 AllocateStack : 257 void 258 Thread::StackAllocate (VoidFunctionPtr func, _int arg) 259 { 260 stack = (int *) AllocBoundedArray(StackSize * sizeof(_int)); ... 20 272 stackTop = stack + StackSize - 4; // -4 to be on the safe side! ... 284 machineState[PCState] = (_int) ThreadRoot; 285 machineState[StartupPCState] = (_int) InterruptEnable; 286 machineState[InitialPCState] = (_int) func; 287 machineState[InitialArgState] = arg; 288 machineState[WhenDonePCState] = (_int) ThreadFinish; 289 }

 
AllocBoundedArray는 스레드 스택을 할당하고 스택을 스택 스택 아래쪽으로 가리킵니다.
stack은 스레드 창고 꼭대기를 가리킨다.
 
매크로 PCstate, StartupPCstate, InitialPCstate, InitialArgState와 WhenDonePCstate는 각각 9, 3, 0, 1과 2를 대표한다.
ThreadRoot은 어셈블리로 이루어진 함수 이름입니다.InterruptEnable 및 ThreadFinish는 정적 함수 이름입니다.모두 Machine State 배열에 저장됩니다.각 레지스터의 값을 대표합니다.
스레드 엔트리 함수 주소는 InitialPCstate를 아래 첨자(0번 위치)로 하는 배열에 저장됩니다.스레드 함수 매개 변수는 InitialArg(1번 위치)를 다음 표의 MachineState 배열에 저장됩니다.
스레드가 실행되기 시작할 때 Machine State [InitialPCstate] 는 라 레지스터에 불러옵니다.ra는 반환 주소 레지스터라고 불리며, 스레드 함수를 저장하는 첫 번째 명령이 시작되는 위치입니다.
ThreadRoot은 루틴이 실행되기 전에 처음으로 실행되는 함수로 어셈블리 형식으로 작성되었습니다.
 
 
69 .globl ThreadRoot

70 .ent ThreadRoot,0

71 ThreadRoot:

72 or fp,z,z # Clearing the frame pointer here

73 # makes gdb backtraces of thread stacks

74 # end here (I hope!)

75

76 jal StartupPC # call startup procedure

77 move a0, InitialArg

78 jal InitialPC # call main procedure

79 jal WhenDonePC # when we are done, call clean up procedure

80

81 # NEVER REACHED

82 .end ThreadRoo


 
main 스레드를 제외한 모든 스레드는 ThreadRoot에서 실행됩니다.구문은 다음과 같습니다.
ThreadRoot(intInitialPC, int InitialArg, int WhenDonePC,int StartupPC)
여기서 InitialPC는 새로 생성된 스레드의 입구 함수 주소를 가리키며, InitialArg은 이 입구 함수의 매개 변수이다.StartupPC는 이 라인을 실행하는 데 필요한 초기화 작업이InterruptEnable 함수를 가리킨다.예를 들어 끊기;WhenDonePC는 이 라인의 운행이 끝날 때 필요한 후속 작업으로ThreadFinish 함수를 가리킨다.Nachos의 소스 코드에서 ThreadRoot 함수를 명시적으로 호출하는 함수와 방법은 없습니다. ThreadRoot 함수는 스레드가 전환될 때만 호출됩니다.
한 라인이 초기화되는 마지막 준비 작업에서 StackAllocate 방법을 호출합니다. 이 방법은 몇 개의 레지스터 값을 설정합니다. (InterruptEnable 함수 바늘, ThreadFinish 함수 바늘, 그리고 이 라인이 함수를 실행해야 하는 함수 바늘과 함수를 실행하는 매개 변수) 이 라인이 처음 실행될 때 호출되는 것이 ThreadRoot 함수입니다.그 작업 과정은 다음과 같다.
     1.InterruptEnable 함수를 가리키는 StartupPC 함수를 호출합니다.인터럽트 실행하기;
     2.InitialPC 함수를 호출하여 스레드 입구 함수를 가리킵니다.
     3.WhenDonePC 함수를 호출하고 이 함수는CurrentThread->Finish()를 호출하여 라인의 운행을 종료합니다.
Nachos의 스레드 컨텍스트 전환은 Scheduler의 Run 함수를 호출하여 수행됩니다.
 
 
90 void

91 Scheduler::Run (Thread *nextThread)

92 {

93 Thread *oldThread = currentThread;

94

22

95 #ifdef USER_PROGRAM // ignore until running user programs

96 if (currentThread->space != NULL) { // if this thread is a user program,

97 currentThread->SaveUserState(); // save the user’s CPU registers

98 currentThread->space->SaveState();

99 }

100 #endif

101

102 oldThread->CheckOverflow(); // check if the old thread

103 // had an undetected stack overflow

104

105 currentThread = nextThread; // switch to the next thread

106 currentThread->setStatus(RUNNING); // nextThread is now running

107

108 DEBUG(’t’, "Switching from thread \"%s\" to thread \"%s\"
", 109 oldThread->getName(), nextThread->getName()); 110 111 // This is a machine-dependent assembly language routine defined 112 // in switch.s. You may have to think 113 // a bit to figure out what happens after this, both from the point 114 // of view of the thread and from the perspective of the "outside world". 115 116 SWITCH(oldThread, nextThread); 117 118 DEBUG(’t’, "Now in thread \"%s\"
", currentThread->getName()); 119 120 // If the old thread gave up the processor because it was finishing, 121 // we need to delete its carcass. Note we cannot delete the thread 122 // before now (for example, in Thread::Finish()), because up to this 123 // point, we were still running on the old thread’s stack! 124 if (threadToBeDestroyed != NULL) { 125 delete threadToBeDestroyed; 126 threadToBeDestroyed = NULL; 127 } 128 129 #ifdef USER_PROGRAM 130 if (currentThread->space != NULL) { // if there is an address space 131 currentThread->RestoreUserState(); // to restore, do it. 132 currentThread->space->RestoreState(); 133 } 134 #endif 135 }

 
이 함수는 먼저 oldThread를 설정하고 매개 변수를currentThread로 설정합니다.그런 다음 SWITCH를 호출하여 스레드 컨텍스트 전환을 수행합니다.SWITCH는 어셈블리로 구현됩니다.이 함수는 우선 모든 중요한 레지스터의 내용을 현재 라인의 제어 블록에 저장합니다.Thread류의 첫 번째 개인 데이터 구성원은 StackTop이고 그 다음은 Machine State 그룹이다.다시 말하면 Thread 대상을 가리키는 바늘도 StackTop을 가리키는 것이다.그것들의 위치는 매우 중요하기 때문에 바꾸거나 뒤집어서는 안 된다.
한 라인이 cpu 시간을 다시 얻을 때, StackTop과 Machine State에 저장된 모든 값은 레지스터에 불러옵니다. 되돌아오는 주소를 저장하는 라도 포함됩니다.
Run 함수는 내부 핵에 속합니다. 라인의 상하문을 전환하고 새 라인을 실행한 후에 돌아옵니다.
Nachos 커널에 정의된 글로벌 변수에 대해 살펴보겠습니다.threads/system.cc 디렉터리에 최소한 6개의 전역 변수가 정의되어 있습니다.
다음과 같습니다.
 
 
Thread *currentThread; //  

Thread *threadToBeDestroyed; //  

 Scheduler *scheduler; // 

Interrupt *interrupt; // 。

 Statistics *stats; // 。

 Timer *timer; //  , 。

#ifdef FILESYS_NEEDED

23 FileSystem *fileSystem;

24 #endif

25

26 #ifdef FILESYS

27 SynchDisk *synchDisk;

28 #endif

29

30 #ifdef USER_PROGRAM // requires either FILESYS or FILESYS_STUB

31 Machine *machine; // user program memory and registers

32 #endif

33

34 #ifdef NETWORK

35 PostOffice *postOffice;

36 #endif


 
currentThread는 현재 실행 스레드를 가리킵니다.scheduler는 스레드 스케줄링 및 준비 대기열 관리를 담당하는 커널의 Scheduler 클래스 객체를 가리킵니다.
이러한 글로벌 변수는 Nachos 시스템이 시작될 때 생성됩니다.이러한 변수를 만드는 함수는 다음과 같습니다.
Initialize(int argc, char **argv)
ThreadFinish는 라인이 라인 입구 함수에서 되돌아올 때 호출됩니다.thread 클래스의Finish 함수만 호출합니다.Finish 함수에서threadToBeDestroyed를currentThread로 설정합니다.이후 원래 노선의 정리 작업은 새로운 노선에 맡겼다.SWITCH에서 스레드 컨텍스트 전환을 완료하면 다음과 같은 코드가 표시됩니다.
 
 
24 if (threadToBeDestroyed != NULL) {

125 delete threadToBeDestroyed;

126 threadToBeDestroyed = NULL;

127 }


 
이 코드들은 새 라인에서 청소 작업을 수행하는 것이다.
스레드 상하문 전환을 실행할 때마다 새 스레드는threadToBeDestroyed를 검사하여 오래된 스레드를 제거합니다.
이제 Nachos 커널에 대해 살펴보겠습니다.Nachos 커널은 Nachos 운영 체제의 일부입니다.내부 핵 자체도 하나의 프로그램이며main() 함수도 있어야 한다.threads/main.cc 파일에서main 함수를 찾을 수 있습니다.main 함수는 주로 다음과 같은 작업을 수행합니다.
1: Initialize() 호출;
2: ThreadTest() 호출;
     3:currentThread->Finish();
Initialize는 글로벌 변수를 생성하고 초기화하는 작업을 수행합니다.
 
ThreadTest는 테스트 함수입니다.정의는 다음과 같습니다.
 
 
41 void

42 ThreadTest()

43 {

44 DEBUG(’t’, "Entering SimpleTest");

45

46 Thread *t = new Thread("forked thread");

47

48 t->Fork(SimpleThread, 1);

49 SimpleThread(0);

50 }


 
이 함수는 심플Thread 함수를 실행하기 위해forked thread라는 스레드를 생성합니다.
SimpleThread 정의는 다음과 같습니다.
 
24 void

25 SimpleThread(int which)

26 {

27    int num;

28

29    for (num = 0; num < 5; num++) {

30    printf("*** thread %d looped %d times
", which, num); 31 currentThread->Yield(); 32 } 33 }

 
주 스레드와 새 스레드는 모두 SimpleThread 함수를 실행합니다.그것들은 끝날 때까지 끊임없이 라인의 상하문 전환을 한다.
main 함수의 마지막 호출 함수는currentThread->Finish()입니다.이 함수는 결코 되돌아오지 않을 것이다.라인의 상하문이 바뀐 후, 이 라인은 새로운 라인에 의해 방출되기 때문이다.Nachos study book에서 번역한 바와 같이 실수가 있으면 아낌없이 가르쳐 주십시오!!
2013.1.1 산시성

좋은 웹페이지 즐겨찾기