// Task 2 // Along ID, print rotating character void kTestTask2(void) { int i = 0; int iOffset; CHARACTER * pstScreen = (CHARACTER *) CONSOLE_VIDEOMEMORYADDRESS; TCB * pstRunningTask; char vcData[4] = {'-', '\\', '|', '/'}; // Get ID and use is as offset of monitor pstRunningTask = kGetRunningTask(); iOffset = (pstRunningTask->stLink.qwID & 0xFFFFFFFF) * 2; iOffset = CONSOLE_WIDTH * CONSOLE_HEIGHT - (iOffset % (CONSOLE_WIDTH * CONSOLE_HEIGHT)); while (1) { // Set rotating character pstScreen[iOffset].bCharactor = vcData[i % 4]; // Set Color pstScreen[iOffset].bAttribute = (iOffset % 15) + 1; i++; // Switch task kSchedule(); } }
/** * 태스크 2 * 자신의 ID를 참고하여 특정 위치에 회전하는 바람개비를 출력 */ void kTestTask2( void ) { int i = 0, iOffset; CHARACTER* pstScreen = ( CHARACTER* ) CONSOLE_VIDEOMEMORYADDRESS; TCB* pstRunningTask; char vcData[ 4 ] = { '-', '\\', '|', '/' }; // 자신의 ID를 얻어서 화면 오프셋으로 사용 pstRunningTask = kGetRunningTask(); iOffset = ( pstRunningTask->stLink.qwID & 0xFFFFFFFF ) * 2; iOffset = CONSOLE_WIDTH * CONSOLE_HEIGHT - ( iOffset % ( CONSOLE_WIDTH * CONSOLE_HEIGHT ) ); while( 1 ) { // 회전하는 바람개비를 표시 pstScreen[ iOffset ].bCharactor = vcData[ i % 4 ]; // 색깔 지정 pstScreen[ iOffset ].bAttribute = ( iOffset % 15 ) + 1; i++; // 다른 태스크로 전환 kSchedule(); } }
// 태스크 사이에서 사용하는 데이터를 위한 잠근 해제 함수 void kUnlock(MUTEX* pstMutex) { BOOL bInterruptFlag; // 인터럽트를 비활성화 bInterruptFlag = kSetInterruptFlag(FALSE); // 뮤텍스를 잠근 태스크가 아니면 실패 if((pstMutex->bLockFlag==FALSE)||(pstMutex->qwTaskID!=kGetRunningTask(kGetAPICID())->stLink.qwID)) { // 인터럽트를 복원 kSetInterruptFlag(bInterruptFlag); return ; } // 뮤텍스를 중복으로 잠갔으면 잠긴 횟수만 감소 if(pstMutex->dwLockCount>1) { pstMutex->dwLockCount--; } else { // 해제된 것으로 설정, 잠긴 플래그는 가장 나중에 해제해야함 pstMutex->qwTaskID = TASK_INVALIDID; pstMutex->dwLockCount = 0; pstMutex->bLockFlag=FALSE; } // 인터럽트를 복원 kSetInterruptFlag(bInterruptFlag); }
/** * 태스크 1 * 화면 테두리를 돌면서 문자를 출력 */ static void kTestTask1( void ) { BYTE bData; int i = 0, iX = 0, iY = 0, iMargin, j; CHARACTER* pstScreen = ( CHARACTER* ) CONSOLE_VIDEOMEMORYADDRESS; TCB* pstRunningTask; // 자신의 ID를 얻어서 화면 오프셋으로 사용 pstRunningTask = kGetRunningTask(); iMargin = ( pstRunningTask->stLink.qwID & 0xFFFFFFFF ) % 10; // 화면 네 귀퉁이를 돌면서 문자 출력 for( j = 0 ; j < 20000 ; j++ ) { switch( i ) { case 0: iX++; if( iX >= ( CONSOLE_WIDTH - iMargin ) ) { i = 1; } break; case 1: iY++; if( iY >= ( CONSOLE_HEIGHT - iMargin ) ) { i = 2; } break; case 2: iX--; if( iX < iMargin ) { i = 3; } break; case 3: iY--; if( iY < iMargin ) { i = 0; } break; } // 문자 및 색깔 지정 pstScreen[ iY * CONSOLE_WIDTH + iX ].bCharactor = bData; pstScreen[ iY * CONSOLE_WIDTH + iX ].bAttribute = bData & 0x0F; bData++; // 다른 태스크로 전환 //kSchedule(); } //kExitTask(); }
/** * Device Not Available 예외의 핸들러 */ void kDeviceNotAvailableHandler( int iVectorNumber ) { TCB* pstFPUTask, * pstCurrentTask; QWORD qwLastFPUTaskID; BYTE bCurrentAPICID; //========================================================================= // FPU 예외가 발생했음을 알리려고 메시지를 출력하는 부분 char vcBuffer[] = "[EXC: , ]"; static int g_iFPUInterruptCount = 0; // 예외 벡터를 화면 오른쪽 위에 2자리 정수로 출력 vcBuffer[ 5 ] = '0' + iVectorNumber / 10; vcBuffer[ 6 ] = '0' + iVectorNumber % 10; // 발생한 횟수 출력 vcBuffer[ 8 ] = '0' + g_iFPUInterruptCount; g_iFPUInterruptCount = ( g_iFPUInterruptCount + 1 ) % 10; kPrintStringXY( 0, 0, vcBuffer ); //========================================================================= // 현재 코어의 로컬 APIC ID를 확인 bCurrentAPICID = kGetAPICID(); // CR0 컨트롤 레지스터의 TS 비트를 0으로 설정 kClearTS(); // 이전에 FPU를 사용한 태스크가 있는지 확인하여, 있다면 FPU의 상태를 태스크에 저장 qwLastFPUTaskID = kGetLastFPUUsedTaskID( bCurrentAPICID ); pstCurrentTask = kGetRunningTask( bCurrentAPICID ); // 이전에 FPU를 사용한 것이 자신이면 아무것도 안 함 if( qwLastFPUTaskID == pstCurrentTask->stLink.qwID ) { return ; } // FPU를 사용한 태스크가 있으면 FPU 상태를 저장 else if( qwLastFPUTaskID != TASK_INVALIDID ) { pstFPUTask = kGetTCBInTCBPool( GETTCBOFFSET( qwLastFPUTaskID ) ); if( ( pstFPUTask != NULL ) && ( pstFPUTask->stLink.qwID == qwLastFPUTaskID ) ) { kSaveFPUContext( pstFPUTask->vqwFPUContext ); } } // 현재 태스크가 FPU를 사용한 적이 있는 지 확인하여 FPU를 사용한 적이 없다면 // 초기화하고, 사용한적이 있다면 저장된 FPU 콘텍스트를 복원 if( pstCurrentTask->bFPUUsed == FALSE ) { kInitializeFPU(); pstCurrentTask->bFPUUsed = TRUE; } else { kLoadFPUContext( pstCurrentTask->vqwFPUContext ); } // FPU를 사용한 태스크 ID를 현재 태스크로 변경 kSetLastFPUUsedTaskID( bCurrentAPICID, pstCurrentTask->stLink.qwID ); }
// Task 1 // Print character around monitor void kTestTask1(void) { BYTE bData; int i = 0, iX = 0, iY = 0, iMargin; CHARACTER * pstScreen = (CHARACTER *)CONSOLE_VIDEOMEMORYADDRESS; TCB * pstRunningTask; // Get ID and use is as offset of monitor pstRunningTask = kGetRunningTask(); iMargin = (pstRunningTask->stLink.qwID & 0xFFFFFFFF) % 10; while (1) { switch (i) { case 0: iX++; if ( iX >= (CONSOLE_WIDTH - iMargin) ) { i = 1; } break; case 1: iY++; if ( iY >= (CONSOLE_HEIGHT - iMargin) ) { i = 2; } break; case 2: iX--; if ( iX < iMargin ) { i = 3; } break; case 3: iY--; if ( iY < iMargin ) { i = 0; } break; } // Set characture and color pstScreen[iY * CONSOLE_WIDTH + iX].bCharactor = bData; pstScreen[iY * CONSOLE_WIDTH + iX].bAttribute = bData & 0x0F; bData++; // Switch task kSchedule(); } }
/** * 태스크 사이에서 사용하는 데이터를 위한 잠금 함수 */ void kLock( MUTEX* pstMutex ) { // 이미 잠겨 있다면 내가 잠갔는지 확인하고 잠근 횟수를 증가시킨 뒤 종료 if( kTestAndSet(&( pstMutex->bLockFlag ), 0, 1 ) == FALSE ) { // 자신이 잠갔다면 횟수만 증가시킴 if( pstMutex->qwTaskID == kGetRunningTask()->stLink.qwID ) { pstMutex->dwLockCount++; return ; } // 자신이 아닌 경우는 잠긴 것이 해제될 때까지 대기 while( kTestAndSet( &( pstMutex->bLockFlag ), 0, 1 ) == FALSE ) { kSchedule(); } } // 잠김 설정, 잠김 플래그는 위의 kTestAndSet() 함수에서 처리함 pstMutex->dwLockCount = 1; pstMutex->qwTaskID = kGetRunningTask()->stLink.qwID; }
// 태스크 사이에서 사용하는 데이터를 위한 잠금 함수 void kLock(MUTEX* pstMutex) { BYTE bCurrentAPICID; BOOL bInterruptFlag; // 인터럽트를 비활성화 bInterruptFlag = kSetInterruptFlag(FALSE); // 현재 코어의 로컬 APIC ID를 확인 bCurrentAPICID = kGetAPICID(); // 이미 잠겨 있다면 내가 잠갔는지 확인하고 잠근 횟수를 증가시킨 뒤 종료 if(kTestAndSet(&(pstMutex->bLockFlag),0,1)==FALSE) { // 자신이 잠겼다면 횟수만 증가시킴 if(pstMutex->qwTaskID==kGetRunningTask(bCurrentAPICID)->stLink.qwID) { // 인터럽트를 복원 kSetInterruptFlag(bInterruptFlag); pstMutex->dwLockCount++; return; } // 자신이 아닌 경우는 잠긴 것이 해제될 때까지 대기 while(kTestAndSet(&(pstMutex->bLockFlag),0,1)==FALSE) { kSchedule(); } } // 잠금 설정, 잠긴 플래그는 위의 kTestAndSet() 함수에서 처리함 pstMutex->dwLockCount =1; pstMutex->qwTaskID=kGetRunningTask(bCurrentAPICID)->stLink.qwID; // 인터럽트를 복원 kSetInterruptFlag(bInterruptFlag); }
/** * 공통으로 사용하는 예외 핸들러 */ void kCommonExceptionHandler( int iVectorNumber, QWORD qwErrorCode ) { char vcBuffer[ 100 ]; BYTE bAPICID; TCB* pstTask; // 현재 예외가 발생한 코어를 반환 bAPICID = kGetAPICID(); // 현재 코어에서 실행 중인 태스크를 반환 pstTask = kGetRunningTask( bAPICID ); // 메시지 출력 kPrintStringXY( 0, 0, "====================================================" ); kPrintStringXY( 0, 1, " Exception Occur~!!!! " ); // 예외 벡터와 코어 ID, 에러코드를 출력 kSPrintf( vcBuffer, " Vector:%d Core ID:0x%X ErrorCode:0x%X ", iVectorNumber, bAPICID, qwErrorCode ); kPrintStringXY( 0, 2, vcBuffer ); // 태스크 ID를 출력 kSPrintf( vcBuffer, " Task ID:0x%Q", pstTask->stLink.qwID ); kPrintStringXY( 0, 3, vcBuffer ); kPrintStringXY( 0, 4, "====================================================" ); // 유저 레벨 태스크의 경우는 무한 루프를 수행하지 않고 태스크를 종료시키고 다른 // 태스크로 전환 if( pstTask->qwFlags & TASK_FLAGS_USERLEVEL ) { // 태스크 종료 kEndTask( pstTask->stLink.qwID ); // 무한 루프 수행 // kEndTask() 함수에서 다른 태스크로 전환하므로 실제로 여기는 수행되지 않음 while( 1 ) { ; } } // 커널 레벨인 경우는 무한 루프 수행 else { // 무한 루프 수행 while( 1 ) { ; } } }
/** * 태스크 사이에서 사용하는 데이터를 위한 잠금 해제 함수 */ void kUnlock( MUTEX* pstMutex ) { // 뮤텍스를 잠근 태스크가 아니면 실패 if( ( pstMutex->bLockFlag == FALSE ) || ( pstMutex->qwTaskID != kGetRunningTask()->stLink.qwID ) ) { return ; } // 뮤텍스를 중복으로 잠갔으면 잠긴 횟수만 감소 if( pstMutex->dwLockCount > 1 ) { pstMutex->dwLockCount--; return ; } // 해제된 것으로 설정, 잠김 플래그를 가장 나중에 해제해야 함 pstMutex->qwTaskID = TASK_INVALIDID; pstMutex->dwLockCount = 0; pstMutex->bLockFlag = FALSE; }
/** * 태스크를 생성 * 태스크 ID에 따라서 스택 풀에서 스택 자동 할당 * 프로세스 및 스레드 모두 생성 가능 * bAffinity에 태스크를 수행하고 싶은 코어의 ID를 설정 가능 */ TCB* kCreateTask( QWORD qwFlags, void* pvMemoryAddress, QWORD qwMemorySize, QWORD qwEntryPointAddress, BYTE bAffinity ) { TCB* pstTask, * pstProcess; void* pvStackAddress; BYTE bCurrentAPICID; // 현재 코어의 로컬 APIC ID를 확인 bCurrentAPICID = kGetAPICID(); // 태스크 자료구조 할당 pstTask = kAllocateTCB(); if( pstTask == NULL ) { return NULL; } // 동적 메모리 영역에서 스택 할당 pvStackAddress = kAllocateMemory( TASK_STACKSIZE ); if( pvStackAddress == NULL ) { kFreeTCB( pstTask->stLink.qwID ); return NULL; } // 임계 영역 시작 kLockForSpinLock( &( gs_vstScheduler[ bCurrentAPICID ].stSpinLock ) ); // 현재 프로세스 또는 스레드가 속한 프로세스를 검색 pstProcess = kGetProcessByThread( kGetRunningTask( bCurrentAPICID ) ); // 만약 프로세스가 없다면 아무런 작업도 하지 않음 if( pstProcess == NULL ) { kFreeTCB( pstTask->stLink.qwID ); kFreeMemory( pvStackAddress ); // 임계 영역 끝 kUnlockForSpinLock( &( gs_vstScheduler[ bCurrentAPICID ].stSpinLock ) ); return NULL; } // 스레드를 생성하는 경우라면 내가 속한 프로세스의 자식 스레드 리스트에 연결함 if( qwFlags & TASK_FLAGS_THREAD ) { // 현재 스레드의 프로세스를 찾아서 생성할 스레드에 프로세스 정보를 상속 pstTask->qwParentProcessID = pstProcess->stLink.qwID; pstTask->pvMemoryAddress = pstProcess->pvMemoryAddress; pstTask->qwMemorySize = pstProcess->qwMemorySize; // 부모 프로세스의 자식 스레드 리스트에 추가 kAddListToTail( &( pstProcess->stChildThreadList ), &( pstTask->stThreadLink ) ); } // 프로세스는 파라미터로 넘어온 값을 그대로 설정 else { pstTask->qwParentProcessID = pstProcess->stLink.qwID; pstTask->pvMemoryAddress = pvMemoryAddress; pstTask->qwMemorySize = qwMemorySize; } // 스레드의 ID를 태스크 ID와 동일하게 설정 pstTask->stThreadLink.qwID = pstTask->stLink.qwID; // 임계 영역 끝 kUnlockForSpinLock( &( gs_vstScheduler[ bCurrentAPICID ].stSpinLock ) ); // TCB를 설정한 후 준비 리스트에 삽입하여 스케줄링될 수 있도록 함 kSetUpTask( pstTask, qwFlags, qwEntryPointAddress, pvStackAddress, TASK_STACKSIZE ); // 자식 스레드 리스트를 초기화 kInitializeList( &( pstTask->stChildThreadList ) ); // FPU 사용 여부를 사용하지 않은 것으로 초기화 pstTask->bFPUUsed = FALSE; // 현재 코어의 로컬 APIC ID를 태스크에 설정 pstTask->bAPICID = bCurrentAPICID; // 프로세서 친화도(Affinity)를 설정 pstTask->bAffinity = bAffinity; // 부하 분산을 고려하여 스케줄러에 태스크를 추가 kAddTaskToSchedulerWithLoadBalancing( pstTask ); return pstTask; }
/** * 태스크를 생성 * 태스크 ID에 따라서 스택 풀에서 스택 자동 할당 * 프로세스 및 스레드 모두 생성 가능 */ TCB* kCreateTask( QWORD qwFlags, void* pvMemoryAddress, QWORD qwMemorySize, QWORD qwEntryPointAddress ) { TCB* pstTask, * pstProcess; void* pvStackAddress; // 임계 영역 시작 kLockForSpinLock( &( gs_stScheduler.stSpinLock ) ); pstTask = kAllocateTCB(); if( pstTask == NULL ) { // 임계영역 끝 kUnlockForSpinLock( &( gs_stScheduler.stSpinLock ) ); return NULL; } // 현재 프로세스 또는 스레드가 속한 프로세스를 검색 pstProcess = kGetProcessByThread( kGetRunningTask() ); // 만약 프로세스가 없다면 아무런 작업도 하지 않음 if( pstProcess == NULL ) { kFreeTCB( pstTask->stLink.qwID ); // 임계 영역 끝 kUnlockForSpinLock( &( gs_stScheduler.stSpinLock ) ); return NULL; } // 스레드를 생성하는 경우라면 내가 속한 프로세스의 자식 스레드 리스트에 연결함 if( qwFlags & TASK_FLAGS_THREAD ) { // 현재 스레드의 프로세스를 찾아서 생성할 스레드에 프로세스 정보를 상속 pstTask->qwParentProcessID = pstProcess->stLink.qwID; pstTask->pvMemoryAddress = pstProcess->pvMemoryAddress; pstTask->qwMemorySize = pstProcess->qwMemorySize; // 부모 프로세스의 자식 스레드 리스트에 추가 kAddListToTail( &( pstProcess->stChildThreadList ), &( pstTask->stThreadLink ) ); } // 프로세스는 파라미터로 넘어온 값을 그대로 설정 else { pstTask->qwParentProcessID = pstProcess->stLink.qwID; pstTask->pvMemoryAddress = pvMemoryAddress; pstTask->qwMemorySize = qwMemorySize; } // 스레드의 ID를 태스크 ID와 동일하게 설정 pstTask->stThreadLink.qwID = pstTask->stLink.qwID; // 임계 영역 끝 kUnlockForSpinLock( &( gs_stScheduler.stSpinLock ) ); // 태스크 ID로 스택 어드레스 계산, 하위 32비트가 스택 풀의 오프셋 역할 수행 pvStackAddress = ( void* ) ( TASK_STACKPOOLADDRESS + ( TASK_STACKSIZE * GETTCBOFFSET( pstTask->stLink.qwID ) ) ); // TCB를 설정한 후 준비 리스트에 삽입하여 스케줄링될 수 있도록 함 kSetUpTask( pstTask, qwFlags, qwEntryPointAddress, pvStackAddress, TASK_STACKSIZE ); // 자식 스레드 리스트를 초기화 kInitializeList( &( pstTask->stChildThreadList ) ); // FPU 사용 여부를 사용하지 않은 것으로 초기화 pstTask->bFPUUsed = FALSE; // 임계 영역 시작 kLockForSpinLock( &( gs_stScheduler.stSpinLock ) ); // 태스크를 준비 리스트에 삽입 kAddTaskToReadyList( pstTask ); // 임계 영역 끝 kUnlockForSpinLock( &( gs_stScheduler.stSpinLock ) ); return pstTask; }