1. 직접 실행 프로토콜과 문제점
프로그램을 CPU상에서 직접 실행시키면 CPU의 빠른 연산속도에 의해 프로그램을 빠르게 실행할 수 있을 것이다. 아무런 제한이 없는 직접 실행 프로토콜은 다음과 같이 이루어진다.
그러나 이러한 직접 실행 기법에서는 다음과 같은 문제점이 자연스럽게 대두된다.
- 프로그램에게 제한된 연산은 어떻게 수행하는가
- 시분할 기법의 구현은 어떻게 하는가
2. 제한된 연산의 수행방법
(1) 커널 모드와 시스템 콜
프로세스가 시스템 자원에 영향을 주는 특수한 연산(디스크 입출력 요청, 추가 메모리 할당 등)을 수행하길 원하는 경우, 이를 방치한다면 여러 문제가 발생할 수 있다. 악성 프로그램이 디스크 전체를 읽을 수 도 있고, 메모리를 무한정으로 할당하여 시스템을 다운시킬 수 도 있을 것이다. 이를 방지하기 위해서는 사용자 프로그램은 이런 특수한 연산을 직접 하지 못하도록 막을 필요가 있다.
이를 위해 CPU는 사용자 모드와 커널 모드라는 두 가지 모드를 갖는다. 사용자 모드는 일반 애플리케이션이 실행되는 모드로, 보안과 안정성을 위해 제한된 권한으로 실행된다. 이에 반해 커널 모드는 운영체제의 핵심 부분인 커널이 실행되는 모드로, 하드웨어에 대한 완전한 접근 권한을 가져 시스템 자원을 직접 관리하고 제어할 수 있다.
그렇다면 프로세스가 특수한 연산을 실행해야 할 때에는 어떻게 해야 할까? 바로 운영체제에 요청하는 방식으로 이루어져야 한다. 이렇게 프로세스가 '커널 모드에서 실행되는 운영체제의 기능'을 호출하는 방법을 시스템 콜이라 한다. 시스템 콜의 동작 방식은 다음과 같다.
- 프로그램이 open()과 같은 시스템 콜을 호출
- CPU가 trap 명령어를 실행 -> 커널 모드로 전환
- 운영체제가 요청을 처리
- 요청이 끝나면 return-from-trap을 실행 -> 사용자 모드로 전환
- 프로그램이 요청 결과를 받아서 계속 실행 됨
(2) 제한적 직접 실행 프로토콜
시스템 콜의 동작 방식에서 CPU가 trap 명령어를 실행하면 커널 모드로 전환되고 운영체제가 요청을 처리한다고 하였다. 요청을 처리한다는 것은 커널 모드에서만 실행할 수 있는 커널 내부의 어떤 코드를 실행한다는 의미이다. 이때 커널 내부에는 많은 코드가 있을텐데 trap이 발생했을 때 커널 내부에서 어느 지점의 코드를 실행할지 어떻게 알 수 있을까? 이 의문은 (보안과 안정성의 요구로) 사용자 모드와 커널 모드가 분리되어 사용자 프로그램이 커널 함수 주소를 직접 호출할 수 는 없다는 점에서 비롯된다.
여기서 등장하는 개념이 트랩 테이블과 트랩 핸들러이다. 트랩 테이블은 각 트랩 번호와 그에 대응하는 트랩 핸들러의 시작 주소를 미리 기록한 자료구조이고, 트랩 핸들러는 트랩 테이블에 의해 지정된 특정 커널 코드이다. 커널은 부팅 시에 트랩 테이블을 만들고, 프로그램이 실행되다가 트랩이 발생하면 CPU가 트랩 테이블을 참조하여 해당 핸들러를 실행하는 것이다. 다음 그림에서 구체적인 동작 과정을 살펴보자.
이렇게, 사용자 프로그램은 직접 커널 함수의 주소를 지정하지 않고 trap 명령어를 통해 커널 모드로 진입하며, 운영체제는 트랩 테이블에 기록된 정보를 이용해 적절한 트랩 핸들러를 실행한다. 이러한 메커니즘은 사용자 모드와 커널 모드의 분리 원칙을 준수하면서 필요한 커널 기능을 안전하게 호출할 수 있게 해주는데, 이 전체 과정을 제한적 직접 실행(LDE, Limited Direct Execution) 프로토콜이라고 한다.
3. 시분할 기법의 구현
(1) 운영체제의 CPU 획득 방법
시분할 기법, 즉 프로세스간 전환은 어떻게 이루어져야 할까? 실행중인 프로세스를 계속 실행할 지 혹은 멈추고 다른 프로세스를 실행할 것인지 운영체제가 결정해야 한다. 그런데 현재 CPU에서 프로세스가 실행중이기 때문에 운영체제는 실행중이지 않다. 결국 프로세스간 전환은 운영체제가 CPU를 어떻게 다시 획득하는지의 문제로 귀결된다.
협조 방식은 시스템 콜이 호출되기를 기다리는 방식이다. 즉, 운영체제가 다른 작업을 실행할 결정을 할 수 있도록 주기적으로 프로세스가 CPU를 포기할 것으로 가정하는 것이다. 이외에 프로그램이 비정상적인 행위를 하여 트랩이 일어나기를 기다려 CPU의 제어권을 다시 획득한다.
프로세스가 비협조적인 상황에서도 CPU의 제어를 획득할 필요가 있을 것이다. 비협조 방식으로 타이머 인터럽트를 이용하는 방법이 가능하다. 타이머 장치가 수 밀리 초마다 인터럽트를 발생시키도록 프로그램을 하고, 인터럽트가 발생하면 현재 수행중인 프로세스가 중단되고 미리 구성된 운영체제의 인터럽트 핸들러가 실행되는 방식이다. 이 시점에서 운영체제는 CPU 제어권을 다시 얻게 된다.
(2) 문맥의 저장과 복원
운영체제가 CPU 제어권을 다시 얻으면 운영체제의 스케줄러 정책에 따라 현재 실행중인 프로세스를 계속 실행할 것인지 혹은 다른 프로세스로 전환할 것인지 결정한다.
다른 프로세스로 전환하는 경우 운영체제는 문맥 교환 코드를 실행한다. 문맥 교환이란 CPU가 한 프로세스의 실행을 중단하고 다른 프로세스의 실행을 재개하는 과정으로, 이때 운영체제는 현재 실행중인 프로세스의 레지스터 값을 커널 스택에 저장하고 곧 실행될 프로세스의 커널 스택으로부터 레지스터 값을 복원하는 작업을 수행한다. 이를 통해 return-from-trap 명령어를 통해 다른 프로세스로 리턴하여 실행을 다시 시작할 수 있게 된다.
'독서 > 운영체제 아주 쉬운 세 가지 이야기' 카테고리의 다른 글
[OSTEP] CPU 가상화 : MLFQ 스케줄러 (0) | 2025.03.06 |
---|---|
[OSTEP] CPU 가상화 : 스케줄링 개요 (0) | 2025.03.04 |
[OSTEP] CPU 가상화 : 프로세스 (0) | 2025.02.28 |
[OSTEP] Intro : 운영체제 개요 (0) | 2025.02.27 |
책 소개 : 운영체제 아주 쉬운 세 가지 이야기 (0) | 2025.02.16 |