728x90
3. 제한적 직접 실행 원리(Limited Direct Execution)
- CPU를 가상화하기 위해서 운영체제는 여러 작업들이 동시에 실행되는 것처럼 보이도록 물리적인 CPU를 공유한다. 이러한 가상화 기법을 구현하기 위해서는 두 가지 문제를 해결해야 한다.
- 성능저하
- 제어문제
- 제어권을 유지하면서 성능 저하가 없도록 하는 것이 운영체제를 구축하는데 핵심적인 도전 과제이다.
3.1 기본원리 : 제한적 직접 실행
- 운영체제 개발자들은 프로그램을 빠르게 실행하기 위하여 제한적 직접 실행이라는 기법을 개발했다. 운영체제가 프로그램을 실행하기 시작할 때 프로세스 목록에 해당 프로세스 항목을 만들고 메모리를 할당하면 프로그램 코드를 디스크에서 탑재하고 진입점을 찾아 그 지점으로 분기하여 사용자 코드를 실행하기 시작한다.
- 이 접근법은 CPU를 가상화 함에 있어서 두 가지 문제를 일으킨다.
- 프로그램을 직접 실행 시킨다면 프로그램이 운영체제가 원치않는 일을 하지않는다고 어떻게 보장하는가?
- 프로세스 실행 시, 운영체제는 어떻게 프로그램의 실행을 중단하고, 다른 프로세스로 전환 시킬 수 있는가.
- 프로그램 실행에 제한을 두지 않으면 운영체제는 어떠한 것도 제어할 수 없으며 따라서 단순한 라이브러리일 뿐이다.
3.2 문제점 1 : 제한된 연산
- 직접 실행의 장점은 빠르게 실행된다. 그러나 CPU에서 직접 실행시키면 디스크 입출력 요청이나 CPU 또는 메모리와 같은 시스템 자원에 대한 추가 할당 요청이 필요하다.
- 프로세스가 디스크에 대하여 입출력 하는 것을 제한하지 않으면, 프로세스는 전체 디스크를 읽고 쓸 수 있기 때문에 접근 권한을 검사하는 기능이 의미가 없다.
- 이 때문에 사용자 모드(User mode)라고 알려진 새로운 모드가 도입됐다. 사용자 모드에서 실행되는 코드는 할 수 있는 일이 제한된다. 프로세스가 사용자 모드에서 실행중이면 입출력 요청을 할 수 없도록 설정한다. 이때 입출력 요청을 하면 프로세서가 예외를 발생시키고, 운영체제는 해당 프로세스를 제거한다.
- 커널모드(Kernel mode)는 운영체제의 중요한 코드들이 실행된다. 이 모드에서 실행되는 코드는 모든 특수한 명령어를 포함하여 원하는 모든 작업을 수행할 수 있다.
- 사용자 프로세스가 디스크를 읽기와 같은 특권 명령어를 실행해야 할 때 어떻게 해야 하는가? 이런 제한작업의 실행을 허용하기 위해서 거의 모든 현대 하드웨어는 사용자 프로세스에게 시스템 콜을 제공한다.
- 시스템 콜을 실행하기 위해 프로그램은 trap 특수 명령어를 실행 해야한다. 이 명령어는 커널 안으로 분기하는 동시에 특권수준을 커널모드로 상향조정한다. 커널 모드로 진입하면 운영체제는 모든 명령어를 실행 할 수 있고, 이를 통하여 프로세스가 요청한 작업을 처리할 수 있다. 완료되면 운영체제는 return-from-trap 특수 명령어를 호출한다.
- 이 명령어는 특권 수준을 사용자 모드로 다시 하향 조정하면서 호출한 사용자 프로그램으로 리턴한다.
- 하드웨어가 trap 명령어를 수행할 때, 호출한 프로세스의 필요한 레지스터들을 저장해야 한다. 운영체제가 return-from-trap 으로 사용자 프로세스로 리턴하기 위해서다. x86에서는 커널 스택(Kernel Stack)에 저장한다. return-from-trap 명령어가 이 값들을 스택에서 팝(pop)하여 사용자 모드 프로그램의 실행을 다시 시작한다.
- 커널은 부팅시에 트랩테이블(trap table)을 만들고 이를 이용하여 시스템을 통제한다. 컴퓨터가 부트될 때는 커널모드에서 동작하기 때문에 하드웨어를 원하는대로 제어할 수 있다. 운영체제가 하는 초기 작업중 하나는 하드웨어에게 예외사건이 일어났을 때 어떤 코드를 실행해야 하는지 알려주는 것이다.
- 운영체제는 특정 명령어를 사용하여 하드웨어에게 트랩 핸들러(trap handler)의 위치를 알려준다. 하드웨어는 이 정보를 전달받으면 해당 위치를 기억하고 있다.
- 모든 시스템 콜은 자신의 고유 번호를 갖는다. 사용자 프로그램은 원하는 시스템 콜을 호출하기 위해서, 해당 시스템 콜 번호를 레지스터 또는 스택에 지정된 위치에 저장한다. trap 명령어가 호출되면 이 명령어를 처리하는 trap 핸들러가 실행된다.
- trap 핸들러는 운영체제의 일부분이다. 운영체제는 시스템 콜 번호를 읽어 사용자가 명시한 시스템 콜 변호가 유효한 시스템 콜에 해당하는지를 먼저 파악한다. 유효한 시스템 콜로 판명되면 해당 코드로 이동하여 실행한다.
- 각 시스템 콜의 코드 위치는 운영체제만 알고있다. 시스템 콜 코드위치를 사용자 프로그램으로부터 숨김으로써, 사용자는 시스템 콜 번호를 이용하여 커널에게 시스템 콜의 실행을 요청해야 한다. 커널 코드의 무분별한 실행을 방지하기 위한 일종의 보안 기법이다.
- 프로세스는 커널 스택을 각자 가지고 있다. 커널 모드로 진입하거나 진출 할 때 하드웨어에 의해 프로그램 카운터와 범용레지스터 등의 레지스터가 저장되고 복원되는 용도로 사용된다.
- LDE 방식은 두 단계로 진행된다. 전반부에서(부팅 시) 커널은 트랩 테이블을 초기화하고 CPU는 나중에 사용하기 위하여 테이블의 위치를 기억한다. 커널은 이러한 작업을 커널모드에서만 사용할 수 있는 명령어를 이용하여 수행한다.
- 후반부에서(프로세스를 실행할 때) return-from-trap을 이용하여 사용자 프로세스를 시작할 때 몇 가지 작업을 수행한다. 새로운 프로세스를 위한 노드를 할당하여 프로세스 리스트에 삽입하고, 메모리를 할당하는 등의 작업이 포함된다. return-from-trap 명령어는 CPU를 다시 사용자 모드로 전환하고 프로세스 실행을 시작한다. 프로세스는 이후 자신의 할 일을 다하면 main()에서 리턴한다. 이때 일반적으로 스텁코드를 리턴하고 스텁코드가 프로그램을 종료시킨다. 종료시킬 때 exit() 시스템을 호출하고, 다시 운영체제로 트랩된다. 이 시점에 운영체제는 정리작업을 하게 되어 모든 일이 완료된다.
3.3 문제점 2 : 프로세스간 전환
- 프로세스의 전환은 간단해야 한다. 프로세스의 전환은 실행 중인 프로세스를 멈추고 다른 프로세스를 실행하는 것이다.
협조 방식 : 시스템 콜 호출시 까지 대기
- 협조(Cooperative) 방식으로 알려진 방법이다. 이 방식은 각 사용자 프로세스가 비정상적인 행동은 하지 않을것으로 가정한다. CPU를 장기간 사용해야 하는 프로세스들은 다른 프로세스들이 CPU를 사용할 수 있도록 주기적으로 CPU를 반납할 것이라고 믿는다. 착한 사람들만 사는 곳이다. 😊
- CPU를 반납하기 위해서는 운영체제가 해당 프로세스의 실행 상태를 저장해주어야 한다. CPU 반납 문제의 핵심은 응용 프로세스가 어떻게 제어권을 운영체제에게 넘기느냐로 귀결된다. 대부분의 프로세스는 시스템 콜을 자주 호출하는 것으로 알려져있다. 시스템 콜을 호출하면 자연스럽게 운영체제의 코드가 실행되며, 제어권이 운영체제로 넘어간다. 협조 방식을 사용하는 운영체제는 yield 시스템 콜을 제공한다. 이 시스템 콜은 운영체제에게 제어를 넘겨 운영체제가 CPU를 다른 프로세스에게 할당할 수 있는 기회를 제공한다.
- 응용프로그램이 비정상적인 행위를 하면 운영체제에게 제어가 넘어간다. 그러면 운영체제는 다시 CPU를 얻어 해당 프로세스를 종료할 수 있다.
- 협조 방식의 스케줄링 시스템은 근본적으로 수동적이다. CPU 제어권 획득을 위해 운영체제는 시스템 콜이든 불법적인 연산이 일어나기를 대기하는 것이다.
비협조 방식 : 운영체제가 제어권을 확보
- 타이머 인터럽트(timer interrupt)를 이용한다. 타이머는 수 밀리 초마다 인터럽트라 불리는 하드웨어 신호를 발생시키도록 프로그램 가능하다. 인터럽트가 발생하면 운영체제는 현재 수행 중인 프로세스를 중단시키고 해당 인터럽트에 대한 인터럽트 핸들러(interrupt handler)를 실행한다. 인터럽트 핸들러는 운영체제의 일부분이다. 인터럽트를 처리하는 과정에서, 제어권이 자연스럽게 운영체제로 넘어간다.
- 운영체제는 타이머 인터럽트가 발생 시 실행해야 할 코드의 주소를 기록해 두어야 한다. 타이머가 시작되면, 타이머 인터럽트가 발생할 때마다, 제어권이 운영체제에게 넘어간다. 타이머는 인터럽트를 주기적으로 발생시킨다. 운영체제는 사용자 프로그램이 비정상적으로 작동하는 경우가 발생하더라도 언제든지 해당 프로그램을 적절히 처리 할 수 있는 기회를 갖을 수 있다.
문맥의 저장과 복원
- 현재 프로세스를 계속 실행할 것인지, 다른 프로세스로 전환할 것인지 이런 결정은 운영체제의 스케줄러(Scheduler)라는 부분에 의해 내려진다.
- 현재 프로세스를 중단하고 다른 프로세스를 실행하기로 결정을 하면 운영체제는 문맥교환(context switching)이라 불리는 코드를 실행한다. 문맥교환은 현재 실행중인 프로세스의 레지스터 값들을 커널 스택 같은 곳에 저장하고, 새로이 실행될 프로세스 커널 스택으로부터 레지스터 값을 복원하는 것이 전부다. 그렇게 함으로써 운영체제는 return-from-trap 명령어가 마지막으로 실행될 때 현재 실행중이던 프로세스로 리턴하는 것이 아니라 다른 프로세스로 리턴하여 실행을 다시 시작할 수 있다.
- 문맥을 전환하는 과정에서 서로 다른 두 가지 종류의 레지스터의 저장/복원이 발생한다는 것을 주의해야 한다.
- 첫 번째는 타이머 인터럽트가 발생했을 때 일어난다.
- 두 번째는 운영체제가 A에서 B로 전환하기로 결정했을 때 일어난다. 이 경우 커널 레지스터는 운영체제에 의해 해당 프로세스의 프로세스 구조체에 저장된다.
728x90
'책 > 운영체제' 카테고리의 다른 글
운영체제 6. 스케줄링 : 멀티 레벨 피드백 큐(MLFQ : Multi-Level Feedback Queue) (1) | 2024.03.03 |
---|---|
운영체제 5. 스케줄링 : 개요 (0) | 2024.03.02 |
운영체제 3. 프로세스 API (0) | 2024.02.29 |
운영체제 2. 프로세스 (0) | 2024.02.29 |
운영체제 1. 운영체제 개요 (0) | 2024.02.25 |