운영체제(1) - 운영체제란?(2)

‘운영체제’를 공부하여 정리한 내용입니다.

3. 컴퓨터 시스템 구조

Single-Processor System

  • Single-Processor System은 범용 명령어 집합을 실행할 수 있는 하나의 주 CPU를 가진다.
  • 거의 대부분의 시스템은 전용 처리기를 가지는데, 기본적으로 간단한 것들은 Single-Processor System으로 이루어져 있다.
    • 예를들어 이것이 메인 CPU로부터 연속된 요청을 받게 된다고 생각하자(컴퓨터에 연결된 이동식 디스크)
    • 이 때 이 전용 처리기는 메인 CPU의 부탁을 받아들이고, 내부에서 조정해야 하는 디스크 큐와 스케쥴링은 스스로 진행해서, 메인 CPU는 이런 자잘한 부분은 신경쓰지 않아도 괜찮게된다.
    • 각자 할일만 잘하면 되는 세상이다. 아주 이상적이다.
  • 정리하자면, 범용 처리기 CPU가 하나 있는 시스템을 Single-Processor System이라고 부른다.

MultiProcessor System

  • 컴퓨터구조(1) - 컴퓨터 추상화 및 관련 기술(3)에서도 그 중요성을 언급한 적이 있다.
  • MultiProcessor System은 여러개의 CPU 코어가 컴퓨터 버스, 클록, 때로는 메모리와 주변장치를 공유하는 형태로 사용될 때를 의미한다.
    • 물론 코어가 아니라 마이크로 프로세서가 아예 나뉘어 내장되어 있을 수도 있지만, 설명의 용이를 위해 여기서는 하나의 집적회로 칩에 코어가 여러개 달렸다고 생각하자.
  • 장점

    • 증가된 처리량
      • N개의 코어가 생기면 N배까지는 아니더라도, 처리량 자체가 확실히 증가된다.(병렬처리가 가능한 task에 대해서만)
    • 규모의 경제
      • 여러 프로그램이 동일한 데이터를 사용하려고할 때, 데이터를 복사해서 또 올리는 것이 아니라 올려져 있는 데이터를 공유해서 작업할 수 있게 할 수 있다.
      • 이는 전체적인 자원을 절약하는 효과를 불러온다.
    • 증가된 신뢰성
      • 10개의 코어중에 하나가 고장나더라도 나머지 9개가 정상 동작한다면 어쨌든 일을 처리할 수 있게된다.
      • 안전성이 올라간다.
  • MultiProcessor System에는 대표적으로 두가지 형태가 있다.
    • Asymmetric multiprocessing을 지원하는 형태
      • 이 경우는 하나의 메인 코어(혹은 처리기)가 있어 이가 시스템을 제어하고, 다른 코어들은 메인 처리기의 명형을 실행하거나 내장된 테스크를 처리하도록 한다.
      • 주종관계가 정의되어있다고 생각하면 된다.
    • symmetric multiprocessing을 지원하는 형태
      • 주종관계가 없이 대등한 관계를 가진다.
      • 각 코어는 자신만의 레지스터와 캐시를 지니고 있다.
      • 이들은 물리 메모리를 공유하게 된다. 주로 메인메모리인 RAM을 의미한다.
      • 이 경우는 동시에 코어의 개수만큼의 프로세스들이 실행될 수 있지만, 이들의 데이터 전달을 굉장히 조심스럽게 조정해야 한다.
      • 그리고 CPU가 분리되어 있기 때문에, 한 CPU가 일을 과다하게 하지 않을 수 있도록 Task분배를 잘 해줄 필요가 있다. 이는 일부 자료 구조(큐 등)를 버퍼식으로 공유하는 경우 피하는 것이 가능하다.

클러스터형 시스템

  • 정확한 정의는 없지만 대략적인 정의는 있다.
    • 클러스터 컴퓨터는 여러대의 컴퓨터들이 저장장치를 공유하고 근거리 통신망이나 고속 연결망으로 연결되는 형태이다.
    • 예를 들어 컴퓨터들이 여러대가 연결되어 있는데 저장장치는 모두 공유하고 있다.
    • 이 때 하나가 고장나면, 다른 컴퓨터(노드라고 한다)가 그 컴퓨터의 저장장치의 소유권을 넘겨받고(저장장치를 공유하고 있기에 소유권 양도가 가능하다) 이어받아서 시작하게 된다. 신뢰성 증대의 효과가 있다.
  • 위에서 말한 MultiProcessor System처럼 Asymmetric하거나 symmetric하게 동작하게 하는 것이 가능하다.

4. OS의 구조

세상에 OS는 매우 많은 종류가 존재한다.

  • 하지만 그 가운데에서도 거의 공통적으로 지니고 있는 특징은 존재한다.
  • 이 장에서는 그런 것들에 대해서 논의하겠다.

운영체제에서 중요한 측면들 (아래의 질문에 대답할 수 있는가)

  • 어떻게 다중 프로그램을 실행할 수 있는 것인지?
  • 모든 프로그램을 메인 메모리에 올릴 수 없을 때는 어떻게 해야 하는지?
  • 사용자가 모든 프로그램에 거의 동시에 상호작용할 수 있도록 하기위해서 사용하는 시분할 방법이란? 그리고 이를 가능하게 하기위한 Context changing 방식이란?
  • 메모리에 여러 프로그램이 올라와 있을 때, 우리는 이를 어떻게 관리하는가?
  • 지금 어떤 프로세스를 실행시켜야 할 지 선택하는 작업을 CPU 스케쥴링이라고 한다. 이는 어떻게 하는가?
  • 가상 메모리 방법은 어떻게 구현되어 사용되는가? 물론 우리는 이를 사용할 때, 체감 시스템 동작시간을 떨어트려서는 안된다.
  • 시분할 시스템은 파일 시스템도 제공해야 한다. 그리고 이를 위해 우리는 디스크를 어떻게 관리해야하는지에 대해서도 논의할 필요가 있다.
  • 작업이 질서정연하게 실행되기 위해서 시스템은 반드시 작업 동기화롸 통신 기법을 제공해야 하며,
  • 작업이 서로를 영원히 기다리는 Deadlock에 빠지지 않도록 주의해야 한다.

5. OS 동작

인터럽트 구동식

  • 실행할 프로세스가 없고, 다른 어떤 이벤트도 없다면, OS는 계속 어떠한 프로세스나 이벤트를 기다리게 된다.
  • 이벤트가 일어나면, 이는 인터럽트를 발생시켜 신호를 보내게 된다.
  • 앞서 운영체제(1) - 운영체제란?(1)-#2. 컴퓨터 시스템의 구성의 인터럽트 파트에서도 설명했듯이 각 인터럽트는 벡터화 되어 있으며 운영체제는 이를 통해 각각에 인터럽트에 대해서 적절한 ISR(Interrupt Service Routine)을 제공한다. (ISR은 단순하게 이 인터럽트를 어떻게 처리할지 정의된 일련의 과정이라고 생각하면 된다)

중요한 건, Multi Programming 환경에서는 각각의 프로그램이 다른 프로그램 혹은 OS에 악영향을 끼치지 말아야 한다는 것이다.

  • 각 프로그램이 다른 사용자의 프로그램에 영향을 미치게 되면, 이는 우리가 OS를 신뢰할 수 없다는 의미로 이어지게 될 수 있기 때문이다.

이중 동작 모드

  • 필요성

    • 운영체제의 적절한 동작을 보장하기 위해서, 운영체제 코드의 실행과, 유저가 정의한 코드의 실행을 구분할 수 있어야 한다.
    • 운영체제의 경우는 프로세스를 매니징하거나 때로는 죽이는 등의 작업을 할 수 있지만, 유저 프로그램이 이런 작업을 함부러 할 수 있으면 다른 프로그램에 영향을 미쳐서 위에서 말했듯이 OS를 신뢰할 수 없는 상황으로 이어질 수 있기 때문이다.
  • 어떻게 나누나?

    • Kernel mode (mode bit 0)
      • 커널은 운영체제 그 자체라고 생각해도 괜찮다.
      • 여기에서 실행할 수 있는 명령은 각 프로세스에 영향을 줄 수 있는 아주 특권있는 명령어들이 포함된다.
      • 그런 명령어를 우리는 Privileged instruction이라고 하고, 이를 실행시키는 이 커널 모드를 Privileged mode라고 부르기도 한다.
    • User mode (mode bit 1)
      • 커널 모드에 비해서 사용할 수 있는 명령어가 한정적이다.

Syscall

  • User 모드는 Privileged instruction을 사용할 수 없기 때문에, Syscall을 거쳐야 한다.
  • 단순히 생각해보면, Syscall 자체가 그럼 Privileged instruction을 쓰는 것이니까 User 모드도 Privileged instruction을 쓰는 상황이 되는 것이 아닌가 하는 의문이 들게 된다. (내가 1년동안 헷갈렸던 개념이라…)
  • 이런 생각이 든다면 아래의 설명을 들어보자. 도움이 되길 바란다.
  • file open(Privileged instruction)이 필요한 User program이 있다고 생각해보자
    1. User program은 파일을 열어야 한다.
      • 그런데 file open을 위해서는 Privileged instruction이 필요하다.
      • 하지만 User mode에서는 위의 Privileged instruction을 실행할 수 없다.
      • 그래서 file open을 위한 Syscall을 부른다. 중요한 것은 이 Syscall을 통해 관련한 인터럽트가 발생한다.
      • 이 인터럽트를 통해 정의된 ISR로 넘어가면 Mode bit가 1에서 0으로 수정되게 되고, 여기에서 Privileged instruction등을 사용해서 User program이 요청한 file open 작업을 진행한다. (이때 User program은 file open을 위해 필요한 파일 이름이나 경로 같은 정보를 Syscall을 통해 건넨 상태이다.) (ISR은 Interrupt Service Routine으로 이미 OS에 정의된 루틴이고, 시스템은 OS는 신뢰하기에 Privileged instruction을 통해서 파일을 열수 있게 허락 한다.)
      • Kernel에서 이 모든 작업을 완료하면 결과를 User program에게 건네준다. (무사히 메모리에 적재한 file 정보의 포인터 등)
      • 한 마디로 User program이 Syscall을 부른다는 것은 자신이 Privileged instruction을 사용한 프로그램을 짠다는 의미가 아니라, 이미 약속된 ISR을 알고, kernel에게 이 작업을 부탁하는 것이다. 보통은 라이브러리 형태로 묶여져있기 때문에 프로그래밍을 할 때는 그 차이를 느끼기 어렵겠지만, 사실 File을 열기위해 디스크에 직접 접근하고 메모리에 적재시키는 등의 작업을 우리는 할 수 없다.
      • 혹여 어셈블리어로 메모리 정보를 바꾸는 등(sw를 사용해서)의 작업을 하려고 도전해도 인터럽트를 통해 kernel 모드로 들어가져있지 않기 때문에 시스템이 오류를 발생시킨다.
      • 이런 종류의 작업을 위해서 User는 그저 OS에게 부탁하는 방법밖에는 없다. 인터럽트가 발생하고 끝나는 구간까지는 우리가 건들일 수 없다. (지금은 User programming을 하는 것만 논의하고 있다)

Mode bit 1 -> 0

  • 이 것도 살짝 애매할 수 있다.
  • 이를 위해서 우리는 trap(트랩)을 통해서 인터럽트를 발생시킨다.
  • 위에서 본 것 처럼 Syscall을 사용하면 이런 상황이 발생한다.
  • 중요한 것은 인터럽트는 소프트웨어적으로 발생이 불가능하다는 것이다.
    • 이 말의 의미가 매우 헷갈렸었는데, 아래의 설명을 보자.
    • 소프트웨어적으로 발생 시킬 수 없다는 것은 프로그램 코딩 중에 다음과 같은 코딩이 불가능하다는 것이다.
      mode_bit = 0; # when we in (user mode -> mode_bit == 1)
      [PrivilegedInstruction];  
      ...
      
    • 코드를 해석하면 mode_bit가 1일때 우리가 직접 mode_bit를 0으로 만들려고 시도하고 유저가 직접 PrivilegedInstruction을 사용하는 함수를 사용하는 프로그램을 짠것이다.
    • 이걸 시스템은 아예 막아두었다! 왜냐하면 User는 절대 직접 Privileged Instruction을 사용해서는 안되기 때문이다.
    • 그래서 무조건 인터럽트가 발생했을 때만 mode_bit가 0으로 변한다. 그리고 이는 하드웨어적으로 구현되어 있다.
    • 여기서도 중요한 것은 User는 PrivilegedInstruction을 직접 사용할 수 없다는 것이다.
  • 중요한 것이 하나 더! trap은 인터럽트를 발생시킨다. 이는 kernel에서 제공하는 특정 서비스를 사용하기 위한 인터럽트로 이 상황이 발생하면 유저가 만든 프로그램이 실행되는 루틴에서 잠시 빠져나와 특정 ISR을 처리하고 다시 mode_bit를 1로 되돌린다. 결국 유저 프로그램으로 돌아오기 때문에 결과적으로 User는 PrivilegedInstruction을 직접 사용할 수 없다.

  • 아… 적다보니 또 중구난방이긴 하다. 나중에 다시 정리하겠다…

타이머

  • 우리는 OS가 CPU에 대한 제어를 유지할 수 있도록 보장해야 한다.

    • 예를 들어 사용자 프로그램이 무한루프 등과 같은 문제에 걸려서 제어를 OS에게 돌려주지 않는다고 생각해보자.
    • 이러면 우리는 이 프로그램 때문에 다른 프로그램을 실행시키거나 하는 생산적인 활동을 할 수 없게된다.
  • 위와 같은 문제를 해결할 수 있음을 보장하기 위해서 OS는 타이머를 가진다.

    • 이 타이머를 통해 일정시간 이상 User program이 제어를 가지고 있지 않을 수 있도록 한다.
    • 그리고 이는 마찬가지로 인터럽트를 발생시키는 방식으로 이루어진다.
    • 일정 시간이 경과하면, 인터럽트가 발생해서 kernel로 돌아오는 것이다.
    • 이 덕분에 우리는 운영체제가 계속해서 제어권을 가져올 수 있다고 보장할 수 있게된다.

Leave a Comment