Task Switching(CALL 이용)

이전의 소스 중 kernel.asm 파일 내 jmp 문을 call 및 Process 루틴 하단의 jmp문을 iret 으로 변경하여 준다. (변경 시 Process2 명령어가 실행되고 이상하게 계속 재부팅이 된다..??)

여튼 .. 변경 후 원리를 알아보자..

GDT에 있는 TSS 디스크립터에 대하여 셀렉터를 사용한 CALL 명령에서 태스크 스위칭이 일어나고 또한 IRET 명령을 통해서도 태스크 스위칭이 일어난다.
이전 장에서 TSS 세그먼트 디스크립터에 포함된 Type 필드 내 1 0 B 1 이 존재하는 것을 배웠다. 그리고 EFLAG 레지스터의 14번 비트는 NT비트로 사용한다.

NT비트(Nested의 약자)는 IRET 명령을 행할 때 이것이 인터럽트 핸들러의 IRET인지, 태스크 스위칭되어 다시 이전의 태스크로 돌아가는 의미의 IRET인지를 CPU가 구별할 때 사용한다.
LTR 명령은 해당 TSS 디스크립터의 B 비트를 1로 세트한다. 그리고 CPU는 현재 실행되고 있는 태스크의 비트는 항상 1이라고 인식한다. CALL 명령에 의한 태스크 스위칭이 일어날 때 신태스크의 EFLAG에 있는 NT비트가 1로 세트된다. 이때 B비트도 1로 세트된다. 또한 TSS영역에 있는 이전 태스크로의 백링크 칸에 구태스크의 TSS 디스크립터 셀렉터를 저장해 둔다. 구태스크의 B비트는 1인채로 남아 있다.
IRET 명령을 사용하면 이전 태스크로 스위칭 된다. IRET 명령이 실행되기 위해 현재 실행중인 태스크의 NT비트가 반드시 1이어야 한다. 인터럽트의 IRET 명령과 구별하기 위함이다. 그리고 이전 태스크의 B비트도 1이어야 한다. IRET은 현재 태스크의 TSS 영역에 있는 이전 태스클의 백링크를 사용하여 이전 태스크를 찾는다. 이전 태스크로 돌아갈 때 현재 실행중인 태스크의 NT 비트는 0으로 클리어 되고, B비트도 0으로 클리어 된다.

CALL 명령과 IRET 명령에 의한 태스크 스위칭이 B비트, NT비트로 복잡한 규정이 설정되어 있다. 이것은 태스크 스위칭에 어느 정도 질서를 마련하기 위함이다.

TSS3에서 CALL TASK2:0과 같은 형식으로 이전 태스크로 돌아갈 수 없다. 왜냐하면 TASK2의 B비트가 1이 된 상태이기 때문이다. 이처럼 B 비트를 둔 이유는 CALL 명령을 사용한 태스크 스위칭에서는 태스크끼리 연결되어야 하고, CALL 명령을 사용해서 이전 태스크로 돌아가지는 못하게 하려는 뜻이다.

TASK1 실행 도중 CALL 명령을 통해 TASK2로 태스크 스위칭을 한다. TASK2 실행 도중 인터럽트가 발생한다. CPU는 인터럽트가 발생한 순간 CS, EIP와 함께 EFLAGS를 PUSH하여 스택에 저장한다. TASK2의 NT비트가 EFLAGS에 있으므로 이때 함께 저장된다. 그리고 CPU는 EFLAGS를 초기화하고, 인터럽트 핸들러를 실행한다. 인터럽트 핸들러에서 사용하는 EFLAGS에 있는 NT비트도 0으로 초기화 되는 것이다.
커널모드의 EFLAGS에는 NT비트가 0이므로 인터럽트 핸들러 실행을 마치고 IRET 할때에는 태스크 스위칭이 아닌 인터럽트 핸들러에서 인터럽트가 발생될 당시의 루틴으로 돌아가는 IRET의 의미가 된다. IRET 될 때에 스택에 저장되어 있던 CS, EIP, EFLAGS등이 다시 복원된다. 복원된 EFLAGS에는 TASK2가 사용하던 NT비트가 포함되어 있고, 이 비트가 1인 상태로 복원된다. TASK2가 실행을 마치고 IRET을 통해 이전 태스크로 스위칭할 때에는 NT비트가 1이므로 IRET이 태스크 스위칭을 위한 IRET의 의미가 된다. 그리하여 TASK1로 무사히 태스크 스위칭을 할 수 있다.

태스크 스위칭과 인터럽트 핸들링은 다른 방식으로 이루어 진다는 것을 알 수 있다.
여기까지 알 수 있는 것은 CALL 명령과 IRET 명령으로 태스크 스위칭을 하는 것은 태스크들이 하나의 체인으로 묶여 있고, CALL 명령으로 스위칭 된 태스크들은 CALL 명령을 통해 새로운 태스크로 스위칭 될 수 있으나 반드시 같은 순서로 IRET 명령을 통해 이전 태스크로 돌아오게 되어야 한다는 것이다. (CALL 명령은 커널 레벨에서 이루어지고, IRET은 태스크 자신의 루틴에 포함된다.)
이러한 방법은 태스크 관리에 있어 비선점 프로세스 관리에 해당된다. 하나의 프로세스가 다른 프로세스로 스위칭되고 난 후 구 프로세스는 신프로세스가 끝나기 전에는 절대로 실행이 재개될 수 없다.

JMP 명령에 의한 태스크 스위칭에 대해선 아래와 같다.

1. JMP 명령을 통해 TASK2로 점프한다. 이때 TASK1의 B비트는 0이건 1이건 무조건 0으로 세트한다. 그리고 태스크 스위칭 된 이후 TASK2의 B비트는 0에서 1로 세트된다. JMP 명령을 통한 태스크 스위칭은 신태스크의 B비트가 0인 상태에서만 가능하다. TASK2의 NT비트는 무조건 0이 된다.
2. 실행되고 있는 TASK2를 JMP 명령을 통해 TASK1로 스위칭을 하면 TASK2의 B 비트는 0이 되고, TASK1로 스위칭 된 후 TASK1의 B비트는 0에서 1이 된다. TASK1의 NT비트는 무조건 0이 된다.

태스크 스위칭 하는 동안 구태스크의 B비트가 0이 되므로 태스크 스위칭을 JMP 명령만으로 행한다면 B비트가 1이 되어 있는 것은 항상 현재 실행되고 있는 태스크뿐이라는 것을 알 수 있다. 태스크 스위칭 후 신태스크의 NT비트는 항상 0이므로 CALL 명령과는 달리 태스크끼리의 구속성이 없다. JMP 명령으로 태스크 스위칭을 하는 것은 상당히 자유스럽다는 것을 알 수 있다.

CALL 명령으로 태스크 스위칭을 하는 방법비선점형 태스크 스위칭을 위한 기능이다. JMP 명령은 유저 모드의 태 스크로 스위칭 하는 데에 약간의 문제점을 가지고 있다.

+ Recent posts