운영체제 공룡책 1단원 정리

목차

운영체제 중간고사를 맞아서 내용을 정리한다. 수업 ppt와 OSTEP, Operating System Concepts 책을 참고하였다. 또한 박성범 님의 블로그도 큰 도움이 되었다.

1. 운영체제 기본 개념

1.1 운영체제의 역할

프로그램이 실행될 때는 어떤 일이 일어나는가? 프로그램을 실행하는 프로세서는 명령어를 반입(fetch)하고, 해석(decode)하고, 실행(execute)한다. 프로그램이 끝날 때까지.

그런데 이 과정에서 프로그램을 동시에 실행하기도 하고, 프로그램끼리 메모리 공유를 하기도 하고 프로그램과 하드웨어 장치가 상호작용하는 등 다양한 일이 일어난다. 이런 일들을 관리하는 것이 운영체제이다.

그리고 사용자에게는 이런 시스템 자원 배분을 운영체제에 맡기고 복잡한 부분을 신경쓰지 않고 프로그램을 실행할 수 있도록 해준다. 즉 하드웨어와 응용 프로그램 사이의 조율을 해준다.

1.2 컴퓨터 시스템 구조

컴퓨터 시스템의 구조는 어떻게 되어 있을까? 현대의 컴퓨터는 하나 이상의 CPU와 여러 개의 디바이스 컨트롤러로 이루어져 있으며 CPU, 디바이스 컨트롤러는 common bus를 통해 공유 메모리에 접근할 수 있도록 되어 있다. 버스는 데이터를 주고받을 수 있게 해주는 매개체 정도로 생각하면 된다.

각 디바이스 컨트롤러는 연결된 특정 장치를 제어한다. 컨트롤러에 따라 둘 이상의 장치가 연결될 수도 있다. 이때 이 컨트롤러는 로컬 버퍼와 레지스터를 가지고 있다. 그리고 디바이스 컨트롤러는 담당하고 있는 주변 기기와 로컬 버퍼 사이의 데이터 전송을 관리한다.

그리고 CPU와 디바이스 컨트롤러는 병렬로 실행될 수 있는데 그러면 CPU와 디바이스 컨트롤러는 메모리 사이클을 얻기 위해 경쟁한다. 어떤 디바이스 컨트롤러가 공유 메모리에 먼저 접근할지를 결정하기 위한 메모리 컨트롤러도 따로 있다.

1.3 인터럽트

일반적인 입출력을 수행하는 프로그램을 생각한다. 그러면 디바이스 드라이버가 컨트롤러의 적절한 레지스터에 값을 로드한다. 디바이스 컨트롤러는 이 레지스터에 담긴 정보를 읽고 어떤 행동을 취할지 결정한다. (가령 키보드에서 입력 읽어오기). 그리고 디바이스 컨트롤러는 장치에서 로컬 버퍼로 데이터를 전송하기 시작한다.

이게 끝나면 디바이스 컨트롤러는 디바이스 드라이버에게 operation을 끝냈다는 신호를 보내고 디바이스 드라이버가 운영체제의 다른 부분에 제어를 넘긴다. 이때 컨트롤러가 드라이버에게 작업 완료 사실을 알리는 건 인터럽트를 통해 이루어진다. 이 인터럽트는 운영체제-하드웨어 간 상호작용의 핵심이다.

CPU가 인터럽트되면 하던 일을 중단하고 즉시 특정한 고정 위치로 실행을 옮긴다. 이 고정 위치는 일반적으로 인터럽트의 서비스 루틴 시작 주소를 가지고 있다. 이 루틴의 실행이 완료되면 CPU는 원래 하던 일을 다시 시작한다.

인터럽트는 하드웨어, 소프트웨어 둘 모두에 의해 발생될 수 있다. 하드웨어는 시스템 버스를 통해 인터럽트를 발생시키고 소프트웨어적으로는 시스템 콜을 통해 인터럽트를 발생시킨다. 소프트웨어에 의한 인터럽트는 트랩이라고 하기도 한다.

인터럽트는 결과적으로 적절한 서비스 루틴으로 프로그램 제어를 전달하는 것이다. 이때 사용할 수 있는 방법은 인터럽트가 발생했을 때 그 정보를 조사하는 핸들러를 호출하는 것이다. 이 핸들러는 인터럽트가 발생한 원인을 파악하고 적절한 서비스 루틴을 호출한다.

하지만 이는 인터럽트의 처리 속도를 늦춘다. 따라서 테이블을 통해서 간접적으로 인터럽트가 호출되도록 하는 방법이 있다. 이 테이블은 인터럽트 벡터라고 불리며 인터럽트 번호와 서비스 루틴의 주소를 매핑한다. 인터럽트가 발생하면 CPU는 해당 인터럽트 번호를 테이블에서 찾아 해당 서비스 루틴의 주소를 얻는다. 이 주소로 CPU가 이동하고 서비스 루틴을 실행한다.

그리고 인터럽트가 실행 종료된 이후 다시 원래 하던 일을 계속해야 한다. 따라서 인터럽트 루틴으로 넘어가기 전에 현재 상태를 저장해 놓아야 한다.

1.4 DMA

인터럽트 기반의 I/O 처리는 다음과 같이 작동한다. 어떤 입출력 작업이 시작되면 장치 드라이버가 장치 컨트롤러 레지스터에 값을 로드한다. 그러면 장치 컨트롤러는 이 레지스터 내용을 검사후 작업을 수행하고 CPU에 인터럽트를 전달한다. CPU는 인터럽트를 받아서 인터럽트 핸들러를 호출하고 이 핸들러는 인터럽트가 발생한 원인을 파악하고 적절한 서비스 루틴을 호출한다. 그리고 그 서비스 루틴이 종료되면 CPU가 원래 진행하고 있던 작업(인터럽트로 인해 중단)으로 복귀한다.

하지만 이는 장치 컨트롤러가 전달한 데이터를 CPU를 거쳐 로드하기 때문에 CPU의 처리 속도에 영향을 받는다. 따라서 CPU를 거치지 않고 장치 컨트롤러가 직접 메모리에 데이터를 로드하는 방법이 있다. 이를 DMA(Direct Memory Access)라고 한다. DMA는 CPU가 데이터를 메모리에 로드하는 것이 아니라 장치 컨트롤러가 직접 메모리에 데이터를 로드한다.

따라서 하나의 데이터 블록에 하나의 인터럽트만 발생한다. 이 하나의 인터럽트는 디바이스 컨트롤러 -> 메인 메모리 데이터 전달이 끝났다고 CPU에게 알려주는 인터럽트이다. 그리고 CPU는 디바이스 컨트롤러가 메인 메모리에 데이터를 전달하는 시간 동안 다른 일을 할 수 있다.

1.5 컴퓨터 시스템 구조

싱글 프로세서

싱글 프로세서 시스템은 하나의 CPU만을 가지고 있다. 그리고 그 CPU는 하나의 처리 코어를 가지고 있다. 코어는 명령을 실행하고 로컬로 데이터를 저장하기 위한 레지스터를 포함하는 하나의 구성 요소로 CPU의 기본 계산 단위이다.

멀티 프로세서

멀티 프로세서 시스템은 하나 이상의 CPU를 가진 2개 이상의 프로세서가 있다. 그리고 각 CPU는 하나 이상의 코어를 가지고 있다. 멀티 프로세서 시스템은 버스, 메인 메모리 및 주변 장치를 공유한다. 이렇게 프로세서 수를 늘리면 처리량이 늘어난다.

물론 N개의 프로세서를 쓴다고 처리량이 N배 정확하게 늘어나는 건 아니다. 여러 프로세서가 협력할 때 모든 프로세서가 올바르게 작동하게 유지하는 데 드는 오버헤드가 있다. 또한 공유 자원에 대한 경합도 문제가 된다.

멀티 프로그래밍

멀티 프로그래밍은 여러 프로그램을 동시에 실행하는 것이다. 다중 프로그래밍은 CPU가 항상 프로그램 한 개는 실행할 수 있도록 프로그램을 구성한다. 즉 여러 프로세스를 동시에 메모리에 유지한다. 하나의 프로그램이 유휴 상태가 되면 다른 프로그램을 실행한다. 이렇게 하면 CPU가 항상 프로그램을 실행할 수 있게 된다.

멀티태스킹은 시분할(Time sharing)이라고도 하는데 여러 프로세스를 전환하며 프로세스를 실행하는 것이다. 전환이 자주 발생하므로 사용자에게 빠른 응답 시간을 제공한다. 이때 여러 프로세스가 동시에 실행할 준비가 되면 시스템은 스케줄링을 통해 다음에 실행할 프로세스를 선택한다. 또한 가상 메모리의 사용은 메모리에 일부만 적재되어 있는 프로세스를 실행할 수 있게 한다. 즉 프로세스 크기가 물리 메모리 크기보다 커도 된다! (또한 사용자에게 보이는 논리 메모리를 물리 메모리와 분리시킬 수 있는 이점도 있다)

1.6 운영체제의 안전 보장

운영체제는 잘못되거나 악의적인 프로그램으로 인해 다른 프로그램까지 잘못 실행되는 것을 막아야 한다. 이를 위해 운영체제는 다음과 같은 기능을 제공한다.

  • 이중 모드
  • 입출력 보호
  • 메모리 보호
  • 타이머

이중 모드

운영체제는 사용자 모드와 커널 모드, 2개의 독립된 연산 모드를 필요로 한다. 이는 모드 비트로 나타나며 0이 커널 모드, 1이 사용자 모드를 나타낸다. 이를 통해 운영체제를 위해 실행되는 작업/사용자를 위해 실행되는 작업을 구분할 수 있다.

몇몇 명령어는 커널 모드일 때만 실행되도록 설계되어 있는데 이는 특권 명령(privileged instruction)이라고 한다. 사용자 모드에서는 특권 명령을 실행할 시 불법적인 명령으로 간주되어 예외가 발생한다. 사용자는 시스템 콜을 통해서 운영체제에 특정 작업을 요청할 수 있다.

입출력 보호

모든 입출력 명령어는 특권 명령으로 커널 모드에서만 실행될 수 있다.

메모리 보호

인터럽트 벡터(각 인터럽트별로 실행할 작업의 주소가 저장된 테이블)와 인터럽트 서비스 루틴이 저장된 메모리 위치는 보호해야 한다. 따라서 이런 메모리 보호를 위해서, 프로그램이 접근할 수 있는 메모리 범위를 결정하는 두 개의 레지스터를 사용한다.

프로그램이 접근할 수 있는 최소의 물리 메모리 주소를 저장하는 Base register, 접근 가능한 메모리의 range를 저장하는 Limit register가 그것이다. 이 범위를 벗어나는 메모리는 보호된 것으로 간주한다.

타이머

타이머는 운영체제에게 제어권을 보장하는 수단이다. 이를 위해 일정 주기마다 인터럽트를 발생시킨다. 이렇게 지정된 시간 후 컴퓨터를 인터럽트하도록 하는 것을 타이머라고 한다.

운영체제 타이머는 특정 값을 설정하고 클럭 틱마다 1씩 감소시킨다. 이 값이 0이 되면 인터럽트를 발생시키는 방식이다. 이 타이머 값을 변경하는 명령은 특권 명령으로 커널 모드에서만 실행될 수 있다.

1.7 시스템 콜

시스템 콜은 운영체제의 기능을 사용하기 위해 프로그램이 운영체제에 요청하는 방법이다. 사용자는 시스템 콜을 이용해서 운영체제가 제공하는 기능을 사용할 수 있다.

일반적으로 각 시스템 콜은 특정 번호와 연결되어 있으며 이 관계를 저장한 시스템 콜 테이블이 존재한다. 이 시스템 콜을 사용하는 사용자는 각 시스템 콜의 내부 구현을 알 필요 없이 API를 통해 사용할 수 있다.

이때 시스템 콜에 파라미터를 전달해야 할 때가 있는데 이태는 3가지 방법이 있다.

  1. 레지스터를 통해 전달 레지스터에 그대로 파라미터를 전달한다. 그런데 이렇게 하면 레지스터의 수가 부족할 수 있다.
  2. 블럭을 통해 전달 파라미터들을 메모리 내의 블럭이나 테이블에 저장하고 그 블럭의 주소를 레지스터에 전달한다.
  3. 스택을 통해 전달 파라미터들을 스택에 저장하고 스택 포인터를 레지스터에 전달한다.

시스템 콜의 종류

  • 프로세스 제어 Process Control
  • 파일 조작 File Management
  • 장치 조작 Device Management
  • 정보 유지 Information Maintenance
  • 통신 Communication
  • 보호(자원 권한 관리 등) Protection

1.8. 시스템 부팅

시스템 부팅은 커널을 로드하는 것부터 시작한다.

시스템 전원이 들어오면 비휘발성 메모리에 있는 부트스트랩 프로그램이라는 프로그램이 실행된다. 이 프로그램은 커널을 메모리에 로드하고 커널을 실행한다.

가끔은 부트스트랩 로더가 디스크에서 더 복잡한 부트 프로그램을 로드하고 이 부트 프로그램이 커널을 로드하는 경우도 있다.