카테고리 없음

큐(Queue)를 활용한 스케줄링 기법 (버퍼, 프로세스)

kguidebook0001 2026. 2. 1. 14:34


컴퓨터 시스템과 네트워크 서버는 제한된 자원(CPU, 메모리, 대역폭)을 효율적으로 관리해야 하는 숙명을 안고 있습니다. 수많은 요청이 동시에 쏟아질 때, 시스템이 마비되지 않고 안정적으로 서비스를 제공할 수 있는 비결은 바로 큐(Queue) 자료구조를 활용한 스케줄링(Scheduling)버퍼링(Buffering) 기술에 있습니다. 이 글에서는 큐가 운영체제의 프로세스 관리와 데이터 통신에서 어떻게 '완충 지대' 역할을 수행하는지, 그리고 대표적인 스케줄링 알고리즘인 FCFS와 라운드 로빈(Round Robin)이 큐를 통해 어떻게 구현되는지 심층 분석합니다.

1. 시스템 안정성의 핵심: 버퍼(Buffer)로서의 큐

하드웨어 장치 간, 혹은 소프트웨어 모듈 간에는 필연적으로 속도 차이가 존재합니다. 예를 들어, CPU는 초고속으로 데이터를 처리하지만, 디스크나 네트워크 I/O는 상대적으로 매우 느립니다. 이 속도 불균형을 해소하기 위해 큐를 버퍼(Buffer)로 사용합니다.

1-1. 생산자-소비자 패턴 (Producer-Consumer Pattern)

큐 기반 스케줄링의 가장 기본이 되는 모델입니다.

  • 생산자(Producer): 데이터를 생성하여 큐(버퍼)에 넣는(Enqueue) 주체입니다.
  • 소비자(Consumer): 큐에서 데이터를 꺼내어(Dequeue) 처리하는 주체입니다.

이 구조 덕분에 생산자는 소비자의 처리 속도를 기다릴 필요 없이 데이터를 큐에 쌓아두고 자신의 작업을 계속할 수 있으며, 이를 비동기 처리(Asynchronous Processing)라고 합니다.

2. 운영체제(OS)의 프로세스 스케줄링 기법

운영체제는 CPU라는 핵심 자원을 여러 프로세스(프로그램)가 공평하게 사용할 수 있도록 관리해야 합니다. 이때 준비 큐(Ready Queue)를 사용하여 실행 대기 중인 프로세스들을 줄 세웁니다.

2-1. 선입선출 스케줄링 (FCFS / FIFO)

가장 단순한 형태의 스케줄링으로, 큐의 본질인 FIFO(First-In, First-Out)를 그대로 따릅니다. 먼저 도착한 프로세스가 CPU를 먼저 할당받습니다.

  • 장점: 구현이 매우 간단하고 공평해 보입니다.
  • 단점(Convoy Effect): 처리 시간이 긴 프로세스가 앞에 있으면, 뒤에 있는 짧은 작업들이 하염없이 기다려야 하는 병목 현상이 발생합니다.

2-2. 라운드 로빈 (Round Robin)

시분할 시스템(Time Sharing System)에서 사용되는 방식으로, 큐의 순서를 따르되 시간 할당량(Time Quantum)을 둡니다.

  • 동작 원리: 프로세스가 할당된 시간 동안만 CPU를 사용하고, 작업이 끝나지 않으면 다시 준비 큐의 맨 뒤로 들어갑니다.
  • 특징: 큐가 원형으로 연결된 것처럼 동작하며, 모든 프로세스가 응답 시간을 보장받을 수 있어 멀티태스킹 환경에 적합합니다.

3. 파이썬(Python)을 이용한 작업 스케줄링 구현

파이썬의 `queue` 모듈은 스레드 간 통신을 위한 안전한(Thread-safe) 큐를 제공합니다. 다음은 큐를 이용해 작업을 순차적으로 처리하는 간단한 워커(Worker) 스케줄링 예제입니다.


import queue
import time
import threading

# 작업 대기열 (버퍼 역할)
task_queue = queue.Queue()

def worker(worker_id):
    while True:
        # 큐에서 작업 가져오기 (비어있으면 대기)
        task = task_queue.get()
        if task is None:
            break
            
        print(f"[Worker {worker_id}] Processing task: {task}")
        time.sleep(1)  # 작업 처리 시뮬레이션
        task_queue.task_done()

# 1. 스레드(소비자) 생성 및 실행
threads = []
for i in range(2):
    t = threading.Thread(target=worker, args=(i,))
    t.start()
    threads.append(t)

# 2. 작업(생산자) 투입
for item in ["Email", "Rendering", "Data Backup", "Log Analysis"]:
    task_queue.put(item)

# 3. 모든 작업 완료 대기
task_queue.join()

# 4. 작업 종료 신호 전송
for i in range(2):
    task_queue.put(None)

위 코드에서 `task_queue`는 작업 요청이 폭주하더라도 워커 스레드가 처리할 수 있는 속도에 맞춰 작업을 분배하는 버퍼 역할을 수행합니다.

[핵심 요약 : 큐와 스케줄링]
1. 버퍼링: 큐는 빠른 CPU와 느린 I/O 장치 사이의 속도 차이를 완충하여 시스템의 비동기 처리를 가능하게 합니다.
2. 스케줄링: 운영체제는 준비 큐(Ready Queue)를 통해 FCFS나 라운드 로빈 방식으로 프로세스의 CPU 점유 순서를 제어합니다.
3. 안정성: 트래픽 폭주 시 요청을 큐에 쌓아둠으로써(대기열), 서버가 다운되지 않고 순차적으로 처리할 수 있는 탄력성을 제공합니다.