이 문서의 과거 버전 (r1)을 보고 있습니다. 수정일: 2026.02.13 22:23
운영체제에서 프로세스와 스레드는 프로그램 실행의 기본 단위이다. 이들은 컴퓨터 시스템이 여러 작업을 동시에 수행하거나, 하나의 응용 프로그램 내에서 여러 작업을 병렬로 처리할 수 있게 하는 핵심 메커니즘을 제공한다.
프로세스는 실행 중인 프로그램을 의미하며, 운영체제로부터 독립된 메모리 공간과 시스템 자원을 할당받는다. 각 프로세스는 코드, 데이터, 힙, 스택 영역으로 구성된 자신만의 주소 공간을 가지며, 다른 프로세스의 메모리 영역에 직접 접근할 수 없다. 이는 프로세스 간의 안정성과 보안을 보장하는 중요한 특성이다.
반면, 스레드는 하나의 프로세스 내에서 실행되는 흐름의 단위이다. 같은 프로세스에 속한 스레드들은 코드, 데이터, 힙 영역과 같은 메모리 자원을 공유한다. 각 스레드는 자신만의 레지스터 집합과 스택을 가지며, 프로그램 카운터를 독립적으로 관리한다. 스레드는 프로세스에 비해 생성과 관리의 오버헤드가 적고, 공유 메모리를 통해 효율적인 통신이 가능하다.
프로세스와 스레드의 개념은 멀티태스킹, 멀티프로세싱, 멀티스레딩과 같은 현대 컴퓨팅의 기반이 된다. 운영체제는 스케줄러를 통해 여러 프로세스와 스레드에 CPU 시간을 할당하고, 문맥 교환을 통해 빠르게 전환함으로써 사용자에게 동시에 여러 작업이 실행되는 것 같은 환상을 제공한다.
프로세스는 실행 중인 프로그램을 의미한다. 프로그램은 저장 장치에 존재하는 수동적인 명령어와 데이터의 집합인 반면, 프로세스는 이 프로그램이 메모리에 적재되어 CPU에 의해 실행되는 능동적인 실체이다. 운영체제는 여러 프로세스를 동시에 관리하며, 각 프로세스는 독립된 자원 공간을 할당받는다.
프로세스는 실행 과정에서 여러 상태를 거친다. 주요 상태로는 생성(new), 준비(ready), 실행(running), 대기(waiting), 종료(terminated) 상태가 있다. 준비 상태는 CPU를 할당받기 위해 대기하는 상태이며, 실행 상태는 실제로 CPU에서 명령어를 수행하는 상태이다. 입출력 작업과 같은 이벤트를 기다릴 때는 대기 상태가 된다. 이러한 상태 전이는 운영체제의 스케줄러와 디스패처에 의해 관리된다.
각 프로세스를 관리하기 위해 운영체제는 프로세스 제어 블록(PCB)이라는 자료 구조를 유지한다. PCB에는 다음과 같은 정보가 저장된다.
정보 종류 | 설명 |
|---|---|
프로세스 식별자(PID) | 프로세스를 고유하게 구분하는 번호 |
프로세스 상태 | 현재의 상태(실행, 준비, 대기 등) |
프로그램 카운터(PC) | 다음에 실행할 명령어의 주소 |
CPU 레지스터 정보 | |
메모리 관리 정보 | |
할당된 자원 정보 | 열린 파일 목록, 입출력 장치 정보 등 |
CPU 스케줄링 정보 | 프로세스의 우선순위, 스케줄링 큐 포인터 등 |
계정 정보 | CPU 사용 시간, 시간 제한 등 |
프로세스는 주로 부모-자식 관계를 통해 생성된다. 시스템 호출인 fork()를 통해 기존 프로세스(부모)가 자신의 복사본인 새로운 프로세스(자식)를 생성하는 방식이 일반적이다. 자식 프로세스는 exec() 시스템 호출을 통해 새로운 프로그램을 자신의 메모리 공간에 적재하여 실행할 수 있다. 프로세스는 정상적으로 작업을 완료하거나(exit()), 오류가 발생하거나, 부모 프로세스에 의해 강제 종료되는 경우에 소멸된다.
프로세스는 실행 중인 프로그램을 의미한다. 프로그램이란 저장 장치에 존재하는 명령어와 데이터의 집합인 정적인 개체인 반면, 프로세스는 그 프로그램이 메모리에 적재되어 CPU에 의해 실행되는 동적인 활동의 단위이다. 운영체제는 이러한 프로세스를 관리하는 기본 단위로 삼는다.
프로세스는 실행 과정에서 여러 가지 상태를 거친다. 일반적인 상태는 다음과 같다.
상태 | 설명 |
|---|---|
생성(New) | 프로세스가 생성된 직후의 상태이다. |
준비(Ready) | |
실행(Running) | CPU를 할당받아 명령어를 실행하고 있는 상태이다. |
대기(Waiting) | 입출력(I/O) 작업 완료나 다른 사건을 기다리는 상태이다. CPU를 필요로 하지 않는다. |
종료(Terminated) | 프로세스의 실행이 완료되거나 강제 종료된 상태이다. |
프로세스는 디스패처에 의해 CPU를 할당받아 실행 상태가 되고, 할당된 시간을 다 쓰거나 입출력 요청을 하면 CPU를 반환한다. 실행 상태에서 입출력 요청을 하면 대기 상태로 전환되며, 입출력이 완료되면 다시 준비 상태가 되어 CPU 할당을 기다린다. 이와 같은 상태 전이는 프로세스 스케줄러에 의해 관리된다.
프로세스 제어 블록은 운영체제가 프로세스를 관리하기 위해 필요한 모든 정보를 담고 있는 자료 구조이다. 각 프로세스가 생성될 때마다 고유한 PCB가 할당되며, 프로세스가 종료되면 PCB도 함께 소멸된다. 운영체제는 실행 중인 모든 프로세스의 PCB를 연결 리스트나 테이블 형태로 유지하며, 이를 통해 프로세스의 상태를 추적하고 프로세스 스케줄링을 수행한다.
PCB에 저장되는 정보는 운영체제마다 다르지만, 일반적으로 다음과 같은 범주로 구분된다.
정보 범주 | 포함 내용 예시 |
|---|---|
프로세스 식별 정보 | 프로세스 ID(PID), 부모 프로세스 ID, 사용자 ID |
프로세스 상태 정보 | 현재 상태(실행, 준비, 대기 등), 프로그램 카운터(PC) 값 |
CPU 관련 정보 | |
메모리 관리 정보 | |
입출력 상태 정보 | 프로세스에 할당된 입출력 장치 목록, 열린 파일 목록 |
문맥 교환이 발생하면, 운영체제는 현재 실행 중인 프로세스의 레지스터 상태와 프로그램 카운터 값을 해당 프로세스의 PCB에 저장한다. 그리고 다음에 실행할 프로세스의 PCB에서 저장된 상태 정보를 불러와 레지스터와 프로그램 카운터에 복원함으로써 프로세스 실행을 재개한다. 이 과정에서 PCB는 프로세스의 실행 상태를 보존하는 핵심적인 역할을 담당한다.
프로세스는 부모 프로세스에 의해 새로운 자식 프로세스가 생성되는 방식으로 늘어난다. 이 과정을 fork라고 부르기도 한다. 대부분의 운영체제에서 새로운 프로세스는 기존 프로세스를 복제하는 방식으로 생성된다. 자식 프로세스는 부모 프로세스의 메모리 공간, 열린 파일 디스크립터, 실행 문맥 등을 상속받는다. 이후 자식 프로세스는 exec 시스템 호출을 통해 자신의 메모리 공간을 새로운 프로그램 이미지로 교체하고 독립적으로 실행된다.
프로세스가 종료되는 조건은 다양하다. 프로세스가 자신의 마지막 명령을 실행하고 운영체제에 exit 시스템 호출을 통해 종료를 요청하는 것이 일반적이다. 또한, 부모 프로세스가 자식 프로세스를 강제로 종료시키거나, 치명적인 오류(예: 세그멘테이션 폴트)가 발생했을 때 운영체제에 의해 강제 종료될 수 있다. 모든 프로세스는 종료 시 자신의 종료 상태를 부모 프로세스에게 전달한다.
부모 프로세스는 wait 시스템 호출을 통해 자식 프로세스의 종료를 기다리고, 그 종료 상태를 회수한다. 이 과정을 통해 운영체제는 종료된 프로세스와 관련된 시스템 자원(메모리, 열린 파일 등)을 정리한다. 부모 프로세스가 자식의 종료 상태를 회수하지 않으면, 해당 프로세스는 완전히 정리되지 않은 좀비 프로세스 상태로 남게 된다. 반대로, 부모 프로세스가 자식보다 먼저 종료되면, 해당 자식 프로세스는 고아 프로세스가 되어 운영체제의 init 프로세스 같은 특정 프로세스의 자식으로 재할당되는 경우가 많다.
스레드는 프로세스 내에서 실행되는 흐름의 단위이다. 하나의 프로세스는 최소 하나의 스레드(메인 스레드)를 가지며, 여러 개의 스레드를 가질 수 있다. 이렇게 하나의 프로세스 내에서 여러 스레드가 동시에 작업을 수행하는 것을 멀티스레딩이라고 한다. 각 스레드는 프로그램 카운터, 레지스터 집합, 스택 영역과 같은 독립적인 실행 환경을 가지지만, 코드, 데이터, 힙 영역과 같은 메모리 자원은 같은 프로세스에 속한 다른 스레드들과 공유한다.
스레드가 도입된 주요 필요성은 성능 향상과 효율적인 자원 활용에 있다. 새로운 프로세스를 생성하는 것보다 스레드를 생성하는 것이 훨씬 빠르고 메모리 오버헤드가 적다. 또한, 같은 프로세스 내의 스레드들은 메모리를 공유하기 때문에 데이터 교환이 간편하며, 문맥 교환 시에도 캐시 메모리를 더 효율적으로 사용할 수 있다. 이는 특히 입출력 작업이 많은 프로그램이나 사용자 인터페이스 응답성을 유지해야 하는 프로그램에서 유용하다.
운영체제의 스레드 지원 방식에 따라 사용자 스레드와 커널 스레드로 구분된다. 사용자 스레드는 커널의 지원 없이 사용자 수준의 스레드 라이브러리에 의해 관리된다. 반면, 커널 스레드는 운영체제 커널이 직접 인식하고 관리한다. 이 두 모델의 주요 특징은 다음과 같다.
구분 | 관리 주체 | 장점 | 단점 |
|---|---|---|---|
사용자 레벨 라이브러리 | 커널 개입 없이 빠른 생성/전환, 이식성 높음 | 한 스레드의 블록이 전체 프로세스를 블록시킴, 커널은 스레드를 인식하지 못함 | |
운영체제 커널 | 커널이 각 스레드를 개별 스케줄링, 한 스레드 블록이 다른 스레드에 영향 적음 | 사용자 모드에서 커널 모드로의 전환으로 인한 오버헤드 발생 |
이러한 차이를 보완하기 위해 다대일 모델, 일대일 모델, 다대다 모델과 같은 혼합형 스레딩 모델이 사용되기도 한다.
스레드는 프로세스 내에서 실행되는 흐름의 단위이다. 하나의 프로세스는 최소한 하나의 스레드, 즉 메인 스레드를 가지며, 필요에 따라 여러 개의 스레드를 생성하여 동시에 작업을 수행할 수 있다. 스레드는 프로세스의 코드, 데이터, 힙 영역을 공유하지만, 각 스레드는 독립적인 스택과 레지스터 상태, 프로그램 카운터를 가진다. 이는 하나의 애플리케이션이 동시에 여러 작업을 처리할 수 있게 하는 기본 모델이다.
스레드가 필요한 주요 이유는 응답성 향상과 자원 효율성, 그리고 병렬성 활용에 있다. 단일 스레드 프로세스에서 긴 작업이 실행되면 전체 프로그램이 멈춘 것처럼 보일 수 있다. 반면, 멀티스레딩을 사용하면 사용자 인터페이스를 담당하는 스레드는 응답을 유지하면서 백그라운드에서 계산이나 입출력 작업을 다른 스레드가 처리할 수 있다. 또한, 프로세스를 새로 생성하는 것에 비해 스레드를 생성하는 오버헤드는 훨씬 적다. 이는 스레드가 프로세스의 메모리 공간을 공유하기 때문에 새로운 주소 공간을 할당하거나 많은 자원을 복제할 필요가 없기 때문이다.
특징 | 설명 |
|---|---|
자원 공유 | 스레드는 같은 프로세스 내의 코드, 데이터, 힙, 열린 파일 등의 자원을 공유한다. |
경량성 | 프로세스 생성보다 생성 및 문맥 교환 비용이 적게 든다. |
독립적 실행 흐름 | 각 스레드는 자신만의 스택, 레지스터 상태, 프로그램 카운터를 가진다. |
통신 효율성 | 공유 메모리를 통해 스레드 간 데이터 교환이 빠르고 쉽다. |
멀티코어 프로세서 시스템에서는 스레드의 중요성이 더욱 커진다. 여러 스레드를 서로 다른 코어에 분배하여 진정한 병렬 처리를 구현함으로써 애플리케이션의 전체 처리 속도를 크게 높일 수 있다. 따라서 현대적인 소프트웨어, 특히 서버, 게임, 과학 계산, 그래픽 처리 프로그램 등은 성능과 효율성을 위해 필수적으로 멀티스레딩 기법을 활용한다.
사용자 스레드는 운영체제 커널의 지원 없이 사용자 수준의 스레드 라이브러리에 의해 완전히 관리되고 스케줄링되는 스레드이다. 반면, 커널 스레드는 운영체제 커널에 의해 직접 생성되고 관리되는 스레드이다. 이 두 모델의 근본적인 차이는 스레드의 생성, 스케줄링, 동기화를 누가 담당하느냐에 있다.
사용자 스레드의 주요 특징은 다음과 같다.
* 관리 주체: 스레드 라이브러리 (예: POSIX Pthreads의 일부 구현, 이전 자바 Green Threads)
* 커널 인지도: 커널은 사용자 스레드의 존재를 알지 못하며, 하나의 프로세스로만 인식한다.
* 문맥 교환: 스레드 간 전환 시 커널 모드로의 전환이 필요 없어 오버헤드가 매우 적다.
* 블로킹 문제: 하나의 사용자 스레드가 시스템 콜 등으로 블록되면, 해당 프로세스 내의 모든 다른 사용자 스레드도 함께 블록된다. 이는 커널이 프로세스 자체를 블록 상태로 간주하기 때문이다.
커널 스레드의 주요 특징은 다음과 같다.
* 관리 주체: 운영체제 커널
* 커널 인지도: 커널이 각 스레드를 개별적인 스케줄링 단위로 인식하고 관리한다.
* 문맥 교환: 스레드 간 전환 시 커널 모드로 진입해야 하므로 사용자 스레드에 비해 오버헤드가 크다.
* 블로킹 문제: 하나의 스레드가 블록되어도 커널이 다른 스레드로 스케줄링할 수 있어, 동일 프로세스 내 다른 스레드의 실행이 가능하다.
두 모델의 장단점을 비교하면 다음과 같다.
특성 | 사용자 스레드 | 커널 스레드 |
|---|---|---|
구현 및 이식성 | 커널 변경 없이 라이브러리로 구현 가능, 이식성 높음 | 운영체제 커널의 지원이 필요함 |
스케줄링 속도 | 매우 빠름 (사용자 공간 내 처리) | 상대적으로 느림 (커널 모드 전환 필요) |
블로킹 영향 | 한 스레드 블록 시 전체 프로세스 블록 | 한 스레드 블록 시 다른 스레드 실행 가능 |
다중 처리기 활용 | 단일 CPU 코어에서만 효율적 |
현대 운영체제는 주로 커널 스레드 모델을 채택하거나, 사용자 스레드와 커널 스레드를 연결하는 하이브리드 스레드 모델(예: N:1, M:N 모델)을 사용한다. M:N 모델은 M개의 사용자 스레드를 N개의 커널 스레드에 매핑하여, 사용자 수준의 빠른 스케줄링과 커널 수준의 병렬성 및 블로킹 회피 장점을 모두 취하려는 시도이다.
프로세스와 스레드의 핵심 차이는 자원의 할당과 독립성에 있다. 프로세스는 운영체제로부터 독립적인 메모리 공간, 파일 디스크립터, 보안 컨텍스트 등의 자원을 할당받는 실행 단위이다. 각 프로세스는 자신만의 가상 주소 공간을 가지며, 기본적으로 다른 프로세스의 메모리에 직접 접근할 수 없다. 이는 높은 안정성과 보안을 제공하지만, 프로세스 간 문맥 교환에는 상대적으로 많은 오버헤드가 발생한다.
반면, 스레드는 하나의 프로세스 내에서 생성되는 실행 흐름의 단위이다. 동일한 프로세스에 속한 스레드들은 힙 메모리, 전역 변수, 열린 파일 등의 자원을 공유한다. 각 스레드는 독립적인 스택과 레지스터 상태를 가지지만, 코드와 데이터 영역은 공유한다. 이로 인해 스레드 간 통신은 공유 메모리를 통해 매우 효율적으로 이루어질 수 있으며, 생성과 문맥 교환에 드는 오버헤드가 프로세스에 비해 훨씬 적다.
비교 항목 | 프로세스 | 스레드 |
|---|---|---|
자원 할당 | 운영체제로부터 독립적인 자원(메모리, I/O 등)을 할당받음 | 부모 프로세스의 자원을 공유함 |
독립성 | 각 프로세스는 완전히 독립된 실행 환경을 가짐 | 동일 프로세스 내 스레드들은 메모리와 자원을 공유함 |
통신 방법 | 프로세스 간 통신(IPC) 메커니즘(파이프, 소켓, 공유 메모리 등)이 필요 | 공유 메모리를 통해 직접 데이터 접근 및 교환이 가능함 |
생성/전환 오버헤드 | 상대적으로 큼(새로운 메모리 공간 설정 등) | 상대적으로 작음 |
장애 전파 | 한 프로세스의 장애가 다른 프로세스에 직접 영향을 미치지 않음 | 한 스레드의 문제(예: 메모리 접근 오류)가 전체 프로세스를 종료시킬 수 있음 |
이러한 차이로 인해 멀티태스킹 환경에서는 안정성이 중요한 작업에는 독립적인 프로세스를, 빠른 응답과 효율적인 자원 공유가 필요한 작업(예: 웹 서버의 다중 연결 처리)에는 멀티스레딩을 적용하는 것이 일반적이다.
프로세스는 운영체제로부터 독립된 자원을 할당받는 실행 단위이다. 각 프로세스는 별도의 주소 공간, 파일 디스크립터, 그리고 자격 증명을 가지며, 기본적으로 다른 프로세스의 메모리나 자원에 직접 접근할 수 없다. 이는 하나의 프로세스에 오류가 발생하더라도 다른 프로세스에 영향을 미치지 않도록 보호하는 메모리 보호의 핵심 원리이다. 프로세스 간 통신이 필요할 경우에는 프로세스 간 통신 메커니즘을 통해 운영체제의 중재를 받아야 한다.
반면, 스레드는 하나의 프로세스 내에서 생성되는 실행 흐름의 단위이다. 동일한 프로세스에 속한 모든 스레드는 힙 메모리, 전역 변수, 열린 파일, 신호 처리기 등 프로세스의 자원을 공유한다. 이로 인해 스레드 간 데이터 교환이 매우 빠르고 효율적이다. 그러나 이러한 공유 자원에 대한 동시 접근은 경쟁 조건을 유발할 수 있으므로, 뮤텍스나 세마포어 같은 스레드 동기화 기법을 통해 접근을 제어해야 한다.
다음 표는 프로세스와 스레드의 자원 공유 및 독립성 차이를 요약한다.
특성 | 프로세스 | 스레드 |
|---|---|---|
메모리 공간 | 독립된 주소 공간을 가짐 | 부모 프로세스의 주소 공간을 공유함 |
자원 할당 | 운영체제로부터 독립적으로 자원(메모리, 파일, CPU 시간) 할당받음 | 프로세스가 할당받은 자원을 스레드끼리 공유함 |
통신 비용 | 프로세스 간 통신(IPC)이 필요하며 상대적으로 느림 | 공유 메모리를 통해 직접 통신하므로 매우 빠름 |
오류 영향 | 한 프로세스의 오류가 다른 프로세스에 일반적으로 영향을 미치지 않음 | 한 스레드의 오류(예: 세그멘테이션 폴트)가 전체 프로세스와 다른 모든 스레드를 종료시킬 수 있음 |
생성/전환 오버헤드 | 상대적으로 높음 (새 주소 공간 생성 등) | 상대적으로 낮음 |
이러한 차이점은 설계 선택에 직접적인 영향을 미친다. 높은 격리성과 안정성이 요구되는 작업에는 프로세스가 적합하며, 빠른 데이터 공유와 협력이 중요한 작업, 예를 들어 웹 서버의 요청 처리나 그래픽 사용자 인터페이스의 반응성 유지에는 스레드가 더 효율적인 모델이 된다.
프로세스와 스레드의 성능 차이는 주로 생성 및 관리 오버헤드, 문맥 교환 비용, 그리고 자원 공유 효율성에서 비롯된다. 스레드는 프로세스 내부에 존재하며 코드, 데이터, 힙 영역 등의 자원을 공유하기 때문에, 새로운 스레드를 생성하는 것은 새로운 프로세스를 생성하는 것보다 훨씬 빠르고 메모리 효율이 높다. 반면, 프로세스는 독립된 메모리 공간을 가지므로 생성 시 더 많은 시스템 자원을 할당받아야 하며, 이로 인해 상대적으로 무겁고 느리다.
문맥 교환 측면에서도 스레드가 유리하다. 프로세스 간 문맥 교환은 CPU의 레지스터 상태와 함께 페이지 테이블과 같은 메모리 관리 정보까지 교체해야 하므로 비용이 크다. 이에 비해 같은 프로세스 내의 스레드 간 문맥 교환은 메모리 공간을 공유하므로, 레지스터 상태와 스택 정보만 교체하면 되어 훨씬 빠르게 수행된다[1]. 이 차이는 고빈도로 실행 단위가 전환되어야 하는 환경에서 성능에 결정적 영향을 미친다.
그러나 자원 공유로 인한 성능 이점은 동기화 문제라는 트레이드오프를 동반한다. 여러 스레드가 동일한 데이터에 동시에 접근할 경우, 뮤텍스나 세마포어와 같은 동기화 기법을 사용하지 않으면 데이터 불일치가 발생할 수 있다. 이 동기화 메커니즘 자체도 오버헤드를 유발하며, 잘못 설계될 경우 데드락이나 성능 저하를 초래한다. 반면 프로세스는 기본적으로 메모리가 분리되어 있어, IPC를 사용하지 않는 한 이러한 동기화 문제에서 자유롭다.
다음 표는 주요 성능 및 오버헤드 요소를 비교한 것이다.
비교 요소 | 프로세스 | 스레드 |
|---|---|---|
생성/종료 속도 | 상대적으로 느림 | 상대적으로 빠름 |
문맥 교환 비용 | 높음 (메모리 공간 교체 포함) | 낮음 (레지스터/스택 교체 위주) |
메모리/자원 사용량 | 독립 할당으로 많음 | 공유로 인해 효율적 |
통신 오버헤드 | IPC 필요 (파이프, 소켓 등) | 공유 메모리로 빠름 |
동기화 필요성 | 기본적으로 불필요 | 공유 자원 접근 시 필수 |
하나의 실패 영향 | 다른 프로세스에 영향 적음 | 같은 프로세스 내 모든 스레드에 영향 큼 |
결론적으로, 작업 단위 간에 빈번한 통신과 협력이 필요하고 빠른 전환이 중요한 경우 스레드 모델이 성능상 유리하다. 반면, 높은 안정성과 격리가 필요하거나 독립적인 작업을 수행하는 경우에는 프로세스 모델이 더 적합하다. 현대 운영체제는 멀티코어 환경에서 이 두 모델을 혼합한 멀티스레딩과 멀티프로세싱을 활용하여 성능을 극대화한다.
프로세스 스케줄링은 운영체제가 프로세스에 CPU 자원을 할당하는 순서와 방법을 결정하는 핵심적인 작업이다. 다중 프로그래밍 환경에서 실행 가능한 프로세스의 수는 일반적으로 가용한 CPU 코어의 수보다 많기 때문에, 운영체제는 스케줄러를 통해 어떤 프로세스를 언제 실행할지 결정한다. 이 과정의 주요 목표는 CPU 이용률을 극대화하고, 응답 시간을 최소화하며, 처리량을 높이고, 모든 프로세스에 공정하게 자원을 배분하는 것이다.
스케줄링 알고리즘은 크게 선점형과 비선점형으로 구분된다. 비선점형 스케줄링에서는 프로세스가 CPU를 자발적으로 반납할 때까지(입출력 작업 대기나 종료 시) 실행을 보장한다. 반면, 선점형 스케줄링에서는 타이머 인터럽트나 더 높은 우선순위의 프로세스 도착 등에 의해 실행 중인 프로세스에서 CPU를 강제로 회수할 수 있다. 주요 알고리즘은 다음과 같다.
알고리즘 | 주요 특징 | 장점 | 단점 |
|---|---|---|---|
FCFS(First-Come, First-Served) | 도착 순서대로 처리하는 비선점형 방식. | 구현이 간단하고 공정하다. | 평균 대기 시간이 길어질 수 있는 호위 효과(Convoy Effect)가 발생한다. |
실행 시간이 가장 짧은 프로세스를 먼저 처리. SJF는 비선점형, SRT는 선점형이다. | 평균 대기 시간을 최소화한다. | 실행 시간을 정확히 예측하기 어렵고, 긴 작업이 기아 상태에 빠질 수 있다. | |
라운드 로빈(Round Robin) | 각 프로세스에 시간 할당량(Time Quantum)을 주고 순환하며 실행하는 선점형 방식. | 모든 프로세스에 공정한 CPU 시간을 보장하며, 응답 시간이 빠르다. | 시간 할당량 설정에 성능이 크게 의존한다. 문맥 교환 오버헤드가 빈번할 수 있다. |
우선순위 스케줄링(Priority Scheduling) | 정적 또는 동적으로 부여된 우선순위가 높은 프로세스를 먼저 처리한다. | 중요한 작업을 먼저 처리할 수 있다. | 낮은 우선순위 프로세스의 기아 현상(Starvation)이 발생할 수 있다. |
다단계 큐(Multilevel Queue) | 프로세스를 특성(시스템/대화형/배치)에 따라 여러 큐로 분리하고, 각 큐마다 다른 스케줄링 정책을 적용한다. | 작업의 유형에 맞게 최적화된 스케줄링이 가능하다. | 큐 간의 스케줄링이 추가로 필요하다. |
다단계 피드백 큐(Multilevel Feedback Queue) | 다단계 큐의 확장으로, 프로세스가 큐 사이를 이동할 수 있다. 가장 일반적인 방식이다. | 유연성이 높고, 프로세스의 행동(CPU 버스트 길이)에 따라 동적으로 대응한다. | 구현과 튜닝이 복잡하다. |
스케줄링 결정으로 인해 실행 프로세스가 변경되면 문맥 교환(Context Switch)이 발생한다. 문맥 교환은 현재 실행 중인 프로세스의 상태(PCB에 저장됨)를 보관하고, 새로 실행할 프로세스의 저장된 상태를 복원하는 과정이다. 이 과정에는 CPU 레지스터 저장/복원, 메모리 관리 정보 갱신 등이 포함되며, 순수 오버헤드 시간으로 간주된다. 따라서 스케줄링 알고리즘은 효율적인 자원 활용과 문맥 교환 오버헤드 간의 균형을 고려해야 한다.
프로세스 스케줄링 알고리즘은 준비 큐에 대기 중인 여러 프로세스 중에서 다음에 실행할 프로세스를 선택하는 정책이다. 이는 시스템의 성능과 효율성에 직접적인 영향을 미친다. 주요 평가 기준으로는 CPU 사용률, 처리량, 턴어라운드 타임, 대기 시간, 응답 시간 등이 있다. 알고리즘은 크게 선점형과 비선점형으로 구분된다.
비선점형 스케줄링은 한 프로세스가 CPU를 점유하면 작업이 완료되거나 자발적으로 양도할 때까지 실행을 보장한다. 대표적인 예로 FCFS(First-Come, First-Served)와 SJF(Shortest Job First)가 있다. FCFS는 도착 순서대로 처리하는 간단한 방식이지만, 평균 대기 시간이 길어질 수 있다. SJF는 실행 시간이 가장 짧은 작업을 먼저 실행하여 평균 대기 시간을 최소화하지만, 실행 시간을 정확히 예측하기 어렵다는 단점이 있다.
선점형 스케줄링은 더 높은 우선순위의 프로세스가 도착하면 현재 실행 중인 프로세스를 중단시키고 CPU를 선점할 수 있다. 대표적인 알고리즘은 다음과 같다.
알고리즘 | 주요 특징 | 장점 | 단점 |
|---|---|---|---|
라운드 로빈(RR) | 각 프로세스에 고정된 시간 할당량(타임 슬라이스)을 부여하여 순환 방식으로 실행한다. | 모든 프로세스에 공정하게 CPU 시간을 배분하며, 응답 시간이 빠르다. | 할당량이 너무 크면 FCFS와 유사해지고, 너무 작으면 문맥 교환 오버헤드가 커진다. |
우선순위 스케줄링(Priority) | 각 프로세스에 우선순위를 부여하여 가장 높은 우선순위의 프로세스를 먼저 실행한다. | 중요한 작업을 먼저 처리할 수 있다. | 기아 현상(우선순위가 낮은 프로세스가 무한히 대기)이 발생할 수 있다. |
다단계 큐(MQ) | 프로세스를 특성에 따라 여러 개의 큐로 분류하고, 각 큐마다 다른 스케줄링 알고리즘을 적용한다. | 시스템(대화형) 프로세스와 배치 프로세스를 구분하여 효율적으로 관리할 수 있다. | 큐 간의 스케줄링이 추가로 필요하다. |
다단계 피드백 큐(MFQ) | 다단계 큐의 확장으로, 프로세스가 큐 사이를 이동할 수 있다. CPU 시간을 많이 사용하는 프로세스는 낮은 우선순위 큐로 이동시킨다. | 기아 현상을 완화하고 유연한 스케줄링이 가능하다. | 구현이 복잡하다. |
현대 운영체제는 대부분 선점형 방식을 기반으로 하며, 실제로는 다단계 피드백 큐와 같은 복합적인 알고리즘을 사용하여 다양한 작업 부하에 대응한다.
문맥 교환은 운영체제가 현재 실행 중인 프로세스의 상태를 저장하고, 다른 프로세스의 상태를 복원하여 CPU 제어권을 넘겨주는 과정이다. 이 과정에서 실행이 중단되는 프로세스의 문맥은 그 프로세스의 프로세스 제어 블록(PCB)에 저장된다. 문맥에는 프로그램 카운터, 레지스터 값, 메모리 관리 정보, 열린 파일 디스크립터 등 프로세스를 재개하는 데 필요한 모든 정보가 포함된다.
문맥 교환은 선점형 스케줄링이 사용되는 시스템에서 주로 발생한다. 스케줄러가 타임 슬라이스가 만료되었거나 더 높은 우선순위의 프로세스가 준비되었을 때 현재 프로세스를 중단시키고 다른 프로세스로 전환할 것을 결정하면 문맥 교환이 시작된다. 또한, 프로세스가 입출력 작업을 위해 자발적으로 CPU를 양도할 때도 발생할 수 있다.
문맥 교환은 순수한 오버헤드 시간이다. 이 시간 동안 CPU는 유용한 작업을 수행하지 않고 운영체제 커널의 문맥 교환 루틴만 실행한다. 따라서 문맥 교환 속도는 시스템 전체 성능에 영향을 미치는 중요한 요소이다. 일반적인 문맥 교환 비용은 다음과 같은 작업들로 구성된다.
비용 요소 | 설명 |
|---|---|
레지스터 저장/복원 | 현재 프로세스의 레지스터 값을 PCB에 저장하고, 다음 프로세스의 레지스터 값을 PCB에서 복원한다. |
캐시 및 TLB 무효화 | |
커널 모드 진입/복귀 | 문맥 교환은 커널 모드에서 수행되므로 모드 전환에 따른 오버헤드가 발생한다. |
스케줄러 실행 | 다음으로 실행할 프로세스를 결정하기 위해 스케줄링 알고리즘을 실행하는 시간이 포함된다. |
문맥 교환의 빈도는 시스템의 응답성과 처리량 사이의 트레이드오프 관계에 있다. 너무 빈번한 문맥 교환은 오버헤드를 증가시켜 처리량을 떨어뜨린다. 반면, 교환이 너무 드물면 대화형 프로세스의 응답 시간이 나빠질 수 있다. 현대 운영체제는 하드웨어 지원(예: 빠른 레지스터 세트 저장 명령어)과 효율적인 커널 설계를 통해 문맥 교환 오버헤드를 최소화하려고 노력한다.
프로세스 간 통신(IPC)은 독립된 주소 공간을 가진 프로세스들이 데이터를 교환하거나 동작을 조율하기 위한 메커니즘이다. 주요 방법으로는 공유 메모리, 메시지 전달, 파이프, 소켓, 원격 프로세스 호출(RPC) 등이 있다. 공유 메모리는 공통의 메모리 영역을 매핑하여 가장 빠른 속도를 제공하지만, 개발자가 직접 동기화를 관리해야 한다. 반면 메시지 전달은 커널을 통해 메시지를 주고받아 통신의 부담은 크지만, 구현이 비교적 간단하고 동기화가 내재되어 있다.
스레드 동기화는 같은 프로세스 내의 여러 스레드가 공유 자원(예: 전역 변수, 힙 메모리)에 안전하게 접근하도록 보장하는 기법이다. 주요 동기화 도구로는 뮤텍스, 세마포어, 모니터, 조건 변수 등이 있다. 뮤텍스는 한 번에 하나의 스레드만이 임계 구역에 진입할 수 있도록 하는 상호 배제 잠금 장치이다. 세마포어는 정수 값과 대기 큐를 사용하여 자원의 개수에 따라 여러 스레드의 접근을 허용하거나 제한할 수 있는 더 일반화된 메커니즘이다.
통신/동기화 유형 | 주요 특징 | 사용 사례 |
|---|---|---|
공유 메모리(IPC) | 고속 통신 가능, 명시적 동기화 필요 | 대용량 데이터 처리, 성능이 중요한 애플리케이션 |
메시지 전달(IPC) | 커널을 통한 통신, 상대적으로 느림 | 클라이언트-서버 모델, 분산 시스템 |
뮤텍스(스레드) | 상호 배제, 잠금/해제 오버헤드 작음 | 공유 변수나 자료구조 보호 |
세마포어(스레드) | 카운팅 가능, 신호 메커니즘 제공 | 유한한 개수의 자원(예: 연결 풀) 관리 |
이러한 동기화 기법을 사용하지 않으면 경쟁 조건(Race Condition)이 발생하여 데이터의 불일치나 프로그램의 비정상 종료 같은 문제가 생길 수 있다. 따라서 멀티스레드 프로그래밍에서는 원자성, 가시성, 순서화를 보장하는 것이 필수적이다.
프로세스 간 통신(IPC, Inter-Process Communication)은 독립된 주소 공간을 가진 프로세스들이 데이터를 교환하거나 동작을 조율하기 위해 사용하는 메커니즘이다. 각 프로세스는 일반적으로 다른 프로세스의 메모리에 직접 접근할 수 없기 때문에, 운영체제가 제공하는 특별한 통신 채널이 필요하다. IPC는 공유 메모리, 메시지 전달, 파이프, 소켓 등 다양한 방법으로 구현된다.
주요 IPC 기법은 크게 공유 메모리 방식과 메시지 전달 방식으로 구분된다. 공유 메모리는 여러 프로세스가 접근할 수 있는 메모리 영역을 운영체제가 설정해 주는 방식이다. 프로세스들은 이 영역을 통해 직접 데이터를 읽고 쓸 수 있어 속도가 매우 빠르다는 장점이 있다. 그러나 공유 자원에 대한 접근을 관리하지 않으면 경쟁 조건이나 데이터 불일치 문제가 발생할 수 있으므로, 세마포어나 뮤텍스 같은 동기화 기법이 반드시 필요하다. 반면 메시지 전달 방식은 프로세스 간에 메시지를 주고받는 통로를 만들어 데이터를 교환한다. 파이프, 메시지 큐, 소켓 등이 이에 해당한다. 이 방식은 커널을 통해 메시지를 복사하여 전달하기 때문에 공유 메모리보다 오버헤드가 크지만, 구현이 비교적 간단하고 동기화 문제를 내부적으로 처리해 준다는 장점이 있다.
다양한 IPC 방법의 특징을 비교하면 다음과 같다.
방법 | 통신 유형 | 주요 특징 |
|---|---|---|
공유 자원 | 가장 빠른 속도, 명시적 동기화 필요 | |
파이프 (익명) | 메시지 전달 | 단방향, 부모-자식 프로세스 간 통신에 사용 |
명명된 파이프(FIFO) | 메시지 전달 | 양방향, 무관계 프로세스 간 통신 가능 |
메시지 전달 | 구조화된 메시지, 우선순위 설정 가능 | |
메시지 전달 | 네트워크를 통한 통신 가능, 가장 범용적 |
IPC 기법의 선택은 통신하는 프로세스의 관계, 필요한 통신 속도, 데이터의 양, 운영체제의 지원 여부 등에 따라 결정된다. 예를 들어, 고성능 컴퓨팅에서는 공유 메모리가 선호되는 반면, 클라이언트-서버 모델에서는 소켓이나 원격 프로시저 호출(RPC)이 널리 사용된다. 모든 IPC 메커니즘은 궁극적으로 프로세스 간의 협력적 작업을 가능하게 하여, 복잡한 소프트웨어 시스템을 구성하는 데 필수적인 역할을 한다.
스레드 동기화는 둘 이상의 스레드가 공유 자원에 접근할 때 발생할 수 있는 경쟁 상태를 방지하고, 실행 순서를 조정하여 프로그램의 정확성을 보장하는 기법이다. 주요 동기화 기법으로는 뮤텍스, 세마포어, 모니터, 조건 변수 등이 있다.
뮤텍스는 상호 배제를 제공하는 가장 기본적인 동기화 객체이다. 한 번에 하나의 스레드만이 뮤텍스를 소유하여 임계 구역에 진입할 수 있다. 다른 스레드는 소유 스레드가 뮤텍스를 해제할 때까지 대기 상태에 머문다. 세마포어는 정수형 변수와 대기 큐를 사용하며, 동시에 여러 개의 스레드가 자원에 접근할 수 있는 허용 개수를 지정할 수 있다. 이진 세마포어는 값이 0 또는 1이므로 뮤텍스와 유사하게 동작한다.
모니터는 공유 자원과 그 자원에 접근하기 위한 연산을 하나의 모듈로 묶은 고수준 동기화 구조이다. 모니터 내부에는 한 번에 하나의 스레드만 실행될 수 있도록 상호 배제가 내장되어 있다. 조건 변수는 주로 모니터와 함께 사용되며, 특정 조건이 만족될 때까지 스레드를 대기시키거나 대기 중인 스레드에게 조건이 충족되었음을 알리는 데 사용된다. 또한 스핀락은 뮤텍스와 유사하지만, 스레드가 락을 획득할 때까지 반복문을 실행하며 대기하는 바쁜 대기 방식을 사용한다. 문맥 교환 오버헤드가 적은 매우 짧은 대기 시간에 유리하지만, CPU 자원을 소모한다는 단점이 있다.
기법 | 주요 특징 | 사용 사례 |
|---|---|---|
상호 배제, 소유권 개념, 소유 스레드만 해제 가능 | 공유 변수, 연결 리스트, 전역 데이터 보호 | |
카운팅 가능, 신호 메커니즘, 소유권 없음 | 유한한 개수의 자원(예: 버퍼 풀, 연결 풀) 관리 | |
상호 배제 자동화, 연산과 데이터 캡슐화 | 고수준 언어(예: Java의 | |
조건 기반 대기/알림, 모니터와 함께 사용 | 생산자-소비자 문제, 특정 상태 대기 | |
바쁜 대기 방식, 문맥 교환 오버헤드 적음 | 멀티코어 환경에서 매우 짧은 락 대기 시간 |
이러한 기법들을 선택할 때는 데드락, 기아 상태, 우선순위 역전과 같은 문제를 고려해야 한다. 올바른 동기화는 데드락을 방지하면서도 병렬 실행의 이점을 최대화하는 핵심 요소이다.
멀티프로세싱은 하나의 응용 프로그램이 여러 개의 프로세스를 생성하여 동시에 작업을 수행하는 모델이다. 각 프로세스는 독립된 메모리 공간과 자원을 할당받아 실행되므로, 한 프로세스의 오류가 다른 프로세스에 직접적인 영향을 미치지 않는다는 장점이 있다. 그러나 프로세스 간 통신에는 IPC와 같은 별도의 메커니즘이 필요하며, 문맥 교환으로 인한 오버헤드가 상대적으로 크다. 이 모델은 안정성과 격리성이 중요한 시스템에서 주로 사용된다.
반면, 멀티스레딩은 하나의 프로세스 내에서 여러 개의 스레드를 생성하여 동시성을 구현하는 모델이다. 모든 스레드는 동일한 프로세스의 코드, 데이터, 힙 영역을 공유하므로, 자원 공유와 통신이 매우 효율적이다. 스레드 간의 문맥 교환은 프로세스 간 교환보다 훨씬 빠르게 이루어진다. 그러나 한 스레드의 오류가 전체 프로세스를 중단시킬 수 있으며, 공유 자원에 대한 접근은 반드시 동기화 기법으로 제어해야 한다.
아키텍처별 구현 방식은 다음과 같이 구분할 수 있다.
아키텍처 | 설명 | 주요 특징 |
|---|---|---|
대칭형 멀티프로세싱(SMP) | 여러 프로세서가 단일 메모리와 시스템 버스를 공유하며, 모든 프로세서가 대등한 관계를 유지한다. | 작업 부하를 프로세서들에 균등하게 분배할 수 있으며, 하나의 프로세서가 고장나도 시스템이 정상 작동할 수 있다. |
비대칭형 멀티프로세싱(AMP) | 하나의 마스터 프로세서가 시스템을 통제하고, 나머지 슬레이브 프로세서들은 특정 작업만을 수행한다. | 프로세서 간 역할이 명확히 구분되어 있어 설계가 비교적 단순하지만, 마스터 프로세서에 병목 현상이 발생할 수 있다. |
동시성 프로그래밍 모델은 멀티프로세싱과 멀티스레딩을 기반으로 발전했다. 액터 모델은 메시지 패싱을 통해 격리된 액터들이 비동기적으로 통신하는 방식으로, 주로 멀티프로세싱 환경에 적합하다. 반면, 공유 메모리 모델은 락이나 세마포어 같은 동기화 도구를 사용하여 스레드들이 공유 데이터에 안전하게 접근하는 방식으로, 멀티스레딩 환경의 핵심이다. 최근에는 고루틴이나 async/await와 같은 더 경량화된 동시성 추상화 모델도 널리 사용된다.
멀티프로세싱과 멀티스레딩은 하드웨어와 운영체제의 지원 방식에 따라 다양한 형태로 구현된다. 대칭형 멀티프로세싱(SMP)은 가장 일반적인 형태로, 두 개 이상의 동일한 프로세서가 단일 메인 메모리를 공유하며 모든 프로세서가 대등하게 작업을 수행할 수 있다. 반면, 비대칭형 멀티프로세싱(ASMP)에서는 하나의 마스터 프로세서가 시스템을 제어하고 작업을 할당하며, 나머지 프로세서는 할당받은 특정 작업만 수행하는 구조를 가진다.
멀티스레딩 구현은 하드웨어 수준과 소프트웨어 수준으로 구분된다. 하드웨어 멀티스레딩은 단일 프로세서 코어가 여러 개의 스레드 상태(context)를 유지하고, 한 스레드가 대기 상태일 때 다른 스레드로 빠르게 전환하여 처리율을 높이는 기술이다. 이는 다시 세부적으로 구분된다.
구현 방식 | 설명 | 주요 특징 |
|---|---|---|
세밀한 멀티스레딩 (Fine-grained) | 매 클록 사이클마다 다른 스레드로 전환한다. | 스레드 전환 오버헤드가 매우 낮지만, 개별 스레드의 처리 속도는 느려질 수 있다. |
굵은 멀티스레딩 (Coarse-grained) | 하나의 스레드가 긴 지연(예: 캐시 미스)을 만날 때만 다른 스레드로 전환한다. | 세밀한 방식보다 전환 빈도가 낮아 구현이 상대적으로 단순하다. |
동시 멀티스레딩 (SMT) | 단일 프로로세서 코어가 여러 스레드의 명령어를 동시에 실행할 수 있도록 한다. |
소프트웨어적 멀티스레딩은 운영체제의 스케줄러가 여러 스레드를 관리하는 방식으로 구현되며, 사용자 수준 스레드 라이브러리(예: POSIX 스레드(pthreads))를 통해 제공된다. 최근에는 멀티코어 프로세서가 보편화되면서, 물리적 코어 각각에서 SMT와 같은 하드웨어 스레딩 기술을 적용한 하이브리드 형태가 주류를 이루고 있다. 이러한 아키텍처에서는 운영체제가 논리적 프로세서를 모두 인식하고 효율적으로 스레드를 배분한다.
동시성 프로그래밍 모델은 멀티스레딩을 구현하는 소프트웨어적 접근 방식을 정의한다. 주요 모델로는 공유 메모리 모델과 메시지 전달 모델이 있으며, 각각 프로세스 또는 스레드 간 데이터를 교환하고 조율하는 방식이 근본적으로 다르다.
공유 메모리 모델은 여러 스레드가 하나의 공통된 메모리 공간에 접근하여 데이터를 읽고 쓰는 방식이다. 이 모델은 자원 공유가 용이하고 속도가 빠르다는 장점이 있지만, 경쟁 조건이나 데이터 불일치와 같은 문제가 발생할 수 있어 뮤텍스, 세마포어, 모니터와 같은 동기화 기법을 필수적으로 사용해야 한다. 자바, C++, 파이썬의 표준 스레드 라이브러리가 이 모델을 기반으로 한다.
반면, 메시지 전달 모델은 프로세스나 스레드가 각자 독립된 메모리 공간을 가지며, 명시적으로 메시지를 주고받으며 통신한다. 이 방식은 메모리 공간이 분리되어 있어 동기화 문제가 상대적으로 적지만, 메시지 전송에 따른 오버헤드가 존재한다. 에르랑 언어의 액터 모델이나 MPI 라이브러리가 이 패러다임의 대표적 예시이다.
모델 | 통신 방식 | 주요 특징 | 대표적 구현/언어 |
|---|---|---|---|
공유 변수를 통한 읽기/쓰기 | 높은 성능, 동기화 필요 | ||
명시적 메시지 송수신 | 메모리 격리, 오버헤드 존재 |
이러한 모델의 선택은 프로그래밍 언어, 대상 시스템 아키텍처, 그리고 개발하는 응용 프로그램의 복잡성과 안정성 요구사항에 따라 결정된다.
리눅스 커널은 전통적으로 프로세스를 기본 실행 단위로 취급한다. 그러나 스레드 역시 내부적으로는 다른 프로세스와 거의 동일한 구조인 태스크 구조체(task_struct)로 표현된다. 리눅스의 스레드는 clone() 시스템 호출을 통해 생성되며, 이때 메모리 주소 공간, 파일 디스크립터 테이블, 시그널 핸들러 등의 자원을 부모 프로세스와 공유할지 여부를 플래그로 지정한다. 따라서 리눅스에서는 스레드를 '가벼운 프로세스'(LWP)로 구현하며, 모든 스레드는 고유한 PID를 가진다. 반면 마이크로소프트 윈도우는 커널 수준에서 프로세스와 스레드를 명확히 구분한다. 윈도우의 프로세스 객체는 자원을 보호하는 컨테이너 역할을 하고, 실제 실행 단위는 독립적인 스레드 객체가 담당한다. 각 스레드는 자신이 속한 프로세스의 가상 주소 공간을 공유한다.
프로그래밍 언어 수준에서는 스레드 생성과 관리에 대한 추상화를 제공한다. 자바는 java.lang.Thread 클래스와 Runnable 인터페이스를 통해 플랫폼 독립적인 멀티스레딩을 지원하며, JVM이 운영체제의 실제 스레드와 매핑을 관리한다. C++11 표준부터는 <thread> 라이브러리가 언어에 내장되어 이식성 있는 스레드 API를 제공한다. 파이썬의 경우 GIL(전역 인터프리터 락)의 존재로 인해 CPU 바운드 작업에서의 멀티스레딩 성능에 한계가 있지만, threading 모듈을 통해 스레드를 생성하고 관리할 수 있다. I/O 바운드 작업이나 병렬 처리를 위해서는 multiprocessing 모듈을 사용해 프로세스를 생성하거나, asyncio를 이용한 비동기 프로그래밍 모델을 활용한다.
구현 환경 | 프로세스/스레드 모델 | 주요 특징 |
|---|---|---|
가벼운 프로세스(LWP) 모델 | 스레드도 | |
명시적 프로세스-스레드 분리 모델 | 프로세스는 자원 컨테이너, 스레드는 실행 단위 | |
자바(JVM) | 플랫폼 독립적인 스레드 모델 |
|
C++11 이상 | 표준 라이브러리 기반 스레드 |
|
파이썬(CPython) | GIL 제약 하의 스레드 모델 |
|
리눅스 커널은 프로세스와 스레드를 모두 태스크 구조체(task_struct)라는 동일한 내부 자료 구조로 관리한다. 전통적인 의미의 프로세스는 단일 스레드를 가진 태스크에 해당하며, 멀티스레드 프로세스는 같은 프로세스 식별자(PID)와 메모리 주소 공간을 공유하는 여러 개의 태스크로 구현된다. 스레드 생성은 clone() 시스템 호출을 통해 이루어지며, 이때 공유할 자원(주소 공간, 파일 디스크립터 테이블 등)의 정도를 플래그로 세밀하게 제어할 수 있다. 사용자 공간에서는 POSIX 스레드(pthreads) 라이브러리가 이 시스템 호출을 활용하여 스레딩 기능을 제공한다.
반면, 마이크로소프트 윈도우는 프로세스와 스레드를 명확히 구분된 객체로 취급한다. 윈도우 API에서 프로세스는 자원의 컨테이너 역할을 하는 반면, 스레드는 실제 실행 단위이다. 새로운 프로세스는 CreateProcess() 함수로 생성되며, 이 프로세스 내에서 CreateThread() 함수를 호출하여 스레드를 추가한다. 각 프로세스 객체는 독립된 가상 주소 공간과 핸들 테이블을 가지지만, 한 프로세스에 속한 모든 스레드는 이 자원들을 공유한다. 윈도우의 스레드는 유저 모드 스케줄링(UMS)과 같은 고급 기능도 지원한다.
두 운영체제의 구현 차이는 성능과 유연성에 영향을 미친다. 리눅스의 통합 구조체 방식은 경량화된 스레드 생성과 빠른 문맥 교환을 가능하게 하지만, 스레드와 프로세스의 구분이 추상화 수준에서만 존재한다. 윈도우의 명시적 분리는 객체 관리의 명확성을 높이고, 작업 관리자와 같은 시스템 도구에서 프로세스와 스레드를 별도로 모니터링하기 용이하게 한다. 두 방식 모두 현대적인 멀티코어 프로세서 환경에서 효율적인 멀티태스킹과 병렬 처리를 지원하는 것을 목표로 한다.
주요 프로그래밍 언어들은 운영체제가 제공하는 프로세스와 스레드 생성 API를 추상화하여 각자의 동시성 프로그래밍 모델을 제공한다. C와 C++는 POSIX 스레드(pthreads)나 윈도우 API를 직접 호출하는 저수준 접근 방식을 취하며, 개발자가 메모리 관리와 동기화를 세밀하게 제어할 수 있게 한다. 반면, 자바는 언어 차원에서 java.lang.Thread 클래스와 java.util.concurrent 패키지를 제공하여 플랫폼 독립적인 멀티스레딩을 지원한다. 자바의 스레드는 JVM에 의해 관리되는 사용자 스레드이면서, 대부분의 현대 JVM 구현체에서는 커널 스레드와 1:1로 매핑된다.
파이썬은 GIL(Global Interpreter Lock)의 존재로 인해 단일 프로세스 내에서 진정한 의미의 병렬 스레드 실행이 제한된다. 따라서 CPU 바운드 작업의 병렬화에는 multiprocessing 모듈을 이용한 멀티프로세싱이 권장된다. 한편, 고루틴과 이벤트 루프를 기반으로 하는 자바스크립트와 Node.js는 단일 스레드 모델을 사용하며, 비동기 I/O를 통해 높은 동시성을 달성한다. 고(Go) 언어는 '고루틴'이라는 경량 스레드 개념을 도입했는데, 이는 언어 런타임에 의해 관리되며 수천 개의 고루틴을 효율적으로 멀티플렉싱한다.
언어 | 주요 동시성 단위 | 실행 모델 | 특징 |
|---|---|---|---|
스레드 (pthreads, Win32 Thread) | 주로 1:1 (커널 스레드) | 운영체제 API 직접 호출, 높은 제어권 | |
| 1:1 (커널 스레드) | JVM에 의한 관리, 플랫폼 독립성 | |
| GIL 제한 (스레드), 다중 프로세스 | CPU 병렬화는 멀티프로세싱으로 | |
자바스크립트 (Node.js) | 단일 스레드 | 비동기 I/O, 논블로킹 연산 | |
M:N (다대다) | 언어 런타임 스케줄러, 채널을 통한 통신 |
최근 언어들은 안전하고 생산적인 동시성 처리를 강조하는 추세이다. 러스트는 소유권과 빌림 시스템을 통해 데이터 레이스와 같은 메모리 오류를 컴파일 타임에 방지하며 스레드 안전성을 보장한다. 이러한 다양한 접근 방식은 애플리케이션의 요구사항(연산 집약적, I/O 집약적), 개발 편의성, 성능 특성에 따라 선택의 기준이 된다.