[OS] Limited Direct Execution

장선규·2023년 6월 21일
0

[OS] OSTEP Study

목록 보기
3/28

Limited Direct Execution (제한적 직접 실행 원리)

CPU를 가상화하기 위해서 운영체제는 여러 작업들이 동시에 실행되는 것처럼 보이도록 물리적인 CPU를 공유한다.

  • 어떻게? CPU 시간을 나누어 쓰는 것으로 (시분할 기법)
  • 한 프로세스를 잠시동안 실행시키고, 다른 프로세스를 또 잠깐 실행 ...

이런 식으로 가상화 하면 몇가지 문제를 해결해야함

  • 성능저하 문제 - 시스템에 과중한 오버헤드를 주지 않으면서 가상화 가능?
  • 제어 문제 - CPU에 대한 통제를 유지하면서 프로세스 효율적으로 실행 가능?
    • 제어권을 상실하면 한 프로세스가 영원히 실행되거나, 접근하면 안 되는 정보에 접근할 수도 있으므로, 제어 문제는 매우 중요!

핵심질문: 제어를 유지하면서 효과적으로 CPU를 가상화 하는 방법

기본원리: Limited Direct Execution (제한적 직접 실행)

Direct Execution: 프로그램을 CPU 상에 직접 실행시키는 것

  • 프로그램을 빠르게 실행하기 위해 직접 실행 기법 개발
  • 직접 실행을 할 때 운영체제는...
    • 프로세스 목록의 항목을 생성
    • 프로그램 메모리 할당
    • 메모리에 프로그램 탑재
    • 인자(argc/argv) 전달을 위한 스택 셋업
    • 레지스터 내용 삭제
    • 메인 함수 실행 -> 프로그램에서 main ~ return 까지 실행
    • 프로세스 메모리 반환
    • 프로세스 목록에서 항목 제거

프로그램을 CPU에서 직접 실행시켰을 때의 문제점

  1. 운영체제가 원치 않는 일을 하지 않는다는 것 보장 가능?
  2. 프로세스 실행 시, 운영체제는 어떻게 프로그램의 실행을 중단하고 다른 프로세스로 전환?
    • time sharing(시분할) 기법 어떻게 구현?

프로그램 실행에 제한을 두지 않으면, 운영체제는 어떤 것도 제어할 수 없으며, 단순한 라이브러리에 불과하다.

문제점 1: 제한된 연산

직접 실행의 최대 장점은 "빠르게" 실행되는 것
(프로그램이 하드웨어 CPU에서 실행되기 때문)
그런데 프로세스가 원하는 대로 전부 방치하는 것은 바람직한 시스템이 아님.

제한 연산을 수행하는 방법

  • 프로세스는 입출력 연산을 비롯한 다른 제한 연산을 수행하야 한다.
  • 그러나 프로세스는 시스템에 대한 권한이 없기 때문에 제한된 연산을 수행할 수 없다.

    이를 위해 하드웨어는 사용자 모드커널 모드의 두가지 실행 모드를 제공하여 운영체제를 돕는다 (보호된 제어 양도).

보호된 제어 양도 (Protected Control Transfer)

  • 사용자 모드
    • 실행되는 코드는 할 수 있는 일이 제한됨 (권한 제한, 입출력 요청 불가능 등)
    • 커널 모드로 진입하기 위해 trap 명령어 사용,
      다시 사용자 모드로 돌아가려면 return-from-trap 명령어 사용
  • 커널 모드
    • 운영체제의 중요한 코드들이 커널 모드에서 실행됨
    • 모든 특수한 명령어를 포함한 원하는 모든 작업 수행 가능
      • 디스크 읽기, 프로세스 생성 및 제거 등 privileged operation (특권 명령어) 수행 가능!
    • 하드웨어는 trap 명령어를 사용할 때, 호출한 프로세스의 필요한 레지스터들을 저장해야함
      • 왜냐하면 return-from-trap 명령어 실행 시, 사용자 프로세스로 제대로 돌아와야 하므로
      • 카운터, 플래그 등 여러 레지스터를 각 프로세스의 커널 스택에 저장함
      • 이후 커널 모드에서 나올 때, 커널 스택에 있던 레지스터 값들을 pop하여 사용자 모드 프로그램을 다시 시작

trap 테이블 & trap 핸들러

  • 커널은 부팅 시에 트랩 테이블을 만들어 하드웨어에게 예외 사건이 일어났을 때 어떤 코드를 실행해야 하는지 명시

    • 트랩 테이블: 하드웨어야 키보드 인터럽트가 발생하면 ~~~ 코드를 실행하렴
  • 운영체제는 privileged operation (특권 명령어)를 사용하여 하드웨어에게 트랩 핸들러의 위치를 알려줌

    • 운영체제 부팅 시 처음에는 커널 모드로 진입함 (하드웨어 제어 가능)
    • 운영체제는 트랩 핸들러의 주소를 기억하게 하여 예외 상황이 발생했을 때 어떠한 처리를 할 지 알려줌
  • 프로세스가 실행될 때 Limited Direct Execution 프로토콜

    • 새로운 프로세스 실행되므로 프로세스 목록에 항목 추가, 메모리 할당, 커널 스택에 레지스터 저장 등
    • return-from-trap 명령어로 CPU를 사용자 모드로 전환하고 프로세스 실행
    • 프로그램 안에서 시스템 콜을 호출하면 다시 운영체제로 trap
      이때 다시 돌아갈 때를 위해 레지스터를 커널 스택에 저장 후에 트랩 핸들러로 분기
    • 프로그램 종료 시 exit() 시스템 콜이 호출되어 운영체제로 트랩된다.

문제점 2: 프로세스 간 전환 (시분할 구현 문제)

그렇다면 프로세스간의 전환은?

  • 운영체제는 실행중인 프로세스를 계속 실행할지, 멈추고 다른 프로세스를 실행할지 결정해햐 함
  • CPU에서 프로세스가 실행중 == 운영체제는 not running! (그럼 어떡하지...)

핵심질문: 운영체제는 CPU를 어떻게 다시 획득하여 프로세스를 전환할 수 있는가

협조 방식(Cooperative Approach): 시스템 콜 기다리기

  • 협조 방식에서는 운영체제가 프로세스들이 합리적으로 행동할 것이라 믿음
    • 너무 오랫동안 실행되는 프로세스는 운영체제가 다른 작업을 실행할 결정을 할 수 있도록 주기적으로 CPU를 포기할 것이라 가정
  • 프로세스가 어떻게 CPU를 포기?
    -> 프로세스가 자주 시스템 콜을 호출하여 CPU의 제어권을 운영체제한테 줌
    • ex) 파일을 열고 읽는 작업
    • ex) 다른 컴퓨터에게 메세지 송신
    • ex) 새 프로세스를
  • 응용 프로그램이 비정상적인 행위를 하면 운영체제로 트랩이 일어남
    • ex) 0으로 나누기 -> 트랩 일어나서 운영체제가 CPU 획득
    • ex) 접근할 수 없는 메모리에 접근하기 -> 트랩 일어나서 운영체제가 CPU 획득

그러나 이러한 방식은 수동적!
시스템 콜을 호출할 수 없는 상황(ex. 무한루프) 혼자서 해결 불가능

그렇다면 어떻게 비협조적인 상황에서도 CPU의 제어를 획득할 수 있을까?

비협조 방식(Non-Cooperative Approach): 운영체제가 전권을 행사

타이머 인터럽트 (timer interrupt): 수 밀리 초마다 인터럽트 발생시킴

  • 인터럽트가 발생하면 수행중인 프로세스는 중단되고, 인터럽트 핸들러가 실행됨
  • 이 시점에서 운영체제는 CPU 제어권을 다시 얻음
    -> 이후 자신이 원하는 일 할 수 있음 (프로세스 중단, 다른프로세스 싱행 등)
  • 인터럽트 발생 시 하드웨어는 실행중인 프로그램의 상태를 저장해야함
    (나중에 다시 돌아올때를 위해)
    • 레지스터들이 커널 스택에 저장됨
    • return-from-trap 명령어를 통해 복원

문맥의 저장과 복원

협조적, 비협조적 방법 모두 운영체제가 제어권을 다시 획득하면
현재 실행중인 프로세스를 계속 실행할지, 아니면 다른 프로세스로 전환할지 결정해야함
-> 이는 이후 스케줄러 부분에서 논의

만약 다른 프로세스로 전환하기로 결정되면 운영체제는 문맥 교환(context switch)을 함

  • 문맥 교환
    • 원래 실행중인 프로세스의 레지스터 값 저장
    • 이후 새로 전환할 프로세스의 레지스터 값 가져오기
    • return-from-trap 하면 새로 전환할 프로세스로 리턴
    • 이때 타이머 인터럽트가 일어날 때 커널모드로 이동하는 것에 유의

병행성

그럼 만약에 시스템 콜을 처리하는 중에 인터럽트가 발생한다면?
혹은 이미 인터럽트를 처리하고 있을 때 다른 인터럽트가 발생한다면?

  • 이는 병행성(Concurrency) 문제, 이후에 자세히 다룰 것
  • 병행성 문제에 대해 운영체제가 취할 수 있는 방법
    • 인터럽트 불능화 - 한 인터럽트 처리중엔 다른 인터럽트 CPU에 전달 x
    • Lock - 내부 자료 구조에 동시에 접근하는 것을 막음
profile
코딩연습

0개의 댓글