예제 #1
0
파일: Synchronization.c 프로젝트: cocojk/os
// 시스템 전역에서 사용하는 데이터를 위한 잠금 함수 
void kLockForSpinLock(SPINLOCK* pstSpinLock)
{
	BOOL bInterruptFlag;

	// 인터럽트를 먼저 비활성화 
	bInterruptFlag = kSetInterruptFlag(FALSE);

	// 이미 잠겨 있다면 내가 잠갔는지 확인하고 그렇다면 잠금 횟수를 증가시킨 뒤 종료 
	if(kTestAndSet(&(pstSpinLock->bLockFlag),0,1)==FALSE)
	{
		// 자신이 잠갔다면 횟수만 증가시킴 
		if(pstSpinLock->bAPICID ==  kGetAPICID())
		{
			pstSpinLock->dwLockCount++;
			return;
		}

		// 자신이 아닌 경우는 잠긴 것이 해제될 때까지 대기 
		while(kTestAndSet(&(pstSpinLock->bLockFlag),0,1)==FALSE)
		{
			// kTestAndSet() 함수르 계속 호출하여 메모리 버스가 Lock 되는 것을 방지 
			 while(pstSpinLock->bLockFlag == TRUE)
			 {
				 kPause();
			 }
		}
	}

	// 잠김 설정, 잠김 플래그는 위의 kTestAndSet() 함수에서 처리 
	pstSpinLock->dwLockCount = 1;
	pstSpinLock->bAPICID = kGetAPICID();

	// 인터럽트 플래그를 저장하여 Unlock 수행 시 복원 
	pstSpinLock->bInterruptFlag = bInterruptFlag;
}
예제 #2
0
파일: main.c 프로젝트: cocojk/os
// Application Processor용 C 언어 커널 엔트리 포인트
// 대부분의 자료구조는 Bootstrap Processor가 생성해 놓았으므로 코어에 설정하는 작업만 함
void mainForApplicationProcessor(void)
{

    // GDT 테이블을 설정
    kLoadGDTR(GDTR_STARTADDRESS);

    // TSS 디스크립터를 설정, TSS 세그먼트와 디스크립터를 Application Processor의
    // 수만큼 생성했으므로, APIC ID를 이용하여 TSS 디스크립터를 할당
    kLoadTR(GDT_TSSSEGMENT+(kGetAPICID()*sizeof(GDTENTRY16)));

    // IDT 테이블을 설정
    kLoadIDTR(IDTR_STARTADDRESS);

    // 스케줄러 초기화
    kInitializeScheduler();

    // 현재 코어의 로컬 APIC를 활성화
    kEnableSoftwareLocalAPIC();

    // 모든 인터럽트를 수신할 수 있도록 태스크 우선순위 레지스터를 0으로 설정
    kSetTaskPriority(0);

    // 로컬 APIC의 로컬 벡터 테이블을 초기화
    kInitializeLocalVectorTable();

    // 인터럽트를 활성화
    kEnableInterrupt();

    kPrintf("Application Processor[APIC ID: %d] is Activated\n",kGetAPICID());

    // 유후 태스크 실행
    kIdleTask();
}
예제 #3
0
파일: Synchronization.c 프로젝트: cocojk/os
// 태스크 사이에서 사용하는 데이터를 위한 잠근 해제 함수 
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);
}
예제 #4
0
/**
 *  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 );
}
예제 #5
0
파일: Synchronization.c 프로젝트: cocojk/os
// 시스템 전역에서 사용하는 데이터를 위한 잠금 해제 함수 
void kUnlockForSpinLock(SPINLOCK* pstSpinLock)
{
	BOOL bInterruptFlag;

	// 인터럽트를 먼저 비활성화 
	bInterruptFlag = kSetInterruptFlag(FALSE);

	// 스핀락을 잠근 태스크가 아니면 실패 
	if((pstSpinLock->bLockFlag==FALSE)||(pstSpinLock->bAPICID!=kGetAPICID()))
	{
		kSetInterruptFlag(bInterruptFlag);
		return;
	}

	// 스핀락을 중복으로 잠갔으면 잠김 횟수만 감소 
	if(pstSpinLock->dwLockCount>1)
	{
		pstSpinLock->dwLockCount--;
		return;
	}

	// 스핀락을 해제된 것으로 설정하고 인터럽트 플래그를 복원 
	// 인터럽트 플래그는 미리 저장해두었다가 사용 
	bInterruptFlag = pstSpinLock->bInterruptFlag;

	pstSpinLock->bAPICID = 0xFF;
	pstSpinLock->dwLockCount = 0;
	pstSpinLock->bInterruptFlag = FALSE;
	pstSpinLock->bLockFlag = FALSE;

	kSetInterruptFlag(bInterruptFlag);
}
예제 #6
0
/**
 *  인터럽트 부하 분산(Interrupt Load Balancing) 처리
 */
void kProcessLoadBalancing( int iIRQ )
{
    QWORD qwMinCount = 0xFFFFFFFFFFFFFFFF;
    int iMinCountCoreIndex;
    int iCoreCount;
    int i;
    BOOL bResetCount = FALSE;
    BYTE bAPICID;
    
    bAPICID = kGetAPICID();

    // 부하 분산 기능이 꺼져 있거나, 부하 분산을 처리할 시점이 아니면 할 필요가 없음
    if( ( gs_stInterruptManager.vvqwCoreInterruptCount[ bAPICID ][ iIRQ ] == 0 ) ||
        ( ( gs_stInterruptManager.vvqwCoreInterruptCount[ bAPICID ][ iIRQ ] % 
            INTERRUPT_LOADBALANCINGDIVIDOR ) != 0 ) ||
        ( gs_stInterruptManager.bUseLoadBalancing == FALSE ) )
    {
        return ;
    }
    
    // 코어의 개수를 구해서 루프를 수행하며 인터럽트 처리 횟수가 가장 작은 코어를 
    // 선택
    iMinCountCoreIndex = 0;
    iCoreCount = kGetProcessorCount();
    for( i = 0 ; i < iCoreCount ; i++ )
    {
        if( ( gs_stInterruptManager.vvqwCoreInterruptCount[ i ][ iIRQ ] <
                qwMinCount ) )
        {
            qwMinCount = gs_stInterruptManager.vvqwCoreInterruptCount[ i ][ iIRQ ];
            iMinCountCoreIndex = i;
        }
        // 전체 카운트가 거의 최대 값에 근접했다면 나중에 카운트를 모두 0으로 설정
        else if( gs_stInterruptManager.vvqwCoreInterruptCount[ i ][ iIRQ ] >=
            0xFFFFFFFFFFFFFFFE )
        {
            bResetCount = TRUE;
        }
    }
    
    // I/O 리다이렉션 테이블을 변경하여 가장 인터럽트를 처리한 횟수가 작은 로컬 APIC로 전달
    kRoutingIRQToAPICID( iIRQ, iMinCountCoreIndex );
    
    // 처리한 코어의 카운트가 최댓값에 근접했다면 전체 카운트를 다시 0에서 시작하도록
    // 변경
    if( bResetCount == TRUE )
    {
        for( i = 0 ; i < iCoreCount ; i++ )
        {
            gs_stInterruptManager.vvqwCoreInterruptCount[ i ][ iIRQ ] = 0;
        }
    }
}
예제 #7
0
/**
 *  공통으로 사용하는 예외 핸들러
 */
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 )
        {
            ;
        }
    }
}
예제 #8
0
/**
 *  타이머 인터럽트의 핸들러
 */
void kTimerHandler( int iVectorNumber )
{
    char vcBuffer[] = "[INT:  , ]";
    static int g_iTimerInterruptCount = 0;
    int iIRQ;
    BYTE bCurrentAPICID;

    //=========================================================================
    // 인터럽트가 발생했음을 알리려고 메시지를 출력하는 부분
    // 인터럽트 벡터를 화면 오른쪽 위에 2자리 정수로 출력
    vcBuffer[ 5 ] = '0' + iVectorNumber / 10;
    vcBuffer[ 6 ] = '0' + iVectorNumber % 10;
    // 발생한 횟수 출력
    vcBuffer[ 8 ] = '0' + g_iTimerInterruptCount;
    g_iTimerInterruptCount = ( g_iTimerInterruptCount + 1 ) % 10;
    kPrintStringXY( 70, 0, vcBuffer );
    //=========================================================================
    
    // 인터럽트 벡터에서 IRQ 번호 추출
    iIRQ = iVectorNumber - PIC_IRQSTARTVECTOR;

    // EOI 전송
    kSendEOI( iIRQ );
    
    // 인터럽트 발생 횟수를 업데이트
    kIncreaseInterruptCount( iIRQ );

    // IRQ 0 인터럽트 처리는 Bootstrap Processor만 처리
    bCurrentAPICID = kGetAPICID();
    if( bCurrentAPICID == 0 )
    {
        // 타이머 발생 횟수를 증가
        g_qwTickCount++;
    }

    // 태스크가 사용한 프로세서의 시간을 줄임
    kDecreaseProcessorTime( bCurrentAPICID );
    // 프로세서가 사용할 수 있는 시간을 다 썼다면 태스크 전환 수행
    if( kIsProcessorTimeExpired( bCurrentAPICID ) == TRUE )
    {
        kScheduleInInterrupt();
    }
}
예제 #9
0
파일: Synchronization.c 프로젝트: cocojk/os
// 태스크 사이에서 사용하는 데이터를 위한 잠금 함수
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);
}
예제 #10
0
/**
 *  태스크가 자신을 종료함
 */
void kExitTask( void )
{
    kEndTask( gs_vstScheduler[ kGetAPICID() ].pstRunningTask->stLink.qwID );
}
예제 #11
0
/**
 *  태스크를 종료
 */
BOOL kEndTask( QWORD qwTaskID )
{
    TCB* pstTarget;
    BYTE bPriority;
    BYTE bAPICID;
    
    // 태스크가 포함된 코어의 로컬 APIC ID를 찾은 후, 스핀락을 잠금
    if( kFindSchedulerOfTaskAndLock( qwTaskID, &bAPICID ) == FALSE )
    {
        return FALSE;
    }
    
    // 현재 실행중인 태스크이면 EndTask 비트를 설정하고 태스크를 전환
    pstTarget = gs_vstScheduler[ bAPICID ].pstRunningTask;
    if( pstTarget->stLink.qwID == qwTaskID )
    {
        pstTarget->qwFlags |= TASK_FLAGS_ENDTASK;
        SETPRIORITY( pstTarget->qwFlags, TASK_FLAGS_WAIT );
        
        // 임계 영역 끝
        kUnlockForSpinLock( &( gs_vstScheduler[ bAPICID ].stSpinLock ) );
        
        // 현재 스케줄러에서 실행중인 태스크의 경우만 아래를 적용
        if( kGetAPICID() == bAPICID )
        {
            kSchedule();

            // 태스크가 전환 되었으므로 아래 코드는 절대 실행되지 않음
            while( 1 ) 
            {
                ;
            }
        }
        
        return TRUE;
    }
    
    // 실행 중인 태스크가 아니면 준비 큐에서 직접 찾아서 대기 리스트에 연결
    // 준비 리스트에서 태스크를 찾지 못하면 직접 태스크를 찾아서 태스크 종료 비트를
    // 설정
    pstTarget = kRemoveTaskFromReadyList( bAPICID, qwTaskID );
    if( pstTarget == NULL )
    {
        // 태스크 ID로 직접 찾아서 설정
        pstTarget = kGetTCBInTCBPool( GETTCBOFFSET( qwTaskID ) );
        if( pstTarget != NULL )
        {
            pstTarget->qwFlags |= TASK_FLAGS_ENDTASK;
            SETPRIORITY( pstTarget->qwFlags, TASK_FLAGS_WAIT );
        }
        
        // 임계 영역 끝
        kUnlockForSpinLock( &( gs_vstScheduler[ bAPICID ].stSpinLock ) );
        return TRUE;
    }
    
    pstTarget->qwFlags |= TASK_FLAGS_ENDTASK;
    SETPRIORITY( pstTarget->qwFlags, TASK_FLAGS_WAIT );
    kAddListToTail( &( gs_vstScheduler[ bAPICID ].stWaitList ), pstTarget );
    
    // 임계 영역 끝
    kUnlockForSpinLock( &( gs_vstScheduler[ bAPICID ].stSpinLock ) );
    return TRUE;
}
예제 #12
0
/**
 *  인터럽트가 발생했을 때, 다른 태스크를 찾아 전환
 *      반드시 인터럽트나 예외가 발생했을 때 호출해야 함
 */
BOOL kScheduleInInterrupt( void )
{
    TCB* pstRunningTask, * pstNextTask;
    char* pcContextAddress;
    BYTE bCurrentAPICID;
    QWORD qwISTStartAddress;
        
    // 현재 로컬 APIC ID 확인
    bCurrentAPICID = kGetAPICID();
    
    // 임계 영역 시작
    kLockForSpinLock( &( gs_vstScheduler[ bCurrentAPICID ].stSpinLock ) );
        
    // 전환할 태스크가 없으면 종료
    pstNextTask = kGetNextTaskToRun( bCurrentAPICID );
    if( pstNextTask == NULL )
    {
        // 임계 영역 끝
        kUnlockForSpinLock( &( gs_vstScheduler[ bCurrentAPICID ].stSpinLock ) );
        return FALSE;
    }
    
    //==========================================================================
    //  태스크 전환 처리   
    //      인터럽트 핸들러에서 저장한 콘텍스트를 다른 콘텍스트로 덮어쓰는 방법으로 처리
    //==========================================================================
    // IST의 끝부분부터 코어 0 -> 코어 15 순으로 64Kbyte씩 쓰고 있으므로, 로컬 APIC ID를
    // 이용해서 IST 어드레스를 계산
    qwISTStartAddress = IST_STARTADDRESS + IST_SIZE - 
                        ( IST_SIZE / MAXPROCESSORCOUNT * bCurrentAPICID );
    pcContextAddress = ( char* ) qwISTStartAddress - sizeof( CONTEXT );
    
    pstRunningTask = gs_vstScheduler[ bCurrentAPICID ].pstRunningTask;
    gs_vstScheduler[ bCurrentAPICID ].pstRunningTask = pstNextTask;

    // 유휴 태스크에서 전환되었다면 사용한 Tick Count를 증가시킴
    if( ( pstRunningTask->qwFlags & TASK_FLAGS_IDLE ) == TASK_FLAGS_IDLE )
    {
        gs_vstScheduler[ bCurrentAPICID ].qwSpendProcessorTimeInIdleTask += TASK_PROCESSORTIME;
    }
    
    // 태스크 종료 플래그가 설정된 경우, 콘텍스트를 저장하지 않고 대기 리스트에만 삽입
    if( pstRunningTask->qwFlags & TASK_FLAGS_ENDTASK )
    {    
        kAddListToTail( &( gs_vstScheduler[ bCurrentAPICID ].stWaitList ), 
                        pstRunningTask );
    }
    // 태스크가 종료되지 않으면 IST에 있는 콘텍스트를 복사하고, 현재 태스크를 준비 리스트로
    // 옮김
    else
    {
        kMemCpy( &( pstRunningTask->stContext ), pcContextAddress, sizeof( CONTEXT ) );
    }
    
    // 다음에 수행할 태스크가 FPU를 쓴 태스크가 아니라면 TS bit 설정
    if( gs_vstScheduler[ bCurrentAPICID ].qwLastFPUUsedTaskID != 
        pstNextTask->stLink.qwID )
    {
        kSetTS();
    }
    else
    {
        kClearTS();
    }
    
    // 임계 영역 끝
    kUnlockForSpinLock( &( gs_vstScheduler[ bCurrentAPICID ].stSpinLock ) );
    
    // 전환해서 실행할 태스크를 Running Task로 설정하고 콘텍스트를 IST에 복사해서
    // 자동으로 태스크 전환이 일어나도록 함
    kMemCpy( pcContextAddress, &( pstNextTask->stContext ), sizeof( CONTEXT ) );
    
    // 종료하는 태스크가 아니면 스케줄러에 태스크 추가
    if( ( pstRunningTask->qwFlags & TASK_FLAGS_ENDTASK ) != TASK_FLAGS_ENDTASK )
    {
        // 스케줄러에 태스크를 추가, 부하 분산을 고려함
        kAddTaskToSchedulerWithLoadBalancing( pstRunningTask );
    }    
    
    // 프로세서 사용 시간을 업데이트
    gs_vstScheduler[ bCurrentAPICID ].iProcessorTime = TASK_PROCESSORTIME;
    
    return TRUE;
}
예제 #13
0
/**
 *  다른 태스크를 찾아서 전환
 *      인터럽트나 예외가 발생했을 때 호출하면 안됨
 *      단, 인터럽트나 예외에서 태스크를 종료하는 경우는 사용 가능
 */
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;
}
예제 #14
0
/**
 *  스케줄러를 초기화
 *      스케줄러를 초기화하는데 필요한 TCB 풀과 init 태스크도 같이 초기화
 */
void kInitializeScheduler( void )
{
    int i;
    int j;
    BYTE bCurrentAPICID;
    TCB* pstTask;
    
    // 현재 코어의 로컬 APIC ID 확인
    bCurrentAPICID = kGetAPICID();

    // Bootstrap Processor만 태스크 풀과 스케줄러 자료구조를 모두 초기화
    if( bCurrentAPICID == 0 )
    {
        // 태스크 풀 초기화
        kInitializeTCBPool();
        
        // 준비 리스트와 우선 순위별 실행 횟수를 초기화하고 대기 리스트와 스핀락을 초기화
        for( j = 0 ; j < MAXPROCESSORCOUNT ; j++ )
        {
            // 준비 리스트 초기화
            for( i = 0 ; i < TASK_MAXREADYLISTCOUNT ; i++ )
            {
                kInitializeList( &( gs_vstScheduler[ j ].vstReadyList[ i ] ) );
                gs_vstScheduler[ j ].viExecuteCount[ i ] = 0;
            }    
            // 대기 리스트 초기화
            kInitializeList( &( gs_vstScheduler[ j ].stWaitList ) );

            // 스핀락 초기화
            kInitializeSpinLock( &( gs_vstScheduler[ j ].stSpinLock ) );
        }
    }    
    
    // TCB를 할당 받아 부팅을 수행한 태스크를 커널 최초의 프로세스로 설정
    pstTask = kAllocateTCB();
    gs_vstScheduler[ bCurrentAPICID ].pstRunningTask = pstTask;
    
    // BSP의 콘솔 쉘이나 AP의 유휴 태스크(Idle Task)는 모두 현재 코어에서만 실행하도록
    // 로컬 APIC ID와 프로세서 친화도를 현재 코어의 로컬 APIC ID로 설정
    pstTask->bAPICID = bCurrentAPICID;
    pstTask->bAffinity = bCurrentAPICID;
    
    // Bootstrap Processor는 콘솔 셸을 실행
    if( bCurrentAPICID == 0 )
    {
        pstTask->qwFlags = TASK_FLAGS_HIGHEST | TASK_FLAGS_PROCESS | TASK_FLAGS_SYSTEM;
    }
    // Application Processor는 특별히 긴급한 태스크가 없으므로 유휴(Idle) 태스크를 실행
    else
    {
        pstTask->qwFlags = TASK_FLAGS_LOWEST | TASK_FLAGS_PROCESS | TASK_FLAGS_SYSTEM | TASK_FLAGS_IDLE;
    }
    
    pstTask->qwParentProcessID = pstTask->stLink.qwID;
    pstTask->pvMemoryAddress = ( void* ) 0x100000;
    pstTask->qwMemorySize = 0x500000;
    pstTask->pvStackAddress = ( void* ) 0x600000;
    pstTask->qwStackSize = 0x100000;
    
    // 프로세서 사용률을 계산하는데 사용하는 자료구조 초기화
    gs_vstScheduler[ bCurrentAPICID ].qwSpendProcessorTimeInIdleTask = 0;
    gs_vstScheduler[ bCurrentAPICID ].qwProcessorLoad = 0;
    
    // FPU를 사용한 태스크 ID를 유효하지 않은 값으로 초기화
    gs_vstScheduler[ bCurrentAPICID ].qwLastFPUUsedTaskID = TASK_INVALIDID;
}
예제 #15
0
/**
 *  태스크를 생성
 *      태스크 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;
}
예제 #16
0
/**
 *  유휴 태스크
 *      대기 큐에 삭제 대기중인 태스크를 정리
 */
void kIdleTask( void )
{
    TCB* pstTask, * pstChildThread, * pstProcess;
    QWORD qwLastMeasureTickCount, qwLastSpendTickInIdleTask;
    QWORD qwCurrentMeasureTickCount, qwCurrentSpendTickInIdleTask;
    QWORD qwTaskID, qwChildThreadID;
    int i, iCount;
    void* pstThreadLink;
    BYTE bCurrentAPICID;
    BYTE bProcessAPICID;
    
    // 현재 코어의 로컬 APIC ID를 확인
    bCurrentAPICID = kGetAPICID();
    
    // 프로세서 사용량 계산을 위해 기준 정보를 저장
    qwLastSpendTickInIdleTask = 
        gs_vstScheduler[ bCurrentAPICID ].qwSpendProcessorTimeInIdleTask;
    qwLastMeasureTickCount = kGetTickCount();
    
    while( 1 )
    {
        // 현재 상태를 저장
        qwCurrentMeasureTickCount = kGetTickCount();
        qwCurrentSpendTickInIdleTask = 
            gs_vstScheduler[ bCurrentAPICID ].qwSpendProcessorTimeInIdleTask;
        
        // 프로세서 사용량을 계산
        // 100 - ( 유휴 태스크가 사용한 프로세서 시간 ) * 100 / ( 시스템 전체에서 
        // 사용한 프로세서 시간 )
        if( qwCurrentMeasureTickCount - qwLastMeasureTickCount == 0 )
        {
            gs_vstScheduler[ bCurrentAPICID ].qwProcessorLoad = 0;
        }
        else
        {
            gs_vstScheduler[ bCurrentAPICID ].qwProcessorLoad = 100 - 
                ( qwCurrentSpendTickInIdleTask - qwLastSpendTickInIdleTask ) * 
                100 /( qwCurrentMeasureTickCount - qwLastMeasureTickCount );
        }
        
        // 현재 상태를 이전 상태에 보관
        qwLastMeasureTickCount = qwCurrentMeasureTickCount;
        qwLastSpendTickInIdleTask = qwCurrentSpendTickInIdleTask;

        // 프로세서의 부하에 따라 쉬게 함
        kHaltProcessorByLoad( bCurrentAPICID );
        
        // 대기 큐에 대기중인 태스크가 있으면 태스크를 종료함
        if( kGetListCount( &( gs_vstScheduler[ bCurrentAPICID ].stWaitList ) ) 
                > 0 )
        {
            while( 1 )
            {
                // 임계 영역 시작
                kLockForSpinLock( &( gs_vstScheduler[ bCurrentAPICID ].stSpinLock ) );
                pstTask = kRemoveListFromHeader( 
                    &( gs_vstScheduler[ bCurrentAPICID ].stWaitList ) );
                // 임계 영역 끝
                kUnlockForSpinLock( &( gs_vstScheduler[ bCurrentAPICID ].stSpinLock ) );
                
                if( pstTask == NULL )
                {
                    break;
                }
                
                if( pstTask->qwFlags & TASK_FLAGS_PROCESS )
                {
                    // 프로세스를 종료할 때 자식 스레드가 존재하면 스레드를 모두 
                    // 종료하고, 다시 자식 스레드 리스트에 삽입
                    iCount = kGetListCount( &( pstTask->stChildThreadList ) );
                    for( i = 0 ; i < iCount ; i++ )
                    {
                        // 임계 영역 시작
                        kLockForSpinLock( 
                            &( gs_vstScheduler[ bCurrentAPICID ].stSpinLock ) );
                        // 스레드 링크의 어드레스에서 꺼내 스레드를 종료시킴
                        pstThreadLink = ( TCB* ) kRemoveListFromHeader( 
                                &( pstTask->stChildThreadList ) );
                        if( pstThreadLink == NULL )
                        {
                            // 임계 영역 끝
                            kUnlockForSpinLock( 
                                &( gs_vstScheduler[ bCurrentAPICID ].stSpinLock ) );
                            break;
                        }
                        
                        // 자식 스레드 리스트에 연결된 정보는 태스크 자료구조에 있는 
                        // stThreadLink의 시작 어드레스이므로, 태스크 자료구조의 시작
                        // 어드레스를 구하려면 별도의 계산이 필요함
                        pstChildThread = GETTCBFROMTHREADLINK( pstThreadLink );                        

                        // 다시 자식 스레드 리스트에 삽입하여 해당 스레드가 종료될 때
                        // 자식 스레드가 프로세스를 찾아 스스로 리스트에서 제거하도록 함
                        kAddListToTail( &( pstTask->stChildThreadList ),
                                &( pstChildThread->stThreadLink ) );
                        qwChildThreadID = pstChildThread->stLink.qwID;
                        // 임계 영역 끝
                        kUnlockForSpinLock( 
                            &( gs_vstScheduler[ bCurrentAPICID ].stSpinLock ) );
                        
                        // 자식 스레드를 찾아서 종료
                        kEndTask( qwChildThreadID );
                    }
                    
                    // 아직 자식 스레드가 남아있다면 자식 스레드가 다 종료될 때까지
                    // 기다려야 하므로 다시 대기 리스트에 삽입
                    if( kGetListCount( &( pstTask->stChildThreadList ) ) > 0 )
                    {
                        // 임계 영역 시작
                        kLockForSpinLock( 
                            &( gs_vstScheduler[ bCurrentAPICID ].stSpinLock ) );
                        kAddListToTail( 
                             &( gs_vstScheduler[ bCurrentAPICID ].stWaitList ), 
                             pstTask );
                        // 임계 영역 끝
                        kUnlockForSpinLock( 
                            &( gs_vstScheduler[ bCurrentAPICID ].stSpinLock ) );
                        continue;
                    }
                    // 프로세스를 종료해야 하므로 할당 받은 메모리 영역을 삭제
                    else
                    {
                        // 유저 레벨 프로세스라면 메모리를 할당 받았을 것이므로 할당
                        // 받은 메모리를 삭제
                        if( pstTask->qwFlags & TASK_FLAGS_USERLEVEL )
                        {
                            kFreeMemory( pstTask->pvMemoryAddress );
                        }
                    }
                }                
                else if( pstTask->qwFlags & TASK_FLAGS_THREAD )
                {
                    // 스레드라면 프로세스의 자식 스레드 리스트에서 제거
                    pstProcess = kGetProcessByThread( pstTask );
                    if( pstProcess != NULL )
                    {
                        // 프로세스 ID로 프로세스가 속한 스케줄러의 ID를 찾고 스핀락 잠금
                        if( kFindSchedulerOfTaskAndLock( pstProcess->stLink.qwID, 
                                &bProcessAPICID ) == TRUE )
                        {
                            kRemoveList( &( pstProcess->stChildThreadList ), 
                                         pstTask->stLink.qwID );
                            kUnlockForSpinLock( &( gs_vstScheduler[ 
                                bProcessAPICID ].stSpinLock ) );
                        }
                    }
                }
                
                // 여기까지 왔다면 태스크가 정상적으로 종료된 것이므로, 
                // 태스크 자료구조(TCB)와 스택을 반환
                qwTaskID = pstTask->stLink.qwID;

                // 스택을 반환
                kFreeMemory( pstTask->pvStackAddress );
                 
                // 태스크 자료구조(TCB)를 반환
                kFreeTCB( qwTaskID );                
                kPrintf( "IDLE: Task ID[0x%q] is completely ended.\n", 
                        qwTaskID );
            }
        }
        
        kSchedule();
    }
}
예제 #17
0
파일: main.c 프로젝트: cocojk/os
// Bootstrap Processor용 C 언어 커널 엔트리 포인트
void main( void )
{
    int iCursorX, iCursorY;

    // 부트 로더에 있는 BSP 플래그를 읽어서 Application Processor이면
    // 해당 코어용 초기화 함수로 이동
    if(*((BYTE*)BOOTSTRAPPROCESSOR_FLAGADDESS)==0)
    {
        mainForApplicationProcessor();
    }

    // Bootstrap Processor가 부팅을 완료했으므로, 0x7C09에 있는 Bootstrap Processor를
    // 나타내는 플래그를 0으로 설정하여 Application Processor용으로 코드 실행 경로를 변경
    *((BYTE*)BOOTSTRAPPROCESSOR_FLAGADDESS)=0;

    // 콘솔을 먼저 초기화한 후, 다음 작업을 수행
    kInitializeConsole( 0, 10 );
    kPrintf( "Switch To IA-32e Mode Success~!!\n" );
    kPrintf( "IA-32e C Language Kernel Start..............[Pass]\n" );
    kPrintf( "Initialize Console..........................[Pass]\n" );

    // 부팅 상황을 화면에 출력
    kGetCursor( &iCursorX, &iCursorY );
    kPrintf( "GDT Initialize And Switch For IA-32e Mode...[    ]" );
    kInitializeGDTTableAndTSS();
    kLoadGDTR( GDTR_STARTADDRESS );
    kSetCursor( 45, iCursorY++ );
    kPrintf( "Pass\n" );

    kPrintf( "TSS Segment Load............................[    ]" );
    kLoadTR( GDT_TSSSEGMENT );
    kSetCursor( 45, iCursorY++ );
    kPrintf( "Pass\n" );

    kPrintf( "IDT Initialize..............................[    ]" );
    kInitializeIDTTables();
    kLoadIDTR( IDTR_STARTADDRESS );
    kSetCursor( 45, iCursorY++ );
    kPrintf( "Pass\n" );

    kPrintf( "Total RAM Size Check........................[    ]" );
    kCheckTotalRAMSize();
    kSetCursor( 45, iCursorY++ );
    kPrintf( "Pass], Size = %d MB\n", kGetTotalRAMSize() );

    kPrintf("TCB Poll And Scheduler Initialize...........[Pass]\n");
    iCursorY++;
    kInitializeScheduler();

    // 동적 메모리 초기화
    kPrintf("Dynamic Memory Initialize...................[Pass]\n");
    iCursorY++;
    kInitializeDynamicMemory();

    // 1ms당 한 번씩 인터럽트가 발생하도록 설정
    kInitializePIT(MSTOCOUNT(1),1);

    kPrintf( "Keyboard Activate And Queue Initialize......[    ]" );
    // 키보드를 활성화
    if( kInitializeKeyboard() == TRUE )
    {
        kSetCursor( 45, iCursorY++ );
        kPrintf( "Pass\n" );
        kChangeKeyboardLED( FALSE, FALSE, FALSE );
    }
    else
    {
        kSetCursor( 45, iCursorY++ );
        kPrintf( "Fail\n" );
        while( 1 ) ;
    }

    kPrintf( "PIC Controller And Interrupt Initialize.....[    ]" );
    // PIC 컨트롤러 초기화 및 모든 인터럽트 활성화
    kInitializePIC();
    kMaskPICInterrupt( 0 );
    kEnableInterrupt();
    kSetCursor( 45, iCursorY++ );
    kPrintf( "Pass\n" );

    // 파일 시스템을 초기화
    kPrintf("File System Initialize......................[    ]");
    if(kInitializeFileSystem()==TRUE)
    {
        kSetCursor(45,iCursorY++);
        kPrintf("Pass\n");
    }
    else
    {
        kSetCursor(45,iCursorY++);
        kPrintf("Fail\n");
    }

    // 시리얼 포트 초기화
    kPrintf("Serial Port Initialize......................[Pass]\n");
    iCursorY++;
    kInitializeSerialPort();

    // 유후 태스크를 시스템 스레드로 생성하고 셸을 시작
    kCreateTask(TASK_FLAGS_LOWEST | TASK_FLAGS_THREAD | TASK_FLAGS_SYSTEM | TASK_FLAGS_IDLE,0,0,(QWORD)kIdleTask,kGetAPICID());

    // 그래픽 모드가 아니면 콘솔 셸 실행
    if(*(BYTE*)VBE_STARTGRAPHICMODEFLAGADDRESS==0)
    {
        kStartConsoleShell();
    }
    // 그래픽 모드면 그래픽 모드 테스트 함수 실행
    else
    {
        kStartGraphicModeTest();
    }
}
예제 #18
0
/**
 *  코어 별 인터럽트 처리 횟수를 증가
 */
void kIncreaseInterruptCount( int iIRQ )
{
    // 코어의 인터럽트 카운트를 증가
    gs_stInterruptManager.vvqwCoreInterruptCount[ kGetAPICID() ][ iIRQ ]++;
}