프로세스란 실행 중인 프로그램을 의미한다. 프로세스는 PC와 레지스터를 포함한 현재 상태, 텍스트(=프로그램 코드), 스택(=임시 데이터), 데이터(=전역 변수), 힙으로 이루어져 있다.
위 그림은 프로세스의 상태 변화를 나타낸 그림으로 총 5가지 상태로 나눌 수 있다.
New: 프로세스가 새롭게 생성된 상태
Ready: 프로세서에 의해 실행될 수 있도록 준비된 상태
Running: 프로세서에 의해 실행되고 있는 상태
Waiting: 어떠한 이벤트가 일어나길 기다리는 상태
Terminated: 프로세스가 실행을 완전히 마친 상태
OS는 프로세스마다 PCB를 하나씩 두고 관리한다. 여기에는 프로세스의 상태, PC, 레지스터, 스케쥴링 정보, 할당된 메모리에 대한 정보, 할당된 I/O 디바이스(ex. open file) 정보, CPU 타임 등이 들어있다.
ready 상태에 있는 여러 프로세스들 중에서 다음에 실행될 프로세스를 고르는 것을 CPU 스케쥴링(CPU scheduling)이라고 하며, CPU 스케쥴러(CPU scheduler)에 의해 행해진다. 스케쥴링의 목표는 CPU 이용률을 최대화하거나 응답 시간을 최소화하는 것이다.
OS가 제공하는 스케쥴링 큐(queue)는 그 역할에 따라 세가지로 나눌 수 있다.
1. 작업 큐(job queue): 시스템에 있는 모든 프로세스들의 집합
2. 준비 큐(ready queue): 메인 메모리에 적재되어 있고 ready 상태로 프로세서의 할당을 기다리는 프로세스들의 집합
3. 디바이스 큐(device queue): I/O 디바이스를 기다리는 프로세스들의 집합
OS가 제공하는 스케쥴러도 그 역할에 따라 세가지로 나눌 수 있다.
1. 장기 스케쥴러(long-term scheduler, job scheduler): 작업 큐에서 메인 메모리로 적재할 프로세스를 고르는 것으로 다중 프로그래밍의 정도를 결정한다.
2. 중기 스케쥴러(mid-term scheduler): 메인 메모리에 적재되었지만 오랫동안 실행되지 않거나 사용량이 적은 프로세스들은 메모리를 낭비하게 된다. 그러한 프로세스들을 중기 스케쥴러가 골라 메모리로부터 내쫓는 것을 스와핑(swapping)이라고 한다. 이렇게 함으로써 다중 프로그래밍의 정도를 조절할 수 있다.
3. 단기 스케쥴러(short-term shceduler, CPU scheduler): 준비 큐에서 프로세서를 할당할 프로세스를 고른다.
이전 글에서 다루었던 내용인데, 프로세스마다 CPU를 활용하는 정도가 다르다. 주로 연산 작업을 하는 프로세스는 CPU를 많이 쓰기 때문에 CPU burst라고 하며, 연산보다 I/O 작업을 많이 하는 프로세스는 I/O burst라고 한다. 메모리에 적재된 프로세스들이 모두 I/O burst면 어떻겠는가? CPU 이용률은 낮아질 수 밖에 없다. 따라서 CPU burst와 I/O burst의 조합을 고려하여 스케쥴링하여야 하고, 이는 장기 스케쥴러가 담당한다.
인터럽트가 발생하거나 시스템 콜이 호출되면 실행 중이던 프로세스 A의 정보를 PCB에 저장한다. 그리고 CPU 스케쥴러에 의해 지정된 다음 프로세스 B를 실행하기 위해 디스패쳐(dispatcher)가 B의 PCB를 불러오면 해당 프로세스를 실행한다. 이 과정을 문맥 교환(context switch)라고 한다.
다음에 실행될 프로세스를 고르기 위한 기준은 다음과 같다.
1. CPU 이용률
2. 처리량(throughput): 단위시간 실행을 완료한 프로세스의 수
3. 소요시간(turnaround time): 프로세스의 시작부터 종료까지 걸리는 시간
4. 응답시간(response time): 요청을 보낸 시간부터 처음 응답받을 때까지 걸리는 시간
5. 대기시간(waiting time): 준비 큐에서 대기하는 시간
시분할 시스템에서는 여러 사용자가 있고 이들에게 공평하고 서비스를 제공해야 하므로 사용자 간에 응답 시간의 차이를 최소화하는 것이 중요하다.
CPU 스케쥴링 알고리즘은 7가지가 있지만 이 글에서 자세한 설명은 생략하겠다.
1. FCFS(first come first served)
2. SJF(shortest job first)
3. SRTF(shortest remaining time first)
4. 우선순위(priority)
5. 라운드 로빈(round robin)
6. 다단계 큐(multi-level queue): 여러 큐 사이에 우선순위가 존재하며 각 큐들은 서로 다른 스케쥴링 알고리즘을 가질 수 있다.
7. 다단계 피드백 큐(multi-level feedback queue): 여러 큐가 있지만 프로세스가 들어갈 수 있는 입구는 하나다. 여러 큐 사이에서 이동할 수 있으며, 프로세스가 하나의 큐에서 작업을 마치지 못하면 다음 큐로 이동하는 식이다.
프로세스들은 정보를 공유하거나 하나의 작업을 같이 처리하는 등의 이유로 서로 간에 통신할 수 있다.
IPC는 두가지 종류가 있다.
1. 공유 메모리(shared memory): 프로세스 간에 통신할 공유 메모리를 메모리에 생성하기 때문에 커널이 관여할 필요가 없어 성능 면에서 좋다. 하지만 공유되는 데이터에 동시에 접근할 경우 동기화 문제가 생길 수 있다.
2. 메시지 패싱(message passing): 프로세스가 메시지를 전달하거나 받을 때 시스템 콜을 부르고, 커널이 메시지 전달을 관리하므로 동기화에 문제가 없다. 하지만 메시지를 주고받을 때마다 시스템 콜을 사용하여 성능 면에서 떨어진다.
프로세스는 기본적으로 하나의 PC를 가지고 있기 때문에 하나의 실행 흐름을 가진다. 하지만 한 프로세스의 PCB에 다수의 PC가 있다고 생각해보자. 하나의 프로세스가 여러 위치에서 동시에 실행될 것이다. 여기서 프로세스 내 실행 흐름을 스레드(thread)라고 한다. 아래 그림은 하나의 프로세스를 여러 실행 단위로 나눴을 때의 메모리 상태이다.
하나의 프로세스 내에서 둘 이상의 스레드가 동시에 작업을 수행하는 것을 멀티 스레딩(multithreading)이라고 한다.
멀티 스레딩을 사용할 경우 얻을 수 있는 장점은 한 프로세스에서 생성된 스레드들은 서로 공유하는 부분이 생기므로 메모리 공간과 시스템 자원의 소모가 줄어든다는 점이다. 또한 스레드 간 통신을 위해서는 별도의 자원 요청 없이 힙이나 전역 변수 공간을 사용할 수 있다. 그리고 스레드들끼리 문맥 교환 시 캐시 메모리를 비울 필요가 없고 스택 영역만 변경하면 되기 때문에 문맥 교환으로 인한 오버헤드가 줄어든다.
멀티 스레딩을 사용할 경우 발생할 수 있는 문제점은 공유하는 데이터에 동시에 여러 스레드가 접근할 경우 동기화(synchronization) 문제가 발생할 수 있다는 점이다. 동기화 문제를 해결하기 위해서는 해당 데이터에 접근을 통제하여야 하는데 여기서 병목 현상이 발생할 수도 있다.
Multiprogramming | Multithreading |
---|---|
많은 메모리 공간 사용 | 적은 메모리 공간 사용 |
동기화 문제 없음 | 동기화 문제 있음 |
문맥 전환 시 오버헤드가 큼 | 문맥 전환 시 오버헤드가 적음 |
한 프로세스의 예기치 못한 종료가 다른 프로세스의 종료로 이어지지 않음 | 한 스레드가 예기치 않게 종료하면 전체 스레드가 종료될 수 있음 |
추가로, 멀티 태스킹(multitasking)은 다중 프로그래밍에서 스케쥴링에 따라 정해진 시간동안 교대로 작업하는 것이다.