OS의 그 하위 계층인 하드웨어, 특히 CPU의 차이를 숨겨주는 역할을 합니다. 즉 , OS는 CPU를 제어하여 애플리케이션에게 추상화된 레이어를 제공하는 프로그램이라고 할 수 있습니다.
운영체제 또한 다른 프로그램들 웹 브라우저, 게임과 같은 프로그램에 해당하므로 메모리 영역에 할당이 되어야만 합니다. 다만 운영체제의 경우 매우 특별한 프로그램에 해당되기 때문에 다른 프로그램들과 달리 커널 영역 이라는 공간에 적재되어 실행이 됩니다.
커널영역을 제외한 나머지 영역은 사용자가 이용하는 응용 프로그램이 적재되는 영역, 사용자 영역이라고 합니다. 커널영역에 적재되어 있는 운영체제가 사용자 영역에 적재된 프로그램들에게 자원을 할당하고 올바르게 실행되도록 돕습니다.
운영체제를 포함한 모든 실행되는 응용 프로그램들은 메모리에서 특정 주소에 특정 크기의 메모리 할당을 부여 받아야 합니다. 또한 응용 프로그램이 실행되기 위해서는 반드시 CPU가 필요합니다. 이 과정에서 필요한 의사과정은 다음과 같습니다.
얼마나 오랫동안 CPU를 이용하게 할 것인가?
어떤 프로그램부터 CPU를 사용하게끔 해야 할 것인가?
이러한 의사결정이 필요한 이유는 특정 프로그램이 CPU을 오랜시간 독점하게 될 경우 다른 프로그램들이 실행될 수 없기 때문입니다. 이는 컴퓨터의 자원들이 가진 제한성, 동시 사용성(Concurrency), 속도 차이 및 독점성(Exclusivity) 때문입니다. 이러한 자원들의 특징으로 인해서 운영체제는 자원들을 공평하게 사용할 수 있도록 스케쥴링 및 관리 정책을 수행해야만 합니다.
CPU는 한 번에 하나의 작업만 처리할 수 있습니다. 멀티 코어 CPU라고 하더라고 각 코어는 동시에 하나의 명령어만 실행이 가능합니다. CPU는 속도는 매우 빠르지만 동시에 여러 작업을 처리할 수 없으며, 특정 작업이 CPU를 점유할 경우 다른 작업이 개입할 수 없습니다. 이러한 특징으로 인해서 CPU는 시간을 분할 하여 여러 작업을 번갈아가며 사용할 수 있도록 해야만 합니다.
메모리의 경우 한정된 용량을 갖고 있습니다. 메모리의 경우 여러 프로그램이 동시에 메모리를 사용하려고 할 경우에 충돌이 발생하기 때문에 프로그램마다 차지하는 메모리 주소는 서로 독립성이 보장되어야만 합니다. 또한 CPU에 비해서 메모리의 접근 속도가 상대적으로 느리기 때문에 효율적인 메모리 관리가 필요합니다.
이처럼 컴퓨터에서 사용되는 자원들은 모두 아래와 같은 공통 원칙을 가지고 있습니다.
모든 자원은 물리적으로 제한되어 있으므로, 운영체제는 자원을 최대한 효율적으로 사용해야 한다.
여러 프로그램이 동시에 실행되기 때문에, 자원 충돌을 방지하면서도 성능을 유지해야 한다.
CPU, 메모리, 디스크는 모두 속도의 차이가 존재하기 때문에 이를 보완하기 위하여 캐시와 버퍼를 적절하게 사용해야만 한다.
특정 자원은 한 번에 하나의 작업만 사용할 수 있으므로, 이를 순차적으로 적절하게 분배해야만 한다.
운영체제는 이중 모드를 수행합니다. 앞서 운영체제에는 커널 영역과 사용자 영역이 나누어진다고 이야기 했습니다. 이렇게 영역이 나뉘어지는 이유는 명확합니다. 사용자 영역의 경우는 사용자가 임의로 데이터를 조작하고 저장이 가능하기 때문입니다.
이러한 작업이 문제가 되는 것은 프로그램 내에서는 변경되지 않고 유지가 되어야 하는 정보 또한 조작이 가능할 경우 그 프로그램 자체에 오류를 발생할 수 있기 때문입니다.
그렇기 때문에 사용자 영역에서는 mmap() 와 같이 사용자 프로그램이 커널에게 메모리 매핑을 요청하는 시스템 콜을 사용하여 실행합니다. 이 시스템 콜은 사용자 모드에서 실행은 되지만 내부적으로는 커널 모드로 전환하여 메모리 관리와 관련된 민감한 작업을 수행합니다.
프로그램이 시스템 콜을 실행하면, 보통 소프트웨어 인터럽트(trap) 명령어가 호출되어 CPU는 사용자 모드에서 커널 모드로 전환됩니다. 시스템 콜, 시스템 호출은 운영체제가 메모리 관리 및 시스템 자원을 보호하기 위해 사용자 영역과 커널 영역을 엄격하게 구분하여, 예기치 않은 오류나 악의적인 공격으로부터 보호 받을 수 있게 합니다.
인터럽트의 순서
입출력 장치가 입출력 요청신호와 인터럽트 벡터를 포함하여 CPU에게 요청을 전송합니다.
CPU는 실행 사이클이 끝날 때마다 인터럽트가 발생 했는 지를 확인합니다.
CPU가 플래그 레지스터 중 인터럽트 플래그를 확인하여 현재 인터럽트를 처리할 수 있는 상태인지를 확인합니다.
인터럽트 처리가 가능하다면 현재 실행 중인 프로그램의 인터럽트 서비스 루틴, 인터럽트 핸들러를 인터럽트 벡터 값을 이용하여 메모리에 저장되어 있는 주소로 점프합니다.
인터럽트 핸들러, 서비스 루틴을 실행하여 인터럽트를 처리한 후에 기존의 실행 중이던 프로그램으로 다시 점프하여 기존의 작업을 마저 수행합니다.
프로세스
프로세스는 프로그램이 실행 될 때 운영체제로 부터 독립적으로 할당 받은 메모리의 공간과 해당 프로그램의 실행 상태를 포함하는 인스턴스입니다. 또한 모든 프로세스는 각 프로세스당 하나씩의 프로세스 제어 블록(PCB, Process Control block) 을 갖고 있습니다.
이러한 이유는 프로세스는 실행 순서대로 작동하지 않기 때문입니다. 이러한 이유는 앞서 이야기 했던 것과 같이 컴퓨터의 자원들은 모두 실행될 경우 모두 독점적으로 자원을 차지하고 있기 때문에 다른 작업들이 수행될 수 없습니다. 이러한 이유로 인해 프로세스는 일정한 상태를 유지하는 것이 아니라 계속해서 상태가 변경이 되기 때문에 프로세스 시작시에 상태를 저장하고 있는 PCB가 생성되어야 합니다.
PCB는 위의 정보를 바탕으로 프로세스의 상태가 변경이 되는, 문맥 교환, Context Switch 가 일어 났을 경우에도 정상적으로 작동할 수 있게 끔 합니다. 이러한 문맥 교환은 인터럽트 가 발생할 경우 해당 인터럽트를 먼저 실행해야 하기 때문에 프로세스의 순서가 바뀔 수 있습니다.
인터럽트로 인해 실행되는 프로세스가 모두 실행 되었다면 해당 작업을 마치고 이전의 작업으로 돌아가야 하기 때문에 해당 프로세스에 대한 정보를 커널 영역에서 프로그램 실행 시 PCB를 생성하여 프로세스에 대한 정보를 PCB에 저장합니다.그리고 PCB는 프로세스가 종료 되었을 경우 폐기합니다.
위의 그림은 프로세스 A가 실행되는 과정에서 프로세스 B가 인터럽트로 들어와서 실행 후 다시 프로세스 A로 돌아가는 과정을 설명합니다. 프로세스가 종료될 때 PCB에 프로세스의 문맥이 저장되고 해당 데이터를 프로세스가 실행 되었을 경우 어떻게 언제 읽어와서 복구 시키는지에 대한 순서를 나타낸 그림입니다.
그렇다면 이러한 PCB가 어떠한 정보를 포함하고 있는 지에 대해서 다루어 보겠습니다.
pid : 특정 프로세스를 식별하기 위해 부여하는 고유한 번호입니다.
state : 현재 프로세스가 어떤 상태인지에 대한 정보가 PCB에 포함됩니다. 이 상태에 대해서는 아래에서 자세하게 설명하겠습니다.
sp : 프로세스의 커널 스택의 최상위 주소를 가리키는 포인터입니다. 함수 호출이나 인터럽트가 발생했을 때 현재 실행 중인 프로세스의 상태를 스택에 저장하기 위한 용도로 사용이 됩니다.
stack : 해당 프로세스가 사용하는 커널 모드의 스택을 의미합니다. 8KB의 크기의 배열로 구성되어 있습니다. 함수 호출, 지역 변수 저장, 인터럽트 처리 시 레지스터 값등을 저장하는데 사용됩니다.
프로세스의 상태
생성 상태 : 생성 상태는 운영체제에 의해서 메모리를 주소의 공간을 할당 받은 상태, PCB를 할당 받은 상태를 의미합니다. 이 상태는 CPU의 할당을 기다리고 있는 상태입니다.
준비 상태 : 준비 상태는 당장이라도 CPU의 할당을 받기만 하면 프로그램을 실행할 수 있는 단계를 의미합니다. 이 단계에서는 스케쥴링에서 자신의 순서가 오면 바로 CPU를 받고 프로그램을 실행할 수 있는, 디스패치가 발생하는 단게입니다.
실행 단계 : 실행 단계는 프로그램이 CPU를 할당 받아 실행 중인 상태를 의미합니다. 이 상태에서 종료 상태로 상태가 전환이 되거나, 타이머 인터럽트 혹은 이외의 입출력 장치에 의해서 인터럽트가 발생할 경우 대기 상태로 전환되는 분기가 나뉩니다.
대기 상태 : 대기 상태는 프로세스 실행 도중 입출력 장치로 인해서 인터럽트가 발생하였을 경우, 입출력 장치가 입출력을 끝낼 때까지 기다렸다 입출력 장치가 작업을 끝낼 경우 준비 상태로 CPU 할당을 다시 기다리게 됩니다.
프로세스 생성 기법
프로세스의 계층 구조는 최초의 프로세스로부터 시작하여 부모 프로세스가 자식 프로세스를 갖는 계층 구조를 갖고 있습니다. 이러한 계층 구조에 의해서 자식 프로세스가 부모 프로세스의 ID에 해당하는 PPID를 갖고 있습니다.
기본적으로 부모 프로세스가 자식 프로세스를 생성할 때는 fork와 exec 명령어를 실행하여 생성을 하게 됩니다. fork의 경우 자식 프로세스를 생성하기 위하여 부모 프로세스를 복사합니다. 이러한 이유 중 하나는 프로세스 간의 자원이 공유가 어렵다는 특징을 갖고 있기 때문입니다.
그렇기 때문에 부모 프로세스는 자신을 복사한 후 exec명령어를 실행하여 자식 프로세스에서 필요한 스택 영역, 힙 영역에 맞춰 메모리 공간을 교체하는 과정을 갖습니다. 이렇게 교체를 하고 난 이후에는 모든 영역이 초기화가 되는 과정을 갖습니다.
프로세스, CPU의 스케쥴링
대부분의 프로세스는 CPU와 입출력 장치를 모두 사용하며 실행이 됩니다. 달리 말하자면 프로세스는 실행 단계와 대기 상태를 반복하며 실행이 됩니다. 그렇기 때문에 프로세스는 비디오 재싱이나 디스크 백업과 같이 입출력 작업이 많은 입출력 집중 프로세스, 입출력 버스트(I/O burst) 가 있고 복잡한 수학 연산, 그래픽 처리 작업과 같은 CPU 집중 프로세스, CPU 버스트 가 있습니다.
프로세스는 실행 순서에 맞춰서 실행이 된다는 특징을 갖고 있습니다. 예를 들어 CPU 버스트 작업과 I/O 버스트 작업이 동시에 CPU 작업을 요청 했다고 하면 어떻게 작업을 수행하는 것이 효율적일 까요? I/O 작업의 경우 우선 순위가 높고, 느리지만 비동기적으로 작업을 수행하기 때문에 가장 먼저 I/O 작업을 실행 시켜 놓고 CPU 버스트 작업을 수행하는 것이 효율적인 방법일 것입니다.
이렇게 당장 실행하고 싶은 프로그램이 있다고 하더라도 앞서 있던 프로세스를 어떻게 실행 시키는 냐에 따라서 성능을 발전 시킬 수 있습니다. 그렇기 때문에 현대에는 여러 스케쥴링 기법이 사용되고 있고 이에 대해서 자세하게 알아 보겠습니다.
우선 순위
가장 먼저 생각할 수 있는 스케쥴링을 개선하는 방법은 우선 순위를 부여하는 방법일 것입니다. 특정 작업마다 서로 다른 우선 순위를 부여하고 우선 순위가 높은 순으로 작업을 수행할 경우 앞서 이야기 했던 순서를 기다렸다 작업을 수행해야 한다는 문제는 해결 할 수 있습니다.
하지만 우선순위를 사용할 경우의 문제점은 우선 순위로 인해서 특정 프로세스는 영원히 동작하지 않을 수도 있다는 것입니다. 예를 들어 어떤 프로세스에 비해서 중요한 우선 순위를 가진 프로세스가 계속해서 추가 된다고 가정 해볼 경우 기존에 있던 우선 순위가 낮은 프로세스는 실행되지 못할 가능성이 존재합니다.
스케쥴링 큐
스케쥴링 큐는 앞서 이야기한 우선순위를 부여하는 방식의 또 다른 단점을 해결하기 위하여 등장한 기법입니다. 입출력 장치에도 여러 우선 순위가 있고 같은 우선 순위로 중복되는 경우가 발생할 수 있습니다. 이러한 모든 경우를 고려하여 우선 순위를 부여하는 것은 굉장히 복잡한 방법입니다.
그렇기 때문에 여기서 수행하는 방법은 프로세스들이 특정 목적에 맞춰서 줄을 서서 기다릴 것을 요구하게 됩니다. 여기서 특정 목적의 예는 "CPU를 쓰고 싶은 프로세스", "하드 디스크를 쓰고 싶은 프로세스" 등의 목적이 있을 것입니다.
이 목적에 맞춰 여러 개의 큐로 나누어 자원을 관리하는 것입니다. 큐의 대표적인 예시로 준비 큐, 대기 큐가 존재합니다. 준비 큐는 CPU를 이용하고 싶은 프로세스들이 줄을 서는 것을 의미하고, 대기 큐의 경우 입출력 장치를 이용하기 원하는 프로세스들이 줄을 서는 큐를 의미합니다.
선점형과 비선점형
선점형의 경우 현재 CPU가 작업을 수행하고 있는 프로세스가 있더라도 자원을 즉시 먼저 차지하여 작업을 처리할 수 있는 방법을 의미하고 선점형 스케쥴링은 자원을 강제로 빼앗아 다른 프로세스에 할당할 수 있는 스케쥴링 방법을 의미합니다.
비선점형의 경우 하나의 자원을 사용하고 있다면 다른 프로세스가 종료되거나 스스로 대기 상태에 접어들기 전까지는 다른 프로세스가 끼어들 수 없는 스케쥴링 방식을 의미합니다.
현재 대부분의 운영체제는 선점형 스케쥴링 방식을 사용하고 있습니다. 선점형의 경우 언제든 프로세스가 끼어들 수 있는 구조이기 때문에 자원 독점을 막고 프로세스들이 골고루 끼어들어 자원을 배분할 수 있다는 장점이 있습니다. 하지만 그만큼 많은 문맥 교환이 이루어지기 때문에 오버헤드가 발생할 수 있다는 단점이 존재합니다.
반면 비선점형의 경우 교환의 횟수가 선점형 스케쥴링 보다 적기 때문에 오버헤드는 적지만 하나의 프로세스가 자원을 사용중이라면 당장에 급한 상황이라도 자원을 사용할 수 없다는 단점이 존재합니다.
스케쥴링 알고리즘
선입 선처리, FCFS 스케줄링 : 단순히 큐에 삽입된 순서대로 프로세스를 처리하는 비선점형 알고리즘에 해당합니다. 이 알고리즘은 비록 실행 시간이 짧을 프로세스라고 하더라도 작업 순서를 기다리다보면 작업을 처리하기 위해 실제 작업 처리 시간에 비해 오랜 시간을 기다려야 하는 호위 효과가 발생한다는 문제점을 갖고 있습니다.
이 알고리즘은 간단한 파일 읽기나 네트워크 요청처럼, 초기 CPU 작업이 짧고 이후 I/O 작업이 주를 이루는 경우에 프로세스의 흐름을 예측 가능하기 때문에 유리합니다.
예: 사용자가 웹 브라우저에서 링크를 클릭하여 페이지를 로드할 때, 각 요청이 도착한 순서대로 처리되는 경우
최단 작업 우선 스케쥴링, SFJ 스케쥴링 : 이 방법은 호위 효과 문제를 개선한 알고리즘입니다. 준비 큐에 삽입 된 프로세스 중에서 CPU 이용 시간이 가장 짧은 프로세스부터 실행하는 스케쥴링 방식을 SFJ 스케쥴링 의미합니다.
SJF는 CPU 실행 시간이 짧은 작업을 우선 처리하므로, 인터랙티브 I/O 요청처럼 빠른 응답이 요구되는 경우 유리합니다.
예: 사용자 인터페이스에서 버튼 클릭 시 빠르게 초기 처리를 수행하고 I/O(네트워크/디스크) 요청을 보내는 작업
라운드 로빈 스케쥴링 : 타임 슬라이스라는 개념을 큐에 적용한 알고리즘입니다. 이 알고리즘은 타임 슬라이스에 의해서 정해진 시간 동안만 CPU 자원을 독점하고 해당 시간이 지나면 작업이 모두 종료되지 않았더라도 큐의 맨뒤로 남은 시간을 업데이트 하여 삽입하는 선점형 알고리즘 입니다.
짧은 CPU 실행 후 곧바로 I/O 요청을 하는 인터랙티브 작업에 적합합니다. 짧은 시간 내에 CPU를 할당받아 빠르게 초기 처리를 완료하고 I/O 대기 상태로 전환할 수 있습니다.
예: 텍스트 편집기에서 키 입력 처리, 간단한 명령 실행 등 인터랙티브 애플리케이션
최소 잔여 시간 우선 스케줄링 : 이 기법은 라운드 로빈과 최단 작업 우선 스케쥴링을 결합한 알고리즘입니다. 정해진 시간, 타임 슬라이스 만큼 작업을 수행하되, CPU가 다음 작업을 수행할 프로세스를 남아 있는 작업 시간이 가장 적은 프로세스를 선택하여 작업을 수행합니다.
우선순위 스케쥴링 : 우선 순위 스케쥴링은 작업의 우선 순위가 높은 프로세스를 우선 처리하는 선점형 스케쥴링 기법입니다. 이 방법은 우선 순위가 낮은 프로세스의 경우 자신의 차례가 계속해서 찾아오지 않는 기아 현상이 발생할 수 있다는 단점이 존재합니다. 그래서 이러한 단점을 해결하기 위하여 등장한 것이 에이징 기법입니다. 에이징 기법은 큐내에서 대기하고 있는 시간이 특정 시간 만큼 길 경우 해당 프로세스의 우선 순위를 점차 증가 시키는 방법을 의미합니다.
예: 운영체제의 GUI 응답, 실시간 알림 처리, 백그라운드에서 실행되는 대규모 계산 작업이나 데이터 처리 작업
다단계 큐 스케쥴링 : 우선 순위 큐를 여러개 사용하여 작업을 처리하는 스케쥴링 기법입니다. 우선순위가 가장 높은 큐부터 시작하여 낮은 큐를 여러 개, 프로세스의 유형별로 사용하는 방법입니다.
우선순위에 따른 응답성 보장:
인터랙티브하고 I/O 버스트 작업에 높은 우선순위를 부여하여 사용자 경험을 개선할 수 있습니다.
효율적인 CPU 자원 분배:
CPU 집약적인 작업은 상위 큐의 작업들이 모두 처리된 후에 실행되므로, 전체 시스템의 효율을 높일 수 있습니다.
분리된 관리:
서로 다른 특성을 가진 작업들을 개별적으로 관리함으로써, 각 작업에 적합한 스케줄링 전략을 적용할 수 있습니다.
이러한 특성 때문에 다단계 큐 스케줄링은 다양한 종류의 작업이 공존하는 환경에서, 각 작업의 특성을 반영하여 공정하고 효율적으로 자원을 할당하는 데 유리합니다.
다단계 피드백 큐 스케쥴링 : 다단계 큐 스케쥴링의 경우 우선순위가 정해진 큐마다 작업을 이동하는 것이 불가능 했지만 이 방법의 경우 어떤 프로세스의 CPU 이용 시간이 길면 낮은 우선순위 큐로 이동시키고, 어떤 프로세스가 낮은 우선 순위 큐에서 너무 오래 기다린다면 높은 우선 순위로 이동 시킬 수 있는 알고리즘 입니다.
인터뷰 질문
Q : 운영체제에서 사용자 영역과 커널 영역을 구분하는 이유는 무엇이며, 이 구분이 시스템 안정성과 보안에 어떤 역할을 하는지 설명해 주세요.
정답 조건:
사용자 영역과 커널 영역의 기본 개념을 정확히 이해하고 있어야 함
커널 영역에서 실행되는 운영체제는 민감한 시스템 자원(메모리, 하드웨어)을 직접 관리하며, 사용자 영역의 임의 접근으로부터 이를 보호한다는 점을 언급
시스템 콜을 통해 사용자 모드에서 커널 모드로 전환하여 안전하게 자원을 요청하는 과정을 설명
꼬리 질문:
시스템 콜이 실행될 때 발생하는 모드 전환 과정을 단계별로 설명해 주세요.
사용자 영역에서 직접 메모리나 하드웨어 명령어를 실행할 경우 발생할 수 있는 문제점에는 어떤 것들이 있는지 논의해 보세요.
Q : 프로세스 제어 블록(PCB)이란 무엇이며, PCB에 포함되어야 하는 주요 정보들은 무엇인지 설명해 주세요. 또한 PCB가 문맥 교환(context switch)에서 어떤 역할을 하는지 논의해 주세요.
정답 조건:
PCB가 각 프로세스의 실행 상태, 식별자(PID), 스택 포인터, 커널 스택 등의 정보를 포함한다는 점 언급
프로세스의 현재 상태(생성, 준비, 실행, 대기, 종료 등)를 저장하는 데 사용됨을 설명
문맥 교환 시 현재 실행 중인 프로세스의 상태를 PCB에 저장하고, 재개 시 복원하는 역할을 한다는 점 명시
꼬리 질문:
문맥 교환이 발생할 때 PCB에 저장되는 정보들이 왜 중요한지 구체적으로 설명해 주세요.
PCB가 없었다면 프로세스 관리에 어떤 문제가 발생할 수 있을지 예를 들어 설명해 보세요.
Q : 다단계 큐 스케줄링과 다단계 피드백 큐 스케줄링의 차이점을 설명하고, 각각이 어떤 상황에서 유리한지 예시를 들어 설명해 주세요.
정답 조건:
다단계 큐 스케줄링은 프로세스를 미리 정의된 여러 큐로 분리하고, 한 번 분류된 프로세스는 다른 큐로 이동하지 않는다는 점을 설명
다단계 피드백 큐 스케줄링은 프로세스의 CPU 사용량에 따라 큐 간 이동이 가능하며, I/O 버스트와 CPU 버스트 작업을 효율적으로 처리할 수 있는 점을 언급
각 스케줄링 기법이 특정 작업 유형(인터랙티브, 배치 등)에 따라 어떤 이점을 가지는지 예시를 포함