👍 Limited Direction Execution이란?
- 컴퓨터에서 여러 프로그램을 동시에 실행시키려면 CPU 가상화(CPU Virtualization)가 필요한데, 이때 사용하는 방법이 Time Sharing 기법이다.
- Time Sharing을 위해서는 두 가지를 고려해야 하는데,
-> 가상화를 구현하는 데 드는 오버헤드가 없어야 하고 (Performance),
-> CPU에 대한 제어를 유지하면서 프로세스를 효율적으로 관리할 수 있어야 한다. (Control)
위의 그림처럼, OS는 프로세스가 필요한 리소스를 할당하고, 프로그램은 할당받은 리소스로 작업을 수행한 후 이를 OS에 돌려주는 것이 정상적이자 가장 단순한 흐름이다.
하지만, 이런 방식에선 OS는 프로세스에 대한 통제권을 잃게 되므로 프로세스가 효율적으로 동작하는지 알 수 없고, 프로세스가 언제 멈출지 판단할 수 없기 때문에 효율적인 Time Sharing이 불가하다는 문제점이 있다. 따라서 우리는 limit을 줘야만 성능면과 제어면에서 효율적인 Time Sharing을 할 수 있다.
👍 Limit 1: Restricted Operations
📢 개요
- 프로세스가 I/O 및 기타작업을 전부 하는 대신, 프로세스와 OS를 User Mode와 Kernel Mode로 나누어 서로 할 수 있는 행동을 분리한다.
📜 유저모드(User mode): 수행할 수 있는 행동에 제한이 있으며, I/O Request 같은 제한된 행동을 한다면 예외(exception)을 발생시킨다.
📜 커널모드(Kernel Mode): OS가 실행되는 공간이며, 유저 모드에선 제한된 I/O Request 같은 동작이 가능하다.
📢 시스템 콜(System Call)이란?
- User mode에서 실행중인 프로세스가 Kernel mode에서 수행할 수 있는 작업을 해야 할 때 System Call이 발생한다.
- 프로세스는 특별한 트랩(Trap) 명령을 수행해야 한다.
📜 트랩(Trap)과 인터럽션(Interruption)의 차이
-> 트랩은 프로세스에서 발생한 인터럽트로, 소프트웨어의 오류로 인해 발생한 오류(ex) div 0)나 시스템 콜에 의해 발생하는 반면,
-> 인터럽션은 외부에서 발생한 이벤트에 의한 것으로, I/O interrupt, Clock interrupt, IPC interrupt 등이 있다. -> 인터럽션도 Trap Table과 같은 기능을 하는 Interrupt Service Table을 가지고 있다. Interrupt number에 따라 Interrupt Service Table에 각각 달리 정의된 Interrupt handler(Interrupt Service Routine, ISR)들이 동작하게 된다.
-
트랩 명령이 발생하면, 유저모드에서 커널모드로 권한이 변경된다. User Mode의 register 값들과 return address가 커널 스택(Kernel Stack)에 저장된다. 그 후, 유저 코드에 명시된 system call number에 따라 trap table에 명시된 트랩 처리 함수의 jump address로 점프한다.
-
커널 모드에서의 작동이 끝나게 되면, OS는 return-from-trap 명령을 실행하여 원래의 권한으로 돌아온다. 커널 스택에 있던 데이터들은 지워진다.
📢 Trap시의 시스템 동작
- User Mode의 프로세스가 실행 중 system call을 call, trap 실행
- 이때 user mode의 프로세스의 레지스터 정보는 커널 스택에 저장된다. 각 프로세스는 개별마다의 커널 스택을 가지고 있다. 그 후 커널 모드로 이동한다.
- Trap table을 참고하여 trap number에 맞는 trap handler의 address로 jump 하며, trap 명령을 수행한다.
- return-from-trap 명령이 실행되며 user mode로 돌아간다. 이때 기존의 커널 스택에 저장해두었던 레지스터 값들은 다시 복구되며, 앞으로 실행한 명령(Program counter)로 다시 점프 한다.
👍 Limit 2: Switching between Processes
📜 Cooperative Approach
- OS는 프로세스가 정상적으로 작동할 것이라고 가정하며, 프로세스는 작동 시간이 길면 스스로 yield call을 통해 CPU를 release한다.
- 프로세스가 system call을 발동하면 CPU의 제어권은 OS로 넘어오게 되고, OS는 스케쥴링 후 다른 프로세스에게 CPU를 넘길 수 있다.
- 그러나 프로세스가 system call을 하지 않는다면, OS는 CPU의 제어권을 가져올 방법이 없다...
📜 Non-cooperative Approach - 프로세스가 CPU를 독점하는 것을 방지
- System call을 발동하는 것 이외에도, timer device가 일정 시간마다 timer interrupt를 발동하면 작동중인 process는 멈추고 OS의 interrupt handler가 작동한다.
- 이때도 마찬가지로 CPU의 제어권은 OS로 넘어오게 되고, OS는 스케쥴링 후 다른 프로세스에게 CPU를 넘길 수 있다.
📢 Context-switch
- OS가 CPU의 통제권을 되찾아왔다면, 기존에 실행해오던 프로세스를 계속 실행할 것인지, 아니면 다른 프로세스를 실행할 것인지 선택해야 한다. -> scheduler가 필요
- 만약 프로세스를 바꾸기로 결정했다면, 기존 프로세스의 레지스터 정보 일부와 현 CPU의 레지스터 정보는 해당 프로세스의 커널 스택에 저장된다.
📢 Context-switch시의 system 동작
- Timer interrupt 발생, 프로세스 A를 실행하는 당시의 CPU 레지스터들과(+프로세스A의 일부 정보들)이 프로세스 A의 커널스택에 저장되고, kernel mode로 전환.
- Kernel mode의 process A에 대한 레지스터 정보들(e.g. process A의 커널스택 주소값)이 PCB A에 저장됨.
- Kernel mode의 process B에 대한 레지스터 정보들(e.g. process B의 커널스택 주소값)이 PCB B에서 CPU로 restore됨.
- Return from trap 발생, User mode에서 process B를 실행하기 위한 레지스터 정보를 프로세스 B의 커널스택에서 restore.
📢 Kernel Stack과 PCB의 차이점?
- 둘다 어떤 프로세스를 실행하기 위한 레지스터 정보를 저장한다는 점에선 동일하나, Kernel stack은 프로세스 실행 당시의 CPU 레지스터 값을 주로 저장하고, PCB는 커널 모드에서 OS가 프로세스를 실행하기 위해 필요한 레지스터 값을 주로 저장한다고 보면 될 것 같다.
- 물론 저 값들만 저장하는 것은 아니며, OS 아키텍쳐에 따라 이 내용은 다를 수 있다.
👍 Concurrency Issue
- system call, time-sharing 및 context-switch만 적용한다고 해서 끝나는 게 아니다.
- Problem 1: Interrupt handling 중에 다른 interrupt가 발생한다면?
- Problem 2: System call handling 중에 다른 interrupt가 발생한다면?
-> 이걸 해결하는 방법은, Interrupt handling 도중 다른 interrupt 발생 제한, Locking, Interrupt priority 적용 등이 있다.