가상화 구현시 문제점
- 성능 저하: 시스템에 과중한 오버헤드를 줄이고 가상화를 구현할 수 있을까?
- 제어 문제: CPU에 대한 통제를 유지하면서 프로세스를 효율적으로 실행시킬 수 있는 방법은 무엇인가?
- 즉, 제어를 유지하면서 효과적으로 CPU를 가상화하기 위해선
하드웨어
와 운영체제
의 지원이 필수적이다.
기본 원리: 제한적 직접 실행(Limited Direct Execution)
- 프로그램을 CPU 상에서 직접 실행시키는 것이다.
문제점 1: 제한된 연산
- 프로세스는 입출력 연산이나 메모리와 같은 시스템 자원에 대한 추가 할당 요청 등의 제한된 연산을 수행할 수 없다.
사용자 모드(user mode)
와 커널 모드(kernel mode)
가 도입되었다.
- 사용자 모드에서 실행되는 코드는 할 수 있는 일이 제한되고, 커널모드에서는 모든 작업을 할 수 있다.

시스템 콜(system call)과 트랩(trap)
- 현대
하드웨어
는 사용자 프로세스가 특권 명령
을 실행할 수 있게 하기 위해 시스템 콜(system call)
을 제공한다.
- 시스템 콜을 실행하기 위해서는
trap 특수 명령어
를 실행해야 한다. 이 명령어는 커널 안으로 분기하는 동시에 특권 수준을 커널 모드로 상향 조정한다.
- 시스템콜이 완료되면 운영체제는
return-from-trap 특수 명령어
를 호출하고, 특권 수준을 사용자 모드로 하향하면서 호출한 사용자 프로그램으로 리턴한다.
- 운영체제가 return-from-trap 실행 시 사용자 프로세스로 제대로 리턴할 수 있도록 플래그, 레지스터 들을 각
프로세스의 커널 스택(kernel stack)에 저장
한다.
trap이 운영체제 코드의 어디를 실행할 지 어떻게 아는가?
호출한 프로세서는 분기할 주소를 명시할 수 없다.
주소를 명시한다는 것은 커널 내부의 원하는 지점을
접근할 수 있다는 것이기에 위험하다.
- 커널은 부팅시에
트랩 테이블(trab table)
을 만들어 시스템을 통제한다.
- 운영체제는 하드웨어에게 예외 사건이 일어났을 때 어떤 코드를 실행해야 하는지 알려준다.
- 운영체제는 특정 명령어를 사용하여 하드웨어에게
트랩 핸들러(trap handler)
의 위치를 알려준다.
- 따라서, 시스템 콜과 같은 예외적인 사건이 발생했을 때 하드웨어는 어느 코드로 분기하여 실행할 지 알 수 있다.
- 하드웨어에게 트랩 테이블의 위치를 알려주는 것은 매우 강력한 기능이다. 사용자에게 이 명령어를 실행하려고 하면 운영체제는 그 프로세스를 제거한다.

문제점 2: 프로세스 간 전환
CPU에서 프로세스가 실행 중이라는 것은
운영체제는 실행 중이지 않다는 것을 의미한다.
운영체제가 실행하고 있지 않다면, 프로세스간 전환을 어떻게 하겠는가?
협조 방식: 시스템 콜 기다리기
- 대부분의 프로세스는 자주 시스템 콜을 호출하여 CPU의 제어권을 운영체제에게 넘겨준다. yield 시스템 콜은 운영체제가 다른 프로세스를 실행할 수 있게 한다.
- 악의적이든 버그든 무한 루프에 빠져 시스템 콜을 호출할 수 없게 되었을 때 운영체제가 할 수 있는 일은 무엇인가?
비협조 방식: 운영체제가 전권 행사
- 이러한 문제를 해결하기 위해
타이머 인터럽트(timer interrupt)
를 이용한다.
- 타이머 장치는 수 밀리 초마다 인터럽트를 발생시킨다. 인터럽트가 발생하면 현재 수행 중인 프로세스는 중단되고 미리 구성된 운영체제의
인터럽트 핸들러(interrupt handler)
가 실행된다.
- 인터럽트 발생 시 하드웨어는 실행 중이던 프로세스의 상태를 저장하여 나중에 refurn-from-trap 명령어가 프로그램을 다시 시작할 수 있도록 한다.
- 프로세스 전환을 위하여 운영체제는 어셈블리 코드를 사용하여
현재 실행 중인 프로세스의 범용 레지스터
, PC
뿐 아니라 현재 커널 스택 포인터
를 저장한다. 그리고 곧 실행될 프로세의 범용 레지스터, PC를 복원하고 커널 스택을 이 프로세스의 커널 스택으로 전환된다.
- 이 과정에서 두 번의 레지스터의 저장/복원이 일어난다.
- 첫 번째는 타이머 인터럽트가 발생했을 때 일어난다.
- 실행 중인 프로세스의 사용자 레지스터가 하드웨어에 의해 암묵적으로 저장되고 저장 장소로 해당 프로세스의 커널 스택이 사용된다.
- 두 번째는 운영체제가 A에서 B로 전환하기로 결정했을 때 일어난다.
- 커널 레지스터는 운영체제에 의하여 해당 프로세스의 프로세스 구조체에 저장된다. 이것은 운영체제가 A가 아닌 B로부터 커널로 트랩된 것처럼 만든다.

시스템 콜을 처리하는 도중 타이머
인터럽트가 발생하면 어떤 일이 발생하는가?
하나의 인터럽트를 처리하고 있을 때
다른 인터럽트가 발생하면 어떤 일이 생기는가?
- 병행성 문제
- 운영체제는 인터럽트를 처리하는 동안
인터럽트를 불능화
시킨다.
프로세스 전환 어셈블리 코드
.globl switch
switch:
# Save old register
movl 4(%esp), %eax # old포인터를 eax에 넣는다.
popl 0(%eax) # old IP를 저장한다.
movl %esp, 4(%eax) # 그리고 스택
movl %ebx, 8(%eax) # 그리고 다른 레지스터
movl %ecx, 12(%eax)
movl %edx, 16(%eax)
movl %esi, 20(%eax)
movl %edi, 24(%eax)
movl %ebp, 28(%eax)
# Load new register
movl 4(%esp), %eax # new포인터를 eax에 넣는다.
movl 28(%eax), %ebp # 다른 레지스터를 복원한다.
movl 24(%eax), %edi
movl 20(%eax), %esi
movl 16(%eax), %edx
movl 12(%eax), %ecx
movl 8(%eax), %ebx
movl 4(%eax), %esp # 스택은 이 지점에서 전환된다.
pushl 0(%eax) # 리턴 주소를 지정된 장소에 넣는다.
ret # 마지막으로 new문맥으로 리턴한다.
참고 자료