본문으로 건너뛰기

프로세스와 스레드

프로세스(Process) 개념

프로세스 는 실행 중인 프로그램으로, 운영체제로부터 자원을 할당받는 작업의 단위를 말합니다. 프로세스는 프로그램을 실행하는데 필요한 데이터, 메모리, 파일, 디바이스 등의 자원을 포함합니다.

process contect: 프로세스가 실행되는 동안 변화하는 정보들을 말합니다. (프로세스 상태, 프로그램 카운터, 레지스터, 메모리 정보, 입출력 상태 등)

프로세스는 다음과 같은 상태를 가집니다.

  • New: 프로세스가 생성 중인 상태
  • Ready: 프로세스가 CPU를 기다리는 상태 (메모리에 올라가 있지만 CPU를 사용하지 않는 상태)
  • Running: 프로세스가 CPU를 사용하여 실행 중인 상태
  • Waiting: 프로세스가 입출력 등의 이벤트를 기다리는 상태
  • Terminated: 프로세스가 실행을 마친 상태 (but 메모리에서 제거되지 않은 상태)

프로세스는 프로세스 제어 블록(PCB)에 의해 관리됩니다. PCB는 프로세스에 대한 정보를 저장하고, 프로세스의 상태 전이를 관리합니다.

컨텍스트 스위칭(Context Switching)은 CPU가 한 프로세스에서 다른 프로세스로 전환하는 것을 말합니다. 컨텍스트 스위칭은 프로세스의 상태를 저장하고 복구하는 작업을 수행합니다.

원래 실행 중이던 프로세스의 상태를 PCB에 저장하고, 다음 실행할 프로세스의 상태를 PCB에서 읽어와 CPU에 적재합니다. (PCB에 저장하면서 해당 프로세스 상태는 ready로 변경, 읽어온 프로세스 상태는 running으로 변경)

모드 변경은 같은 프로세스 내에서 특권 레벨만 전환하는 가벼운 작업인 반면, 컨텍스트 스위칭은 실행 주체 자체를 다른 프로세스로 넘어가는 상대적으로 무거운 작업이다. 따라서 모드 변경을 컨텍스트 스위칭이라고 할 수는 없으며, 컨텍스트 스위칭의 한 부분 집합이나 동등 개념으로 취급하지 않는다.

PCB(Process Control Block) 는 프로세스에 대한 정보를 저장하고, 프로세스의 상태 전이를 관리합니다.(커널 내부에 존재하는 자료구조)

PCB에는 다음과 같은 정보가 포함됩니다.

  • 프로세스 상태: new, ready, running, waiting, terminated
  • 프로그램 카운터: 프로세스가 다음에 실행할 명령어의 주소
  • CPU 레지스터: 누산기, 베이스 레지스터, 스택 포인터 등
  • CPU 스케줄링 정보: 프로세스의 우선순위, 스케줄 큐 포인터 등
  • 메모리 관리 정보: 메모리 사이즈, 메모리 영역 정보
  • 입출력 상태 정보: 프로세스에 할당된 입출력 장치 목록
  • 자원 사용 정보: 사용 중인 자원 목록

장기 스케줄러, 단기 스케줄러, 중기 스케줄러

스케줄러는 어떤 프로세스에게 자원을 할당할지 결정하는 역할을 합니다. 스케줄러는 장기 스케줄러, 단기 스케줄러, 중기 스케줄러로 나뉩니다. (운영체제 커널 내부 코드)

  • 장기 스케줄러(Long-term Scheduler): 디스크에서 메모리로 프로세스를 적재하는 역할을 합니다. 메모리에 적재되는 프로세스의 수를 제어하여 다중 프로그래밍 환경을 유지합니다. (job scheduling)

  • 단기 스케줄러(Short-term Scheduler): CPU를 ready 상태의 프로세스 중 어떤 프로세스에게 할당할지 결정하는 역할을 합니다. CPU 스케줄링을 통해 프로세스를 실행할 순서를 결정합니다. 타이머 인터럽트에 의해 주기적으로 실행됩니다. (CPU scheduling)

현대 운영체제는 대부분 시분할 시스템을 사용하며, 시분할 시스템은 CPU를 여러 프로세스가 공유하여 사용하는 시스템을 말합니다. 시분할 시스템은 프로세스 간의 공정한 CPU 사용을 보장하며, 프로세스의 응답 시간을 최소화합니다. 일반적으로 시분할 시스템은 장기 스케줄러가 없습니다.

  • 중기 스케줄러(Medium-term Scheduler): 메모리에 너무 많은 프로세스가 올라가는 것을 방지하기 위해, 메모리에 있는 프로세스를 디스크로 쫓아내는 역할을 합니다. (swapping, swap out)

프로세스의 생성과 종료

시스템이 부팅된 후, 운영체제가 실행되면서 프로세스가 생성됩니다. 그 이후에 생성되는 프로세스는 부모 프로세스에 의해 생성됩니다.

만약 자식 프로세스가 모두 종료되면, 부모 프로세스는 자식 프로세스의 종료 상태를 수집해야 합니다. 이를 위해 wait() 시스템 콜을 사용합니다.

wait() 시스템 콜은 자식 프로세스가 종료될 때까지 부모 프로세스를 대기 상태로 만들고, 자식 프로세스가 종료되면 자식 프로세스의 종료 상태를 수집합니다.

자식 프로세스가 생성될 떄, 부모 프로세스의 메모리 공간을 복사하여 자식 프로세스를 생성합니다. 이를 fork() 시스템 콜이라고 합니다.

프로세스가 종료될 때, 프로세스는 종료 상태를 운영체제에게 알리고, 운영체제는 프로세스가 사용하던 자원을 회수합니다. 이를 exit() 시스템 콜이라고 합니다.

exec() 시스템 콜은 새로운 프로그램을 실행하는 시스템 콜입니다. exec() 시스템 콜을 사용하면 현재 프로세스의 메모리 공간을 새로운 프로그램으로 덮어씁니다. (fork()와 exec() 시스템 콜을 사용하여 새로운 프로세스를 생성하고 실행할 수 있습니다.)

프로세스 종료의 종류

  • 자발적 종료: 프로세스가 마지막 명령을 수행한 후 exit() 시스템 콜을 호출하여 종료하는 것
  • 비자발적 종료: 부모 프로세스가 자식 프로세스를 강제로 종료시키는 것 (abort 시그널 등)
    1. 자식 프로세스가 자원을 많이 사용하여 시스템 전체의 성능을 해치는 경우
    2. 자식 프로세스가 더 이상 필요하지 않은 경우
    3. 부모 프로세스가 종료되는 경우

기본적으로 fork된 프로세스는 부모 프로세스의 모든 정보와 동일하다. 그렇기 때문에 자신이 복사된 것인지, 원본인지 알 수 없다. 이를 확인하기 위해 fork() 시스템 콜의 반환값을 확인한다. 만약 반환값이 0이라면 자식 프로세스, 0이 아니라면 부모 프로세스이다.

프로세스 간의 협력

  • 프로세스 간 협력: 프로세스 간 협력을 위해 IPC(Inter-Process Communication)를 사용합니다. IPC는 프로세스 간 데이터를 주고받는 것을 말합니다. IPC를 위해 파이프, 메시지 큐, 공유 메모리, 소켓 등의 방법을 사용합니다.

스레드(Thread) 개념

스레드 는 프로세스 내에서 실행되는 흐름의 단위를 말합니다. 스레드는 프로세스 내에서 자원을 공유하며, 프로세스 내의 주소 공간이나 자원을 공유할 수 있습니다. (하나의 프로세스에는 최소 하나의 스레드가 존재합니다.)

스레드는 프로세스 내의 주소 공간이나 자원을 공유하기 때문에, 프로세스 내의 스레드들은 프로세스 내의 데이터 영역이나 코드 영역을 공유합니다. 이를 통해 스레드 간의 통신이 쉽고 빠릅니다.

멀티 스레딩은 멀티 프로세싱보다 적은 자원을 사용하며, 스레드 간의 통신이 쉽고 빠르다는 장점이 있습니다. 그러나 스레드 간의 자원 공유로 인해 동기화 문제가 발생할 수 있습니다.

동기화 문제를 해결하기 위해 뮤텍스(Mutex), 세마포어(Semaphore), 모니터(Monitor) 등의 동기화 기법을 사용합니다.

  • 뮤텍스(Mutex): 상호배제를 위한 동기화 기법으로, 임계 구역에 진입하는 스레드가 다른 스레드에 의해 방해받지 않도록 하는 동기화 기법입니다.

  • 세마포어(Semaphore): 뮤텍스와 유사한 동기화 기법으로, 임계 구역에 진입하는 스레드의 수를 제한하는 동기화 기법입니다.

  • 모니터(Monitor): 뮤텍스와 세마포어를 보다 쉽게 사용하기 위한 추상화된 동기화 기법으로, 임계 구역에 진입하는 스레드의 수를 제한하는 동기화 기법입니다.

CPU 스케줄링 알고리즘

CPU는 여러 프로세스가 동시에 실행되는 것처럼 보이지만, 실제로는 CPU가 여러 프로세스를 번갈아가며 실행하는 시분할 시스템입니다. 프로그램 카운터(PC)를 통해 다음에 실행할 명령어의 주소를 가리키며, CPU 스케줄링 알고리즘을 통해 프로세스를 실행할 순서를 결정합니다.

프로그램 카운터(PC, Program Counter): CPU가 다음에 실행할 명령어의 주소를 가리키는 레지스터

스케줄링의 목표는 다음과 같습니다.

  1. CPU 이용률 최대화: CPU가 놀지 않고 일을 처리하도록 하는 것
  2. 처리율 최대화: 단위 시간당 처리하는 프로세스의 수를 최대화하는 것
  3. 응답 시간 최소화: 사용자가 명령을 입력한 후 첫 번째 응답을 받는 시간을 최소화하는 것
  4. 대기 시간 최소화: 프로세스가 CPU를 사용하기 위해 대기하는 시간을 최소화하는 것
  5. 소요 시간 최소화: 프로세스가 시스템을 사용하는 시간을 최소화하는 것

1,2번은 시스템 성능에 관련된 목표, 3,4,5번은 사용자 관점의 목표입니다.

CPU 스케줄링 알고리즘은 다음과 같은 종류가 있습니다.

  • FCFS(First-Come, First-Served): 먼저 도착한 프로세스를 먼저 실행하는 알고리즘
  • SJF(Shortest Job First): 실행 시간이 가장 짧은 프로세스를 먼저 실행하는 알고리즘
  • Round Robin: 시분할 시스템에서 사용되는 알고리즘으로, 각 프로세스에게 동일한 시간을 할당하여 실행하는 알고리즘
  • Priority Scheduling: 우선순위가 높은 프로세스를 먼저 실행하는 알고리즘
  • Multilevel Queue Scheduling: 여러 개의 큐를 사용하여 다양한 우선순위를 가진 프로세스를 처리하는 알고리즘
  • Multilevel Feedback Queue Scheduling: 프로세스의 상태에 따라 다른 큐로 이동하는 알고리즘
  1. FCFS(First-Come, First-Served)

    • 개념: 프로세스가 CPU를 요청한 순서대로 처리하는 가장 단순한 스케줄링 방식이다.

    • 장점: 구현이 간단하며, 공정성을 어느 정도 보장(먼저 온 순서대로 처리)한다.

    • 단점: 평균 대기 시간이 길어질 수 있으며, CPU 사용 시간이 긴 프로세스가 먼저 오면 뒤에 도착한 짧은 작업들이 오래 기다릴 수 있다(Convoy Effect).

    • 예시: 예를 들어, 3개의 프로세스 A(실행 시간 10ms), B(실행 시간 3ms), C(실행 시간 2ms)가 순서대로 도착했다고 하자. FCFS에서는 A를 모두 처리(10ms)한 뒤 B(3ms), 그 다음 C(2ms) 순으로 처리한다. C는 실행 시간이 짧음에도 A와 B를 기다려 총 15ms를 대기하게 된다.

  2. SJF(Shortest Job First)

    • 개념: 준비 큐에 있는 프로세스들 중에서 실행 시간이 가장 짧은 프로세스를 먼저 처리하는 알고리즘이다. 최적 스케줄링(비선점형 가정)이라 불릴 정도로 평균 대기 시간을 최소화한다.

    • 장점: 평균 대기 시간을 최소화할 수 있다.

    • 단점: 실행 시간을 정확히 예측하기 어렵고, 긴 작업(Long Job)이 뒤로 밀려 “기아(Starvation)” 현상이 발생할 수 있다.

    • 예시: 프로세스 A(8ms), B(4ms), C(2ms), D(1ms)가 거의 동시에 도착했다고 하자. SJF에서는 D(1ms) → C(2ms) → B(4ms) → A(8ms) 순으로 실행한다. 이로써 평균 대기 시간을 최소화한다. 하지만 A처럼 긴 작업은 뒤로 밀릴 가능성이 있다.

    버스트 시간(Burst Time): 프로세스가 CPU를 사용하는 시간을 의미한다. SJF는 프로세스의 버스트 시간을 미리 알고 있어야 하며, 이는 실제 시스템에서는 알기 어려운 문제이다.

    기아 현상(Starvation): 우선순위가 낮은 프로세스가 계속해서 CPU를 할당받지 못하는 상황을 말한다. SJF에서는 실행 시간이 짧은 프로세스가 계속해서 CPU를 할당받아 기아 상태에 빠질 수 있다.

  3. Priority Scheduling

    • 개념: 프로세스마다 우선순위를 부여하고, 우선순위가 높은 프로세스에게 CPU를 먼저 할당한다. 우선순위는 정적일 수도 있고, 동적으로 변경할 수도 있다.

    • 장점: 긴급 처리가 필요한 프로세스를 빠르게 처리 가능하다.

    • 단점: 우선순위가 낮은 프로세스는 실행 기회를 얻지 못하고 기아(Starvation)에 빠질 수 있다. 이를 해결하기 위해 우선순위를 일정 시간이 지나면 높여주는 에이징(Aging) 기법을 적용한다.

    • 예시: A(우선순위 3), B(우선순위 1), C(우선순위 5), D(우선순위 2)라고 할 때, 우선순위가 숫자가 클수록 높다고 가정하면 C(5) → A(3) → D(2) → B(1) 순으로 실행된다.

    에이징(Aging): 우선순위가 낮은 프로세스가 오래 기다리면 우선순위를 높여주는 기법이다. 기아 상태를 방지하기 위해 사용된다.

  4. Round Robin (RR)

    • 개념: 시분할(Time-Sharing) 시스템에서 사용하는 알고리즘으로, 각 프로세스에 동일한 시간 할당량(Time Quantum)을 부여하고, 해당 시간이 지나면 다음 프로세스로 CPU를 넘긴다. 모든 프로세스가 공평하게 CPU를 일정 시간씩 번갈아가며 사용한다.

    • 장점: 모든 프로세스에 대한 응답 시간을 짧게 유지하고, 특정 프로세스가 CPU를 독점하는 상황을 방지한다.

    • 단점: Time Quantum이 너무 짧으면 빈번한 컨텍스트 스위칭으로 오버헤드가 커지고, 너무 길면 FCFS와 유사해진다. 최적의 Time Quantum 설정이 관건이다.

    • 예시: 프로세스 A, B, C가 있고 각자 10ms, 5ms, 6ms의 실행 시간이 필요하다고 하자. Time Quantum을 2ms로 설정하면, 스케줄러는 A(2ms) → B(2ms) → C(2ms) → 다시 A(2ms) → B(남은 3ms 중 2ms) → C(남은 4ms 중 2ms) → A(남은 6ms 중 2ms) … 와 같이 모든 프로세스를 골고루 조금씩 실행한다.

  5. Multilevel Queue Scheduling

    • 개념: 프로세스를 성격이나 우선순위에 따라 여러 개의 큐로 나누고, 각 큐별로 최적의 스케줄링 알고리즘을 적용한다. 예를 들어, 인터랙티브 작업은 RR, 배치(batch) 작업은 FCFS로 처리하는 식이다.

    • 장점: 서로 다른 특성을 지닌 프로세스를 다루기 쉽고, 시스템 요구사항에 따라 다양한 정책 적용이 가능하다.

    • 단점: 큐 간에 우선순위가 정해져 있으면 하위 큐에 속한 프로세스는 계속 뒤로 밀릴 수 있다(기아 문제). 이를 해결하기 위해 Multilevel Feedback Queue 스케줄링을 적용하여 프로세스 특성에 따라 큐를 이동시킬 수 있다.

    • 예시: 상위 우선순위 큐1: 대화형 프로세스(대부분 짧은 CPU 버스트), RR 적용 하위 우선순위 큐2: 배치 프로세스(긴 CPU 버스트), FCFS 적용 인풋 데이터 처리나 사용자 입력 대기 프로그램은 큐1에, 대용량 데이터 연산 같은 배치 작업은 큐2에 분리하여 각각에 맞는 스케줄링을 활용한다.

  6. Multilevel Feedback Queue

    • 개념: Multilevel Queue Scheduling의 단점을 보완하기 위해 고안된 스케줄링 알고리즘으로, 프로세스의 특성에 따라 큐를 이동시키는 기법이다. 프로세스가 CPU를 사용하는 시간이 길면 우선순위를 낮추고, 짧으면 우선순위를 높이는 방식으로 기아 문제를 해결한다.

    • 장점: 프로세스의 특성에 따라 큐를 이동시키기 때문에 다양한 프로세스를 효율적으로 처리할 수 있다.

    • 단점: 큐의 수가 많아지면 관리가 복잡해진다.

    • 예시: 큐1: 우선순위가 높은 큐, RR 적용 (time quantum: 8ms) 큐2: 우선순위가 낮은 큐, RR 적용 (time quantum: 16ms) 큐3: 우선순위가 낮은 큐, FCFS 적용 프로세스가 큐1에서 실행 중일 때, CPU 버스트 시간이 길어지면 큐2로 이동하고, 버스트 시간이 짧아지면 큐1로 이동한다. 큐2에서도 마찬가지로 버스트 시간에 따라 큐3으로 이동할 수 있다.