/** * 스캔 코드를 내부적으로 사용하는 키 데이터로 바꾼 후 키 큐에 삽입 */ BOOL kConvertScanCodeAndPutQueue( BYTE bScanCode ) { KEYDATA stData; BOOL bResult = FALSE; BOOL bPreviousInterrupt; // 스캔 코드를 키 데이터에 삽입 stData.bScanCode = bScanCode; // 스캔 코드를 ASCII 코드와 키 상태로 변환하여 키 데이터에 삽입 if( kConvertScanCodeToASCIICode( bScanCode, &( stData.bASCIICode ), &( stData.bFlags ) ) == TRUE ) { // 임계 영역 시작 bPreviousInterrupt = kLockForSystemData(); // 키 큐에 삽입 bResult = kPutQueue( &gs_stKeyQueue, &stData ); // 임계 영역 끝 kUnlockForSystemData( bPreviousInterrupt ); } return bResult; }
/** * 인터럽트가 발생했을 때, 다른 태스크를 찾아 전환 * 반드시 인터럽트나 예외가 발생했을 때 호출해야 함 */ BOOL kScheduleInInterrupt( void ) { TCB* pstRunningTask, * pstNextTask; char* pcContextAddress; BOOL bPreviousFlag; // 임계 영역 시작 bPreviousFlag = kLockForSystemData(); // 전환할 태스크가 없으면 종료 pstNextTask = kGetNextTaskToRun(); if( pstNextTask == NULL ) { // 임계 영역 끝 kUnlockForSystemData( bPreviousFlag ); return FALSE; } //========================================================================== // 태스크 전환 처리 // 인터럽트 핸들러에서 저장한 콘텍스트를 다른 콘텍스트로 덮어쓰는 방법으로 처리 //========================================================================== pcContextAddress = ( char* ) IST_STARTADDRESS + IST_SIZE - sizeof( CONTEXT ); // 현재 수행중인 태스크의 정보를 수정한 뒤 콘텍스트 전환 pstRunningTask = gs_stScheduler.pstRunningTask; gs_stScheduler.pstRunningTask = pstNextTask; // 유휴 태스크에서 전환되었다면 사용한 Tick Count를 증가시킴 if( ( pstRunningTask->qwFlags & TASK_FLAGS_IDLE ) == TASK_FLAGS_IDLE ) { gs_stScheduler.qwSpendProcessorTimeInIdleTask += TASK_PROCESSORTIME; } // 태스크 종료 플래그가 설정된 경우, 콘텍스트를 저장하지 않고 대기 리스트에만 삽입 if( pstRunningTask->qwFlags & TASK_FLAGS_ENDTASK ) { kAddListToTail( &( gs_stScheduler.stWaitList ), pstRunningTask ); } // 태스크가 종료되지 않으면 IST에 있는 콘텍스트를 복사하고, 현재 태스크를 준비 리스트로 // 옮김 else { kMemCpy( &( pstRunningTask->stContext ), pcContextAddress, sizeof( CONTEXT ) ); kAddTaskToReadyList( pstRunningTask ); } // 임계 영역 끝 kUnlockForSystemData( bPreviousFlag ); // 전환해서 실행할 태스크를 Running Task로 설정하고 콘텍스트를 IST에 복사해서 // 자동으로 태스크 전환이 일어나도록 함 kMemCpy( pcContextAddress, &( pstNextTask->stContext ), sizeof( CONTEXT ) ); // 프로세서 사용 시간을 업데이트 gs_stScheduler.iProcessorTime = TASK_PROCESSORTIME; return TRUE; }
/** * 다른 태스크를 찾아서 전환 * 인터럽트나 예외가 발생했을 때 호출하면 안됨 */ 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 ); }
/** * 현재 수행 중인 태스크를 설정 */ void kSetRunningTask( TCB* pstTask ) { BOOL bPreviousFlag; // 임계 영역 시작 bPreviousFlag = kLockForSystemData(); gs_stScheduler.pstRunningTask = pstTask; // 임계 영역 끝 kUnlockForSystemData( bPreviousFlag ); }
/** * 태스크를 종료 */ BOOL kEndTask( QWORD qwTaskID ) { TCB* pstTarget; BYTE bPriority; BOOL bPreviousFlag; // 임계 영역 시작 bPreviousFlag = kLockForSystemData(); // 현재 실행중인 태스크이면 EndTask 비트를 설정하고 태스크를 전환 pstTarget = gs_stScheduler.pstRunningTask; if( pstTarget->stLink.qwID == qwTaskID ) { pstTarget->qwFlags |= TASK_FLAGS_ENDTASK; SETPRIORITY( pstTarget->qwFlags, TASK_FLAGS_WAIT ); // 임계 영역 끝 kUnlockForSystemData( bPreviousFlag ); kSchedule(); // 태스크가 전환 되었으므로 아래 코드는 절대 실행되지 않음 while( 1 ) ; } // 실행 중인 태스크가 아니면 준비 큐에서 직접 찾아서 대기 리스트에 연결 else { // 준비 리스트에서 태스크를 찾지 못하면 직접 태스크를 찾아서 태스크 종료 비트를 // 설정 pstTarget = kRemoveTaskFromReadyList( qwTaskID ); if( pstTarget == NULL ) { // 태스크 ID로 직접 찾아서 설정 pstTarget = kGetTCBInTCBPool( GETTCBOFFSET( qwTaskID ) ); if( pstTarget != NULL ) { pstTarget->qwFlags |= TASK_FLAGS_ENDTASK; SETPRIORITY( pstTarget->qwFlags, TASK_FLAGS_WAIT ); } // 임계 영역 끝 kUnlockForSystemData( bPreviousFlag ); return TRUE; } pstTarget->qwFlags |= TASK_FLAGS_ENDTASK; SETPRIORITY( pstTarget->qwFlags, TASK_FLAGS_WAIT ); kAddListToTail( &( gs_stScheduler.stWaitList ), pstTarget ); } // 임계 영역 끝 kUnlockForSystemData( bPreviousFlag ); return TRUE; }
/** * 키 큐에서 데이터를 제거 */ BOOL kGetKeyFromKeyQueue( KEYDATA* pstData ) { BOOL bResult; BOOL bPreviousInterrupt; // 임계 영역 시작 bPreviousInterrupt = kLockForSystemData(); bResult = kGetQueue( &gs_stKeyQueue, pstData ); // 임계 영역 끝 kUnlockForSystemData( bPreviousInterrupt ); return bResult; }
/** * 태스크를 생성 * 태스크 ID에 따라서 스택 풀에서 스택 자동 할당 */ TCB* kCreateTask( QWORD qwFlags, QWORD qwEntryPointAddress ) { TCB* pstTask; void* pvStackAddress; BOOL bPreviousFlag; // 임계 영역 시작 bPreviousFlag = kLockForSystemData(); pstTask = kAllocateTCB(); if( pstTask == NULL ) { // 임계영역 끝 kUnlockForSystemData( bPreviousFlag ); return NULL; } // 임계 영역 끝 kUnlockForSystemData( bPreviousFlag ); // 태스크 ID로 스택 어드레스 계산, 하위 32비트가 스택 풀의 오프셋 역할 수행 pvStackAddress = ( void* ) ( TASK_STACKPOOLADDRESS + ( TASK_STACKSIZE * GETTCBOFFSET( pstTask->stLink.qwID ) ) ); // TCB를 설정한 후 준비 리스트에 삽입하여 스케줄링될 수 있도록 함 kSetUpTask( pstTask, qwFlags, qwEntryPointAddress, pvStackAddress, TASK_STACKSIZE ); // 임계 영역 시작 bPreviousFlag = kLockForSystemData(); // 태스크를 준비 리스트에 삽입 kAddTaskToReadyList( pstTask ); // 임계 영역 끝 kUnlockForSystemData( bPreviousFlag ); return pstTask; }
/** * 현재 수행 중인 태스크를 반환 */ TCB* kGetRunningTask( void ) { BOOL bPreviousFlag; TCB* pstRunningTask; // 임계 영역 시작 bPreviousFlag = kLockForSystemData(); pstRunningTask = gs_stScheduler.pstRunningTask; // 임계 영역 끝 kUnlockForSystemData( bPreviousFlag ); return pstRunningTask; }
/** * 태스크의 우선 순위를 변경함 */ BOOL kChangePriority( QWORD qwTaskID, BYTE bPriority ) { TCB* pstTarget; BOOL bPreviousFlag; if( bPriority > TASK_MAXREADYLISTCOUNT ) { return FALSE; } // 임계 영역 시작 bPreviousFlag = kLockForSystemData(); // 현재 실행중인 태스크이면 우선 순위만 변경 // PIT 컨트롤러의 인터럽트(IRQ 0)가 발생하여 태스크 전환이 수행될 때 변경된 // 우선 순위의 리스트로 이동 pstTarget = gs_stScheduler.pstRunningTask; if( pstTarget->stLink.qwID == qwTaskID ) { SETPRIORITY( pstTarget->qwFlags, bPriority ); } // 실행중인 태스크가 아니면 준비 리스트에서 찾아서 해당 우선 순위의 리스트로 이동 else { // 준비 리스트에서 태스크를 찾지 못하면 직접 태스크를 찾아서 우선 순위를 설정 pstTarget = kRemoveTaskFromReadyList( qwTaskID ); if( pstTarget == NULL ) { // 태스크 ID로 직접 찾아서 설정 pstTarget = kGetTCBInTCBPool( GETTCBOFFSET( qwTaskID ) ); if( pstTarget != NULL ) { // 우선 순위를 설정 SETPRIORITY( pstTarget->qwFlags, bPriority ); } } else { // 우선 순위를 설정하고 준비 리스트에 다시 삽입 SETPRIORITY( pstTarget->qwFlags, bPriority ); kAddTaskToReadyList( pstTarget ); } } // 임계 영역 끝 kUnlockForSystemData( bPreviousFlag ); return TRUE; }
BOOL kGetKeyFromKeyQueue( KEYDATA* pstData ) { BOOL bResult; BOOL bPreviousFlag; if( kIsQueueEmpty( &gs_stKeyQueue ) == TRUE ) { return FALSE; } bPreviousFlag = kLockForSystemData(); bResult = kGetQueue( &gs_stKeyQueue, pstData ); kUnlockForSystemData( bPreviousFlag ); return bResult; }
BOOL kConvertScanCodeAndPutQueue( BYTE bScanCode ) { KEYDATA stData; BOOL bResult; BOOL bPreviousFlag; stData.bScanCode = bScanCode; if( kConvertScanCodeToASCIICode( bScanCode, &( stData.bASCIICode ), &( stData.bFlags ) ) ) { bPreviousFlag = kLockForSystemData(); bResult = kPutQueue( &gs_stKeyQueue, &stData ); kUnlockForSystemData( bPreviousFlag ); } return bResult; }
/** * 전체 태스크의 수를 반환 */ int kGetTaskCount( void ) { int iTotalCount; BOOL bPreviousFlag; // 준비 큐의 태스크 수를 구한 후, 대기 큐의 태스크 수와 현재 수행 중인 태스크 수를 더함 iTotalCount = kGetReadyTaskCount(); // 임계 영역 시작 bPreviousFlag = kLockForSystemData(); iTotalCount += kGetListCount( &( gs_stScheduler.stWaitList ) ) + 1; // 임계 영역 끝 kUnlockForSystemData( bPreviousFlag ); return iTotalCount; }
/** * 준비 큐에 있는 모든 태스크의 수를 반환 */ int kGetReadyTaskCount( void ) { int iTotalCount = 0; int i; BOOL bPreviousFlag; // 임계 영역 시작 bPreviousFlag = kLockForSystemData(); // 모든 준비 큐를 확인하여 태스크 개수를 구함 for( i = 0 ; i < TASK_MAXREADYLISTCOUNT ; i++ ) { iTotalCount += kGetListCount( &( gs_stScheduler.vstReadyList[ i ] ) ); } // 임계 영역 끝 kUnlockForSystemData( bPreviousFlag ); return iTotalCount ; }
/** * 키 큐에서 키 데이터를 제거 */ BOOL kGetKeyFromKeyQueue( KEYDATA* pstData ) { BOOL bResult; BOOL bPreviousInterrupt; // 큐가 비었으면 키 데이터를 꺼낼 수 없음 if( kIsQueueEmpty( &gs_stKeyQueue ) == TRUE ) { return FALSE; } // 임계 영역 시작 bPreviousInterrupt = kLockForSystemData(); // 키 큐에서 키 데이터를 제거 bResult = kGetQueue( &gs_stKeyQueue, pstData ); // 임계 영역 끝 kUnlockForSystemData( bPreviousInterrupt ); return bResult; }
/** * 유휴 태스크 * 대기 큐에 삭제 대기중인 태스크를 정리 */ void kIdleTask( void ) { TCB* pstTask; QWORD qwLastMeasureTickCount, qwLastSpendTickInIdleTask; QWORD qwCurrentMeasureTickCount, qwCurrentSpendTickInIdleTask; BOOL bPreviousFlag; QWORD qwTaskID; // 프로세서 사용량 계산을 위해 기준 정보를 저장 qwLastSpendTickInIdleTask = gs_stScheduler.qwSpendProcessorTimeInIdleTask; qwLastMeasureTickCount = kGetTickCount(); while( 1 ) { // 현재 상태를 저장 qwCurrentMeasureTickCount = kGetTickCount(); qwCurrentSpendTickInIdleTask = gs_stScheduler.qwSpendProcessorTimeInIdleTask; // 프로세서 사용량을 계산 // 100 - ( 유휴 태스크가 사용한 프로세서 시간 ) * 100 / ( 시스템 전체에서 // 사용한 프로세서 시간 ) if( qwCurrentMeasureTickCount - qwLastMeasureTickCount == 0 ) { gs_stScheduler.qwProcessorLoad = 0; } else { gs_stScheduler.qwProcessorLoad = 100 - ( qwCurrentSpendTickInIdleTask - qwLastSpendTickInIdleTask ) * 100 /( qwCurrentMeasureTickCount - qwLastMeasureTickCount ); } // 현재 상태를 이전 상태에 보관 qwLastMeasureTickCount = qwCurrentMeasureTickCount; qwLastSpendTickInIdleTask = qwCurrentSpendTickInIdleTask; // 프로세서의 부하에 따라 쉬게 함 kHaltProcessorByLoad(); // 대기 큐에 대기중인 태스크가 있으면 태스크를 종료함 if( kGetListCount( &( gs_stScheduler.stWaitList ) ) >= 0 ) { while( 1 ) { // 임계 영역 시작 bPreviousFlag = kLockForSystemData(); pstTask = kRemoveListFromHeader( &( gs_stScheduler.stWaitList ) ); if( pstTask == NULL ) { // 임계 영역 끝 kUnlockForSystemData( bPreviousFlag ); break; } qwTaskID = pstTask->stLink.qwID; kFreeTCB( qwTaskID ); // 임계 영역 끝 kUnlockForSystemData( bPreviousFlag ); kPrintf( "IDLE: Task ID[0x%q] is completely ended.\n", qwTaskID ); } } kSchedule(); } }