프로세스 스케줄링

멀티프로그래밍과 시분할의 목적

멀티프로그래밍의 목적은 언제나 어떤 프로세스가 실행되도록 하여 CPU 사용률을 극대화하는 것이다. 시분할의 목적은 CPU 코어를 매우 짧은 시간 간격으로 여러 프로세스 간에 전환시켜, 사용자가 실행 중인 각 프로그램과 상호작용할 수 있도록 하는 데 있다.

이러한 목적을 달성하기 위해, **프로세스 스케줄러(process scheduler)**는 사용 가능한 프로세스들 중 하나(여러 개일 수도 있음)를 선택하여 CPU 코어에서 실행되도록 한다. 각 CPU 코어는 한 번에 하나의 프로세스만 실행할 수 있다.


Linux의 프로세스 표현 구조

리눅스 운영체제에서 **프로세스 제어 블록(PCB)**은 task_struct라는 C 구조체로 표현된다. 이 구조체는 <include/linux/sched.h> 헤더 파일에 정의되어 있으며, 다음과 같은 정보를 포함한다:

  • 프로세스의 상태

  • 스케줄링 및 메모리 관리 정보

  • 열린 파일들의 목록

  • 부모 프로세스, 자식 프로세스, 형제 프로세스 포인터

구조체의 일부 필드 예시는 다음과 같다:

long state;                     // 프로세스 상태
struct sched_entity se;         // 스케줄링 정보
struct task_struct *parent;     // 부모 프로세스
struct list_head children;      // 자식 리스트
struct files_struct *files;     // 열린 파일들
struct mm_struct *mm;           // 주소 공간 정보

리눅스 커널은 현재 실행 중인 프로세스를 current 포인터를 통해 추적하며, 시스템 내 활성 프로세스들은 모두 task_struct의 **이중 연결 리스트(double-linked list)**로 구성된다.

current->state = new_state;

이와 같이 현재 실행 중인 프로세스의 상태를 수정할 수 있다.


프로세스 대기와 스케줄링

단일 코어 시스템에서는 한 번에 오직 하나의 프로세스만 실행되며, 멀티코어 시스템에서는 동시에 여러 프로세스를 실행할 수 있다. 실행 가능한 프로세스 수가 코어 수보다 많을 경우, 남는 프로세스는 대기 상태가 되며 스케줄링 대상이 된다.

메모리에 존재하는 전체 프로세스 수를 **멀티프로그래밍의 정도(degree of multiprogramming)**라고 한다.


I/O 바운드 vs CPU 바운드 프로세스

대부분의 프로세스는 다음 두 종류 중 하나로 분류될 수 있다:

  • I/O 바운드: 입출력에 더 많은 시간을 소비하며 계산은 짧게 수행

  • CPU 바운드: 계산 시간이 길고 I/O 요청은 드물게 발생

멀티프로그래밍과 시분할의 목표를 달성하려면, 이러한 프로세스들의 일반적인 행동 특성을 고려해야 한다.


Scheduling Queues

프로세스가 시스템에 들어오면, **준비 큐(ready queue)**에 들어가 CPU 코어 할당을 기다리게 된다. 이 큐는 일반적으로 연결 리스트로 구성되며, 큐 헤더는 첫 번째 PCB를 가리킨다. 각 PCB는 다음 PCB를 가리키는 포인터를 가진다.

한편, 프로세스는 다음과 같은 이유로 기다림(wait) 상태로 전이될 수 있다:

  • I/O 요청

  • 자식 프로세스 종료 대기

  • 인터럽트 대기 등

이 경우, 해당 프로세스는 wait queue에 들어간다.


스케줄링 흐름 예시 (Figure 3.5 기반 설명)

  • 새 프로세스는 ready queue에 들어간다

  • 실행 중 다음 이벤트가 발생할 수 있다:

    1. I/O 요청 → I/O wait queue로 이동

    2. 자식 프로세스 생성 → 자식 종료 대기 wait queue로 이동

    3. 타임 슬라이스 만료 또는 인터럽트 → 다시 ready queue로 이동

이 과정은 프로세스가 종료될 때까지 반복된다.


CPU Scheduling

프로세스는 생애 주기 동안 ready queue와 wait queue를 반복적으로 이동한다. CPU 스케줄러는 ready queue에서 하나의 프로세스를 선택해 CPU에 할당한다.

  • I/O 바운드 프로세스: 짧은 시간만 CPU 사용 후 I/O 요청

  • CPU 바운드 프로세스: 긴 시간 CPU 점유 필요

대부분의 운영체제는 긴 점유를 허용하지 않으며, **주기적으로 프로세스를 강제로 교체(preemption)**한다.

일반적으로 CPU 스케줄러는 최소 100ms마다 실행되며, 실제로는 훨씬 더 짧은 주기로 실행된다.


스와핑 (Swapping)

일부 운영체제는 스와핑(swapping) 기능을 제공한다. 이는 프로세스를 메모리에서 제거하여 디스크에 저장하고, 나중에 다시 불러오는 방식이다.

  • 목적: 메모리 과잉 사용 시 멀티프로그래밍 수준 감소

  • 스와핑은 프로세스를 완전히 메모리 밖으로 내보낸다

  • 이후 다시 불러와 이전 상태부터 실행을 재개


Context Switch

운영체제가 인터럽트를 감지하면 현재 실행 중인 프로세스를 일시 중단하고 커널 루틴으로 전환한다.

이때 CPU 상태를 **PCB에 저장(state save)**하고, 새로 실행할 프로세스의 상태를 **PCB에서 복원(state restore)**해야 한다.

문맥 교환은 다음 두 단계로 구성된다:

  1. 현재 프로세스 상태 → PCB에 저장

  2. 새로운 프로세스 PCB → CPU 상태로 복원

이 작업은 시스템적인 유용 작업이 아니므로 오버헤드로 간주된다. 속도는 수 마이크로초 수준이며, 하드웨어와 OS에 따라 달라진다.

일부 CPU는 복수 레지스터 세트를 제공하여 문맥 전환을 빠르게 수행한다. 하지만 대부분의 시스템은 레지스터를 메모리로 복사해야 한다.


모바일 시스템에서의 멀티태스킹

iOS

  • iOS 초기 버전은 사용자 앱의 멀티태스킹을 허용하지 않음

  • iOS 4부터 일부 제한된 앱에 대해 백그라운드 실행 허용

  • 이후 버전은 하드웨어 개선에 따라 기능 확장

  • 예: iPad는 두 개의 앱을 동시에 실행하는 Split-screen 기능 제공

Android

  • 처음부터 멀티태스킹 지원

  • 백그라운드 실행을 위해 서비스(Service) 사용

  • 예: 음악 앱은 백그라운드로 이동해도 → 서비스가 계속 오디오 데이터를 전송

서비스는 UI 없이 메모리 소모가 적음, → 모바일 환경에서 효과적인 멀티태스킹 수단

Last updated