/** * 다른 태스크를 찾아서 전환 * 인터럽트나 예외가 발생했을 때 호출하면 안됨 */ void kSchedule( void ) { TCB* pstRunningTask, * pstNextTask; BOOL bPreviousFlag; // 전환할 태스크가 있어야 함 if( kGetReadyTaskCount() < 1 ) { return ; } // 전환하는 도중 인터럽트가 발생하여 태스크 전환이 또 일어나면 곤란하므로 전환하는 // 동안 인터럽트가 발생하지 못하도록 설정 // 임계 영역 시작 bPreviousFlag = kLockForSystemData(); // 실행할 다음 태스크를 얻음 pstNextTask = kGetNextTaskToRun(); if( pstNextTask == NULL ) { // 임계 영역 끝 kUnlockForSystemData( bPreviousFlag ); return ; } // 현재 수행중인 태스크의 정보를 수정한 뒤 콘텍스트 전환 pstRunningTask = gs_stScheduler.pstRunningTask; gs_stScheduler.pstRunningTask = pstNextTask; // 유휴 태스크에서 전환되었다면 사용한 프로세서 시간을 증가시킴 if( ( pstRunningTask->qwFlags & TASK_FLAGS_IDLE ) == TASK_FLAGS_IDLE ) { gs_stScheduler.qwSpendProcessorTimeInIdleTask += TASK_PROCESSORTIME - gs_stScheduler.iProcessorTime; } // 태스크 종료 플래그가 설정된 경우 콘텍스트를 저장할 필요가 없으므로, 대기 리스트에 // 삽입하고 콘텍스트 전환 if( pstRunningTask->qwFlags & TASK_FLAGS_ENDTASK ) { kAddListToTail( &( gs_stScheduler.stWaitList ), pstRunningTask ); kSwitchContext( NULL, &( pstNextTask->stContext ) ); } else { kAddTaskToReadyList( pstRunningTask ); kSwitchContext( &( pstRunningTask->stContext ), &( pstNextTask->stContext ) ); } // 프로세서 사용 시간을 업데이트 gs_stScheduler.iProcessorTime = TASK_PROCESSORTIME; // 임계 영역 끝 kUnlockForSystemData( bPreviousFlag ); }
// Test for context switching void kTestTask(void) { int i = 0; while (1) { // Print message and wait for key input kPrintf("[%d] This message is from kTestTask. Press any key to switch " "kConsolShell!!\n", i++); kGetCh(); // Context switching kSwitchContext(&(gs_vstTask[1].stContext), &(gs_vstTask[0].stContext)); } }
/** * 태스크 전환을 테스트하는 태스크 */ void kTestTask( void ) { int i = 0; while( 1 ) { // 메시지를 출력하고 키 입력을 대기 kPrintf( "[%d] This message is from kTestTask. Press any key to switch " "kConsoleShell~!!\n", i++ ); kGetCh(); // 위에서 키가 입력되면 태스크를 전환 kSwitchContext( &( gs_vstTask[ 1 ].stContext ), &( gs_vstTask[ 0 ].stContext ) ); } }
/** * 태스크를 생성해서 멀티 태스킹 수행 */ void kCreateTestTask( const char* pcParameterBuffer ) { KEYDATA stData; int i = 0; // 태스크 설정 kSetUpTask( &( gs_vstTask[ 1 ] ), 1, 0, ( QWORD ) kTestTask, &( gs_vstStack ), sizeof( gs_vstStack ) ); // 'q' 키가 입력되지 않을 때까지 수행 while( 1 ) { // 메시지를 출력하고 키 입력을 대기 kPrintf( "[%d] This message is from kConsoleShell. Press any key to " "switch TestTask~!!\n", i++ ); if( kGetCh() == 'q' ) { break; } // 위에서 키가 입력되면 태스크를 전환 kSwitchContext( &( gs_vstTask[ 0 ].stContext ), &( gs_vstTask[ 1 ].stContext ) ); } }
/** * 다른 태스크를 찾아서 전환 * 인터럽트나 예외가 발생했을 때 호출하면 안됨 * 단, 인터럽트나 예외에서 태스크를 종료하는 경우는 사용 가능 */ BOOL kSchedule( void ) { TCB* pstRunningTask, * pstNextTask; BOOL bPreviousInterrupt; BYTE bCurrentAPICID; // 전환하는 도중 인터럽트가 발생하여 태스크 전환이 또 일어나면 곤란하므로 전환하는 // 동안 인터럽트가 발생하지 못하도록 설정 bPreviousInterrupt = kSetInterruptFlag( FALSE ); // 현재 로컬 APIC ID 확인 bCurrentAPICID = kGetAPICID(); // 전환할 태스크가 있어야 함 if( kGetReadyTaskCount( bCurrentAPICID ) < 1 ) { kSetInterruptFlag( bPreviousInterrupt ); return FALSE; } // 임계 영역 시작 kLockForSpinLock( &( gs_vstScheduler[ bCurrentAPICID ].stSpinLock ) ); // 실행할 다음 태스크를 얻음 pstNextTask = kGetNextTaskToRun( bCurrentAPICID ); if( pstNextTask == NULL ) { // 임계 영역 끝 kUnlockForSpinLock( &( gs_vstScheduler[ bCurrentAPICID ].stSpinLock ) ); kSetInterruptFlag( bPreviousInterrupt ); return FALSE; } // 현재 수행중인 태스크의 정보를 수정한 뒤 콘텍스트 전환 pstRunningTask = gs_vstScheduler[ bCurrentAPICID ].pstRunningTask; gs_vstScheduler[ bCurrentAPICID ].pstRunningTask = pstNextTask; // 유휴 태스크에서 전환되었다면 사용한 프로세서 시간을 증가시킴 if( ( pstRunningTask->qwFlags & TASK_FLAGS_IDLE ) == TASK_FLAGS_IDLE ) { gs_vstScheduler[ bCurrentAPICID ].qwSpendProcessorTimeInIdleTask += TASK_PROCESSORTIME - gs_vstScheduler[ bCurrentAPICID ].iProcessorTime; } // 다음에 수행할 태스크가 FPU를 쓴 태스크가 아니라면 TS 비트를 설정 if( gs_vstScheduler[ bCurrentAPICID ].qwLastFPUUsedTaskID != pstNextTask->stLink.qwID ) { kSetTS(); } else { kClearTS(); } // 태스크 종료 플래그가 설정된 경우 콘텍스트를 저장할 필요가 없으므로, 대기 리스트에 // 삽입하고 콘텍스트 전환 if( pstRunningTask->qwFlags & TASK_FLAGS_ENDTASK ) { kAddListToTail( &( gs_vstScheduler[ bCurrentAPICID ].stWaitList ), pstRunningTask ); // 임계 영역 끝 kUnlockForSpinLock( &( gs_vstScheduler[ bCurrentAPICID ].stSpinLock ) ); // 태스크 전환 kSwitchContext( NULL, &( pstNextTask->stContext ) ); } else { kAddTaskToReadyList( bCurrentAPICID, pstRunningTask ); // 임계 영역 끝 kUnlockForSpinLock( &( gs_vstScheduler[ bCurrentAPICID ].stSpinLock ) ); // 태스크 전환 kSwitchContext( &( pstRunningTask->stContext ), &( pstNextTask->stContext ) ); } // 프로세서 사용 시간을 업데이트 gs_vstScheduler[ bCurrentAPICID ].iProcessorTime = TASK_PROCESSORTIME; // 인터럽트 플래그 복원 kSetInterruptFlag( bPreviousInterrupt ); return FALSE; }