『Operating Systems: Three Easy Pieces』 6장은 제한적 직접 실행(Limited Direct Execution, LDE)을 다룹니다.

처음에는 “프로그램을 CPU에서 실행하되 위험한 동작을 제한한다” 정도로 이해했습니다. 하지만 사용자 모드와 커널 모드, 시스템 콜, 문맥 교환을 따라가면서 잘못 이해한 부분이 하나씩 드러났습니다.

특히 다음 질문들이 이해를 바꾸는 계기가 됐습니다.

  • 프로그램이 커널에 작업을 요청한다면, 왜 CPU의 모드를 바꿔야 할까요?
  • 커널은 CPU와 별도로 요청을 처리하는 존재일까요?
  • 인터럽트 때 상태를 저장했는데, 문맥 교환 때는 왜 또 저장할까요?
  • 다음 작업은 스케줄러가 선택한다면서, 어셈블리에서는 왜 +4 위치를 읽을까요?

이 글은 6장의 개념과 함께, 그 질문들을 풀어 간 과정을 정리한 기록입니다. CPU 코어 하나와 프로세스마다 실행 흐름 하나를 가정합니다. 레지스터와 스택의 세부 동작은 책의 모델을 기준으로 설명합니다.

1. 직접 실행은 왜 빠를까요?

운영체제는 여러 프로세스가 CPU를 나누어 쓰게 합니다. 하나를 잠깐 실행하고 다른 하나를 실행하는 시분할이 기본 아이디어입니다.

시간 →
[A 실행] [B 실행] [C 실행] [A 실행] …

여기에는 두 가지 목표가 있습니다.

  • 성능: 프로그램 실행에 드는 관리 비용을 작게 유지해야 합니다.
  • 통제권: 운영체제가 자원 사용과 실행 차례를 관리할 수 있어야 합니다.

성능을 위해서는 프로그램의 명령어를 CPU가 직접 실행하는 것이 좋습니다. 운영체제가 명령어를 하나씩 읽고 대신 수행한다면 추가 비용이 들기 때문입니다.

하지만 프로그램을 마음대로 실행하게 두면 두 문제가 생깁니다.

  1. 허용되지 않은 메모리나 장치에 접근하려 한다면 어떻게 막을까요?
  2. CPU를 돌려주지 않고 계속 실행한다면 어떻게 멈출까요?

이 두 문제를 해결하는 것이 이름의 “제한적”에 해당합니다. 메모리 접근의 보호에는 주소 변환과 접근 권한 검사도 필요합니다. 이 글에서는 6장의 흐름에 따라 특권 연산과 CPU 제어권에 집중합니다.

2. 직접 실행하기 전에 무엇을 준비할까요?

직접 실행한다고 해서 디스크에 있는 프로그램을 아무 준비 없이 실행하는 것은 아닙니다. 운영체제는 대략 다음 작업을 수행합니다.

  1. 프로세스 관리 목록에 항목을 만듭니다.
  2. 프로그램에 필요한 메모리를 마련합니다.
  3. 프로그램 코드를 메모리에 적재합니다.
  4. 사용자 스택과 프로그램 인수를 준비합니다.
  5. 초기 레지스터 값과 실행 시작 위치를 준비합니다.
  6. 사용자 모드에서 실행을 시작합니다.

책의 그림은 시작점을 main()으로 표현합니다. 실제로는 main()을 호출하기 전에 초기화 코드를 실행할 수도 있습니다.

여기까지는 “운영체제가 환경을 마련하고 CPU가 프로그램을 실행한다”는 흐름으로 이해했습니다. 혼동은 커널 모드가 등장하면서 시작됐습니다.

3. “커널에 읽어 달라고 요청하는 것 아닌가요?”

사용자 프로그램은 디스크를 마음대로 제어할 수 없지만 파일을 읽을 수는 있어야 합니다. 이를 위해 시스템 콜(system call)로 커널에 요청합니다.

저는 이 설명을 듣고 다음과 같이 생각했습니다.

당시 생각한 흐름

사용자 프로그램이 커널에 요청
        ↓
커널이 CPU에 요청
        ↓
CPU가 커널의 요청임을 확인하고 수행
        ↓
커널이 프로그램에 결과 전달

이렇게 생각하니 모드 전환이 이상하게 느껴졌습니다.

커널이 대신 작업한다면 사용자 프로그램의 권한을 높일 필요가 없지 않을까요? CPU는 요청이 커널에서 왔는지만 확인하면 되지 않을까요?

요청을 받은 커널은 어디에서 실행될까요?

A가 파일 읽기를 요청하는 시스템 콜 하나를 따라갑니다. 청록색 띠가 CPU가 지금 실행하는 위치입니다. 단계를 누르면 그 시점에서 멈춥니다.

바로 끝나는 시스템 콜 하나를 그린 개념 모형입니다. 입출력을 기다리는 동안 다른 프로세스로 전환하는 경로와 상태 저장은 생략했습니다. 코드 줄은 흐름을 보여주는 요약입니다.

문제는 커널을 CPU와 별도로 움직이는 요청 처리 주체처럼 생각한 것이었습니다.

커널도 CPU가 실행하는 코드입니다. CPU가 사용자 코드를 실행하다가, 시스템 콜을 계기로 커널 코드를 실행합니다.

같은 CPU가
사용자 코드 실행 → 커널 코드 실행 → 사용자 코드 실행
   사용자 모드       커널 모드        사용자 모드

이렇게 이해하자 “커널에 요청한다”는 표현과 “CPU가 커널 모드로 전환된다”는 표현이 연결됐습니다.

  • 역할 관점: 프로그램이 요청하고 커널이 처리합니다.
  • 실행 관점: CPU가 사용자 코드에서 커널 코드로 넘어가 요청을 처리합니다.

높은 권한으로 실행되는 코드는 커널 코드입니다. 사용자 프로그램의 코드가 높은 권한을 얻어 계속 실행되는 것은 아닙니다.

4. CPU는 현재 실행 모드로 권한을 구분합니다

CPU 명령어에 “커널이 보낸 명령어”라는 신뢰할 수 있는 발신자 표시가 붙어 있는 것은 아닙니다. 사용자 프로그램도 특권 명령어를 자기 코드에 넣을 수 있습니다.

CPU는 하드웨어가 관리하는 현재 실행 모드를 기준으로 특권 명령어의 실행 가능 여부를 판단합니다.

상황동작
사용자 모드에서 허용된 일반 연산 수행실행
사용자 모드에서 허용되지 않은 특권 명령어 실행 시도차단하고 예외 발생
허용된 시스템 콜 진입 절차 수행정해진 커널 코드로 이동하며 모드 전환
커널 모드에서 특권 명령어 실행실행 가능

일반 명령어라도 허용되지 않은 메모리에 접근하면 예외가 발생할 수 있습니다. 위 표는 실행 모드에 따른 특권 명령어의 제한을 구분한 것입니다.

책에서 트랩(trap)이라고 부르는 진입 동작은 실행 위치와 권한을 함께 바꿉니다.

사용자 코드에서 요청 준비
        ↓
트랩
  · 정해진 커널 진입점으로 이동
  · 커널 모드로 전환
        ↓
커널이 요청 확인 및 처리

저는 중간에 “그러면 커널은 절차를 뜻하는 것인가?”라고도 생각했습니다. 이것도 구분이 필요했습니다.

커널은 운영체제의 핵심 코드이고, 트랩은 그 코드로 안전하게 진입하는 방식입니다. 여기서는 OSTEP 6.2절의 용어와 모델을 따릅니다.

응답을 기다리는 상태가 커널 모드일까요?

실행 모드와 프로세스 상태는 다른 구분입니다.

  • 사용자 모드·커널 모드: 어떤 권한으로 코드를 실행하는가?
  • 실행·준비·대기 상태: 프로세스가 실행 중인가, CPU나 이벤트를 기다리는가?

커널에서 입출력을 시작한 뒤 프로세스를 대기 상태로 바꿀 수 있습니다. 그동안 CPU는 다른 프로세스를 실행합니다. 그 대기 자체를 커널 모드라고 부르지는 않습니다.

5. 커널의 진입 위치는 누가 정할까요?

파일 읽기 코드가 다음 순서로 동작한다고 가정해 보겠습니다.

권한 검사 → 파일 읽기 → 결과 반환

사용자 프로그램이 커널의 임의 주소로 들어갈 수 있다면, 권한 검사를 건너뛰고 파일 읽기부터 실행하게 만들 수 있습니다.

따라서 프로그램이 요청할 기능과 인수를 지정하는 것과, 커널의 실행 시작 주소를 지정하는 것은 다릅니다.

책의 모델에서 운영체제는 부팅할 때 트랩 테이블과 처리 코드의 위치를 설정합니다. 하드웨어는 이를 바탕으로 정해진 경로로 커널에 진입합니다. 이 설정을 변경하는 작업도 특권 연산입니다.

보호에는 두 단계가 필요합니다.

  1. 사용자 프로그램은 정해진 경로로 커널에 들어갑니다.
  2. 그 경로 자체도 사용자 프로그램이 마음대로 바꾸지 못합니다.

진입한 뒤 요청과 인수를 검사하는 일은 커널 코드가 수행해야 합니다. 구체적으로 진입점을 등록하는 하드웨어 장치는 아키텍처에 따라 달라집니다.

6. 처음 실행하는데 왜 ‘트랩 복귀’를 사용할까요?

프로그램 실행 그림에는 의외의 동작이 나옵니다. 커널이 새 프로그램을 시작할 때 트랩 복귀(return-from-trap)를 사용합니다.

아직 실행된 적이 없는데 어디로 복귀한다는 것일까요?

한 번도 실행되지 않았는데 어디로 복귀할까요?

두 경우 모두 같은 트랩 복귀 절차를 거칩니다. 경우를 고른 뒤 복귀 절차를 진행해 보세요.

OSTEP 그림 6.2의 모델을 줄여 그린 개념 모형입니다. 실제로 저장·복원하는 레지스터의 종류와 위치는 CPU와 운영체제에 따라 다르고, 값은 주소 대신 의미로 적었습니다. 초기 PC는 main()이 아닌 프로그램 시작점으로 표시했습니다.

핵심은 복귀 명령이 사용할 상태를 커널이 미리 만들어 놓는다는 데 있습니다.

일반적인 복귀에서는 이전에 저장한 값을 사용합니다. 첫 실행에서는 커널이 준비한 초기 값을 사용합니다.

일반적인 복귀
저장된 실행 상태 → 복원 → 중단했던 사용자 코드 실행

첫 실행
커널이 준비한 초기 상태 → 복원 → 사용자 코드 실행 시작

커널이 초기 PC(다음에 실행할 명령의 위치)와 필요한 레지스터 값 등을 준비하면, 복귀 절차는 그 값을 사용하여 사용자 모드로 나갑니다.

여기서 “복원”은 반드시 실제 과거로 돌아간다는 뜻이 아닙니다. 복귀 절차가 읽을 값을 준비해 두고, 그 절차를 첫 실행에도 활용하는 것입니다. 이 설명은 책의 그림 6.2를 기준으로 합니다.

7. 시스템 콜에서 돌아올 때와 종료할 때는 어떻게 다를까요?

커널 코드를 실행하는 동안에도 CPU 레지스터는 사용됩니다. 따라서 사용자 코드로 돌아갈 때 필요한 상태를 먼저 보존해야 합니다.

정상적으로 반환하는 시스템 콜은 개념적으로 다음 흐름을 따릅니다.

사용자 실행 상태 저장
        ↓
커널에서 요청 처리
        ↓
필요한 상태 복원과 결과 전달
        ↓
트랩 다음 명령부터 사용자 코드 실행

트랩 명령을 다시 실행하는 위치로 돌아가면 같은 요청을 반복하게 되므로, 그다음부터 이어 실행합니다. 반환값은 약속된 레지스터 등 정해진 방식으로 전달합니다. 여기서는 시스템 콜 재시작 같은 예외 경로는 생략합니다.

종료할 때의 흐름은 다음과 같습니다.

main()에서 반환
        ↓
실행 지원 코드의 종료 처리
        ↓
프로세스 종료 시스템 콜
        ↓
커널이 자원을 정리하고 프로세스 종료

정상적으로 수행된 프로세스 종료 시스템 콜은 해당 사용자 프로그램으로 돌아오지 않습니다. Unix 계열에서는 부모가 종료 결과를 확인할 수 있도록 일부 정보가 남을 수도 있습니다. 참고: Linux _exit(2) 매뉴얼.

8. 시스템 콜을 하지 않는 프로그램은 어떻게 멈출까요?

시스템 콜이 발생하면 커널이 실행 기회를 얻습니다. 프로그램이 주기적으로 시스템 콜이나 yield를 호출한다고 믿는 것이 협력적 방식입니다.

하지만 다음과 같은 프로그램은 어떨까요?

while (1) {
    // 시스템 콜 없이 계속 실행
}

무한 반복 자체는 금지된 동작이 아닙니다. 허용된 명령어만 실행한다면 예외도 발생하지 않을 수 있습니다.

여기서 “커널도 CPU가 실행하는 코드”라는 이해가 다시 중요해집니다. 운영체제에 프로그램을 멈추는 코드가 있어도, 그 코드를 실행할 기회가 없으면 아무것도 할 수 없습니다.

A가 양보하지 않아도 B를 실행할 수 있을까요?

A는 시스템 콜 없이 반복만 합니다. 모형을 고르고 한 구간을 실행해 보세요. 아래 시간축에 CPU가 어느 코드를 실행했는지 쌓입니다.

코어 하나에 다른 인터럽트와 예외가 없는 비교 모형입니다. 시간축의 길이는 실제 밀리초나 전환 비용을 나타내지 않습니다. 타이머의 유무는 비교를 위한 설정이고, A가 조작할 수 있는 기능이 아닙니다. B로 바꾸는 문맥 교환의 세부 과정은 다음 장면에서 다룹니다.

해결책은 타이머 인터럽트입니다.

A 실행
   ↓
타이머 인터럽트
   ↓
커널의 처리 코드 실행
   ↓
A를 계속 실행하거나 다른 프로세스 선택

책의 모델에서 운영체제는 부팅 과정에서 처리 코드를 등록하고 타이머를 시작합니다. 타이머를 조작하는 것도 특권 연산이므로 사용자 프로그램이 마음대로 꺼 버릴 수 없습니다.

이제 커널은 프로그램의 자발적인 양보 없이도 실행 기회를 얻습니다.

9. 다음 작업은 누가 고를까요?

이 대목에서 runqueue가 떠올랐습니다. 다음 작업을 고르는 방법이 runqueue인지 질문했습니다. 정리하면 다음과 같습니다.

개념역할
runqueue실행 가능한 작업들을 관리하는 자료구조
스케줄링 정책누구를 선택할지 정하는 기준
스케줄러그 기준에 따라 실행 대상을 선택하는 코드
문맥 교환선택한 대상이 실행되도록 실행 상태를 저장·복원하는 작업

추가 질문으로 Linux의 dl_rq, rt_rq, cfs_rq도 연결했습니다. 이들은 각각 deadline, real-time, fair 스케줄링에 쓰이는 실행 후보와 관련 상태를 관리합니다. 참고: Linux v6.12의 실행 큐 구조체 정의.

이때 큐의 이름만 보고 선택 알고리즘을 단정해서는 안 됩니다. Linux의 fair 스케줄러는 EEVDF로 전환되어 왔으며, cfs_rq라는 이름은 여전히 사용됩니다. 특정 커널의 동작을 설명하려면 버전과 구현을 함께 확인해야 합니다. 참고: Linux 커널의 EEVDF 문서.

책의 흐름으로 돌아오면, 스케줄러가 B를 선택한 뒤 문맥 교환 코드가 실제로 B를 실행할 상태를 만듭니다. 타이머 인터럽트가 발생했다고 반드시 B로 바뀌는 것도 아닙니다. 스케줄러가 A를 계속 실행하기로 할 수 있습니다.

10. 왜 레지스터 상태를 두 번 저장할까요?

그림 6.3에서 가장 어려웠던 부분은 레지스터 저장·복원이 두 단계로 등장한다는 것이었습니다.

같은 레지스터를 왜 두 번 저장할까요?

CPU의 레지스터 묶음은 하나뿐입니다. 칩은 그 묶음에 담긴 값이 어느 시점의 것인지를 나타냅니다. 단계를 누르면 그 시점에서 멈춥니다.

OSTEP 그림 6.3의 저장 위치를 따른 개념 모형입니다. B는 이전에 실행되다 멈춰서 사용자·커널 시점의 상태가 이미 저장돼 있다고 가정합니다. 실제로 하드웨어와 소프트웨어가 어떤 레지스터를 어디에 나누어 저장하는지는 CPU와 구현에 따라 다릅니다.

첫 번째: A의 사용자 실행 상태 보존

A의 사용자 코드를 실행하던 중 인터럽트가 발생합니다. 나중에 A의 사용자 코드로 돌아가기 위해 필요한 상태를 저장합니다.

책의 그림에서는 하드웨어가 이 상태를 A의 커널 스택에 저장합니다.

두 번째: A의 커널 실행 상태 보존

인터럽트 이후 CPU는 커널 코드를 실행합니다. 커널 코드도 레지스터와 스택을 사용합니다.

여기서 B로 전환하려면 지금 진행 중인 A의 커널 실행 상태를 저장하고, B가 저장해 둔 커널 상태를 복원해야 합니다.

그림에서는 문맥 교환 코드가 이를 프로세스 구조체의 저장 공간에 보관합니다.

저장 시점보존하는 상태그림의 저장 위치
인터럽트 진입A의 사용자 코드 실행 상태A의 커널 스택
A → B 문맥 교환A의 커널 코드 실행 상태A의 프로세스 구조체

전체 흐름은 다음과 같습니다. 여기서는 B가 이전에 실행되다가 멈추었고, 재개할 상태가 저장되어 있다고 가정합니다.

A의 사용자 코드
      ↓ A의 사용자 상태 저장
A의 커널 실행 흐름
      ↓ A의 커널 상태 저장
      ↓ B의 커널 상태 복원 및 스택 전환
B의 커널 실행 흐름
      ↓ B의 사용자 상태 복원
B의 사용자 코드

“사용자 레지스터”와 “커널 레지스터”는 서로 다른 실행 시점의 값을 구분하는 표현입니다. 서로 완전히 별개의 물리적 레지스터 묶음이라는 뜻은 아닙니다.

이 흐름과 저장 위치는 OSTEP 그림 6.3의 모델입니다. 실제로 하드웨어와 소프트웨어가 어떤 값을 나누어 저장하는지는 CPU와 구현에 따라 달라집니다.

11. +4 위치에서 읽는 것은 무엇일까요?

책의 32비트 x86 문맥 교환 코드에는 다음 명령이 나옵니다.

movl 4(%esp), %eax

처음에는 이렇게 생각했습니다.

다음 작업은 우선순위에 따라 선택할 텐데, 왜 바로 4바이트 뒤에 있을까요? B에서 A로 돌아가면 -4를 읽어야 하는 것 아닌가요?

여기서 읽는 것은 함수 인수입니다. A에서 B로 전환할 때 함수에 전달하는 정보는 다음과 같습니다.

old = A의 문맥을 저장할 공간의 주소
new = B의 문맥이 저장된 공간의 주소

함수 진입 직후의 스택은 다음 형태입니다.

esp     → 커널 복귀 주소
esp + 4 → old
esp + 8 → new

스택에 나란히 놓인 것은 주소 값입니다. 주소가 가리키는 A와 B의 저장 공간은 멀리 떨어져 있어도 됩니다. 아래 주소는 설명을 위한 예시입니다.

old = 0x1000 → A의 저장 공간
new = 0x9000 → B의 저장 공간

같은 +4가 어떻게 old와 new를 따로 읽을까요?

swtch 진입 직후의 스택입니다. 명령을 한 단계씩 진행해 보세요. 전환 방향을 바꾸면 처음부터 다시 시작합니다.

OSTEP 그림 6.4의 32비트 x86 코드 일부를 옮긴 모형입니다. 스택 칸의 주소와 저장 공간 주소 0x1000·0x9000은 설명을 위한 예시입니다. popl로 꺼낸 칸의 값은 메모리에 남지만 흐리게 표시했습니다. 다른 레지스터의 저장·복원은 생략했습니다.

앞부분에서 popl로 복귀 주소를 꺼내면 %esp가 4바이트 이동합니다.

popl 이후

esp     → old
esp + 4 → new

그래서 같은 4(%esp)가 처음에는 old, 나중에는 new를 가리킵니다.

B에서 A로 전환할 때도 인수가 놓이는 위치는 같습니다. old에 B의 주소, new에 A의 주소를 전달합니다.

+4는 이 코드의 함수 호출 방식에서 정해진 인수 위치였습니다. 이 설명은 그림 6.4에 실린 코드를 기준으로 하며, 다른 버전의 xv6나 다른 아키텍처의 코드에는 그대로 적용할 수 없습니다.

12. ret를 실행하면 어디로 돌아갈까요?

문맥 복원 코드의 마지막 부분도 중요했습니다.

movl 4(%eax), %esp
pushl 0(%eax)
ret

이 시점에서 %eax는 B의 저장된 문맥을 가리킵니다. 앞 절의 4(%esp)가 스택의 인수 위치였다면, 여기의 4(%eax)는 문맥 구조체 안에 저장된 스택 포인터의 위치입니다.

  1. B의 스택 포인터를 %esp에 넣어 B의 커널 스택을 사용합니다.
  2. B가 저장한 커널 복귀 주소를 그 스택에 넣습니다.
  3. ret가 그 주소를 꺼내 실행을 이어갑니다.

즉, A의 문맥에서 호출한 함수가 B의 저장된 커널 흐름으로 이어집니다.

swtch의 ret는 바로 B의 사용자 코드로 갈까요?

A의 커널 코드에서 호출한 swtch의 마지막 세 명령과 그 뒤를 따라갑니다. 청록색 띠가 CPU가 실행하는 위치입니다.

OSTEP 그림 6.4의 마지막 명령과 그림 6.3의 전체 흐름을 이어 붙인 개념 모형입니다. 스택에는 설명에 필요한 칸만 그렸고, 다른 레지스터의 복원과 B가 이전에 멈춘 과정은 생략했습니다. ret와 트랩 복귀 사이의 커널 처리는 줄여 그렸습니다.

A의 커널 코드에서 swtch 호출
        ↓
A의 상태 저장
B의 상태와 스택 복원
        ↓ ret
B의 커널 코드 실행 재개
        ↓ 이후 트랩 복귀
B의 사용자 코드 실행 재개

여기의 ret는 커널 모드를 유지합니다. 사용자 모드로 전환하는 것은 이후의 트랩 복귀입니다.

스택 포인터를 바꾸면 이어갈 함수 호출 흐름도 바뀔 수 있다는 점이 여기서 이해됐습니다.

13. 커널이 실행되는 동안에도 동시성을 고려해야 합니다

커널이 자료구조를 수정하는 중에 인터럽트가 발생하면, 다른 처리 코드가 중간 상태를 볼 수 있습니다.

예를 들어 목록에 항목을 연결한 뒤 개수를 갱신하기 전에 다른 코드가 실행되면 두 정보가 맞지 않을 수 있습니다.

책은 이를 다루는 방법으로 다음을 소개합니다.

  • 인터럽트 비활성화: 현재 CPU에서 마스킹 가능한 인터럽트 처리가 끼어드는 것을 제한합니다.
  • 락: 같은 자료에 접근하는 여러 실행 흐름을 조정합니다.

현재 CPU의 인터럽트를 막아도 다른 CPU까지 멈추지는 않습니다. 따라서 다중 CPU 환경에서는 공유 자료에 대한 접근을 별도로 보호해야 합니다.

인터럽트를 너무 오래 막으면 처리가 지연될 수 있고, 락 역시 잘못 사용하면 문제가 생깁니다. 책은 이 논의를 동시성을 다루는 다음 부분으로 넘깁니다.

같은 CPU가 어떤 코드를 실행하는지 보니 연결됐습니다

이번 장을 읽기 전에는 프로그램, 커널, CPU를 각각 요청을 주고받는 독립적인 주체처럼 생각했습니다.

하지만 CPU 코어 하나를 기준으로 보면 핵심은 같은 CPU가 어느 코드를, 어떤 권한과 상태로 실행하고 있는가였습니다.

  • 시스템 콜은 정해진 경로로 커널 코드를 실행하게 합니다.
  • 타이머 인터럽트는 프로그램의 협조 없이 커널이 실행될 기회를 만듭니다.
  • 스케줄러는 다음 실행 대상을 고릅니다.
  • 문맥 교환은 저장된 상태를 복원하여 그 대상의 실행을 이어 갑니다.

특히 “커널에 요청한다”는 역할 설명과 “CPU가 커널 코드로 이동한다”는 실행 설명을 연결하면서 많은 혼동이 풀렸습니다.

이제 제한적 직접 실행을 다음과 같이 이해합니다. 프로그램의 일반적인 작업은 CPU에서 직접 실행합니다. 대신 하드웨어의 보호 기능과 정해진 진입 경로로 권한을 제한하고, 인터럽트로 제어권을 되찾아 실행을 관리합니다.

6장이 설명한 것은 실행을 제한하고 다른 프로세스로 전환하는 방법입니다. 다음에 남는 질문은 실행 가능한 작업 중 누구에게 먼저 CPU를 줄 것인가입니다. 그 선택 기준이 다음 장의 스케줄링 정책으로 이어집니다.


학습 자료: Remzi H. Arpaci-Dusseau, Andrea C. Arpaci-Dusseau, 『Operating Systems: Three Easy Pieces』, Chapter 6, “Mechanism: Limited Direct Execution”.

이 글은 본문과 학습 중 추가 질문을 중심으로 정리했습니다. 책의 측정 실습과 일부 보충 상자는 다루지 않았습니다.

썸네일 사진: He Junhui / Unsplash