
개요
실행 중인 프로그램을 프로세스(Process)라고 하며, 프로세스를 구성하는 실행 단위를 스레드(Thread)라고 합니다.
메모리에는 여러 프로세스가 적재될 수 있으며, 운영체제는 각 프로세스에 필요한 자원을 할당합니다.
스레드는 프로세스가 할당받은 자원을 활용하여 작업을 수행하며, 하나의 프로세스가 여러 개의 스레드를 포함할 수 있습니다. 프로세스를 이루는 스레드가 둘 이상인 경우에는 동일한 작업을 동시에 실행할 수도 있습니다.
운영체제는 여러 프로세스와 스레드가 동시에 실행될 때, 실행 순서를 적절히 제어하고 필요한 자원을 효율적으로 배분함으로써 시스템이 원활하게 동작하도록 관리합니다.
프로세스
프로세스의 유형으로는 포그라운드 프로세스, 백그라운드 프로세스, 데몬 등이 있습니다.
- 포그라운드 프로세스(Foreground Process): 사용자가 보는 화면에서 실행되며, 사용자와 상호작용하는 프로세스
- 백그라운드 프로세스(Background Process): 사용자에게 보이지 않는 곳에서 실행되는 프로세스
- 데몬(Daemon): 백그라운드에서 실행되며, 사용자와 별다른 상호작용 없이 특정 작업만 수행하는 프로세스
포그라운드는 예를 들어 터미널에서 사용자가 명령어를 입력하면 셸이 명령어를 해석, 실행, 결과 출력까지 기다린 다음 사용자가 다음 명령어를 입력하는 대화식으로 작업을 수행합니다.
반면, 백그라운드는 터미널에서 사용자가 명령어를 입력한 뒤, 바로 다른 명령어를 입력해 작업을 수행할 수 있습니다.
데몬은 위 두 프로세스와 달리 사용자가 직접 제어하지 않고(터미널을 갖지 않음), 백그라운드에서 돌면서 여러 작업을 실행한다.
이러한 프로세스의 유형을 막론하고 하나의 프로세스를 구성하는 메모리 내의 정보는 크게 다르지 않습니다.
운영체제의 커널 영역에는 프로세스 제어 블록(PCB, Process Control Block)이 저장되며, 사용자 영역에는 실행 중인 프로세스가 다음 5가지 영역으로 나뉘어 저장됩니다.
- 코드(Code) 영역
- 실행 가능한 명령어가 저장되는 공간
- CPU가 읽고 실행할 명령어가 있으므로, 읽기 전용(read-only) 공간으로 설정됨
- 데이터(Data) 영역
- 프로그램 실행 중 유지해야 하는 데이터를 저장하는 공간
- 정적 변수, 전역 변수 등이 포함됨
- BSS(Block Started by Symbol) 영역
- 초기값이 없는 전역 변수 및 정적 변수를 저장하는 공간
- 초기값이 있는 변수는 데이터 영역에 저장됨
- 힙(Heap) 영역
- 실행 도중 동적으로 할당 가능한 메모리 공간
- 필요에 따라 메모리를 할당하고 반환해야 하며, 메모리 공간을 반환하지 않으면 할당한 공간이 계속 메모리 내에 남아 메모리를 낭비하는 메모리 누수(memory leak)가 발생할 수 있음
- Java에서는 가비지 컬렉션(Garbage Collection) 기능을 통해 불필요한 메모리를 자동으로 회수
- 스택(Stack) 영역
- 일시적으로 사용되는 데이터를 저장하는 공간
- 함수의 실행이 끝나면 사라지는 매개변수, 지역 변수, 함수 복귀 주소 등이 포함됨
- 스택 트레이스(Stack Trace)란 특정 시점의 함수 호출 정보를 저장하여 디버깅 시 유용하게 활용됨
스택 트레이스 예시
NullPointerException이 발생했습니다.
java.lang.NullPointerException
at com.example.Example.myMethod(Example.java:13)
at com.example.Example.main(Example.java:6)
코드 영역과 데이터 영역은 프로그램 실행 도중 크기가 변하지 않기 때문에 정적 할당 영역이라고 하고, 힙, 스택 영역은 크기가 변할 수 있기 때문에 동적 할당 영역이라고 합니다.
프로세스 제어 블록(PCB)
운영체제가 메모리에 적재된 여러 프로세스를 관리하려면, 각 프로세스를 식별할 수 있는 정보가 필요합니다.
이 정보를 담고 있는 것이 프로세스 제어 블록(PCB, Process Control Block)입니다.
PCB는 프로세스와 관련된 다양한 정보를 포함하는 구조체로, 새로운 프로세스가 생성되면 커널 영역에 생성되고, 프로세스 실행이 종료되면 삭제된다.
PCB에 담기는 주요 정보
- 프로세스 ID(PID): 프로세스를 식별하는 고유한 번호
- 프로세스 상태: 실행 중, 대기 중, 종료 등 현재 상태 정보
- 레지스터 값: 실행 과정에서 사용한 레지스터 값
- CPU 스케줄링 정보: 프로세스가 언제, 어떤 순서로 CPU를 할당받는지 나타내는 정보
- 메모리 관련 정보: 프로세스의 메모리상 적재 위치 정보
- 파일 및 입출력 장치 정보: 프로세스가 사용한 파일과 입출력 장치 정보
여러 PCB들은 커널 내에 프로세스 테이블의 형태로 관리됩니다.
프로세스가 비정상 종료되어 사용한 자원이 회수되었음에도 불구하고, PCB가 프로세스 테이블에 남아 있는 비정상 종료 상태를 좀비 프로세스(zombie process)라고 한다.
문맥 교환(Context Switching)
운영체제는 여러 프로세스가 번갈아 가며 실행되도록 관리합니다.
프로세스가 실행된다는 것은 CPU의 자원을 할당받았다는 것을 의미한다.
하지만 프로세스의 CPU 사용 시간은 무제한이 아니며, 일정한 시간 동안만 사용하도록 제한됩니다.
타이머 인터럽트(timer interrupt)가 발생하면, 현재 실행 중인 프로세스는 CPU를 양보하고 다음 차례를 기다려야 합니다.
타이머 인터럽트는 시간이 끝났음을 알리는 인터럽트로, 타임아웃 인터럽트(timeout interrupt)라고도 불립니다.
프로세스 A가 실행 중일 때 타이머 인터럽트가 발생하면, 운영체제는 프로세스 A의 프로그램 카운터(PC)를 비롯해 각족 레지스터 값과 메모리 정보, 실행을 위해 열었던 파일, 사용한 입출력장치 등 지금까지의 중간 정보를 백업해야 합니다.
이렇게 저장된 정보를 바탕으로, 나중에 프로세스 A가 다시 실행될 때 이전 상태를 그대로 복원할 수 있습니다.
이때 백업 대상이 되는 중간 정보, 즉 프로세스의 수행을 재개하기 위해 기억해야 하는 정보를 문맥(context)라고 합니다.
운영체제는 프로세스가 CPU를 사용할 시간이 종료되거나 인터럽트가 발생하면 해당 프로세스의 문맥을 PCB에 저장하고, 이어서 다음 실행할 프로세스의 문맥을 PCB에서 복원합니다.
이처럼 기존 프로세스의 문맥을 PCB에 백업하고, PCB에서 문맥을 복구하여 새로운 프로세스를 실행하는 것을 문맥 교환(context switching)이라고 합니다. 문맥 교환은 여러 프로세스가 끊임없이 빠르게 번갈아 가며 실행됩니다.
하지만 문맥 교환이 너무 자주 발생하면, CPU 캐시 미스로 인해 메모리 접근이 잦아지고, 이는 성능 저하(오버헤드)로 이어질 수 있습니다.
프로세스 상태
하나의 프로세스는 여러 상태를 거치며 실행됩니다.
운영체제는 PCB를 통해 프로세스의 상태를 인식하고, 관리합니다.
- 생성 상태(new): 프로세스가 생성 중인 상태로, 메모리에 적재되고 PCB가 할당된 상태. 생성이 완료된 후 실행 준비가 끝나면 준비 상태로 전환되어 CPU 할당을 기다림.
- 준비 상태(Ready): 프로세스가 실행 가능한 상태지만, 아직 CPU를 할당받지 못해 대기 중인 상태. CPU를 할당받으면 실행 상태가 되며, 이를 디스패치(dispatch)라고 함.
- 실행 상태(Running): CPU를 할당받아 실행 중인 상태로, 일정 시간이 지나거나(타이머 인터럽트) 실행 도중 입출력 장치를 사용해야 하는 경우 상태가 변경됨.
- 타이머 인터럽트 발생 시 → 준비 상태로 전환
- 입출력 또는 이벤트 대기 시 → 대기 상태로 전환
- 대기 상태(Blocked): 프로세스가 입출력 작업을 요청하거나 바로 확보할 수 없는 자원을 요청하는 등 곧장 실행이 불가능한 조건에 놓인 상태. 입출력 작업을 요청하는 경우가 대표적. 입출력 작업이 완료되면 다시 준비 상태로 전환되어 CPU 할당을 기다림.
- 종료 상태(Terminated): 프로세스가 실행을 마치고 종료된 상태. 운영체제는 프로세스의 PCB를 삭제하고, 사용했던 메모리를 정리.
멀티프로세스(Multi-Process)
대표적으로 웹 브라우저가 있습니다.
하나의 탭마다 하나의 프로세스로 동작합니다. 이처럼 동시에 여러 프로세스가 실행되는 것을 멀티프로세스(multi-process)라고 합니다.
각 프로세스는 자원을 공유하지 않고 독립적으로 실행됩니다.
동일한 작업을 수행하더라도 프로세스마다 고유한 PID값이 있으며, 각 프로세스는 개별적인 파일, 입출력 장치 등의 자원을 할당받습니다. 이로 인해 한 프로세스에서 문제가 발생하더라도 다른 프로세스에는 직접적인 영향을 미치지 않는 경우가 많습니다.
멀티스레드(Multi-Thread)
한 프로세스 내에서 여러 코드 실행 흐름을 동시에 처리하는 방법으로는 멀티스레드(multi-thread)가 있습니다.
멀티스레드는 하나의 프로세스를 구성하는 여러 스레드가 동시에 실행되는 방식을 의미합니다. 각 스레드는 고유한 스레드 ID, 프로그램 카운터, 레지스터 값, 스택을 갖습니다. 스레드마다 자신만의 프로그램 카운터와 스택이 있어 스레드마다 다음에 실행할 주소를 가질 수 있고, 연산 과정에서 필요한 임시 데이터를 저장할 수도 있습니다.
멀티스레드 vs 멀티프로세스
멀티스레드와 멀티프로세스의 가장 큰 차이는 자원의 공유 여부입니다.
프로세스는 자원을 공유하지 않고 독립적으로 실행되지만, 같은 프로세스를 실행하는 여러 스레드들은 자원을 공유합니다. 스레드는 동일한 주소 공간의 코드, 데이터, 힙 영역을 공유하고, 열린 파일과 같은 프로세스의 자원을 공유하기 때문에 쉽게 협력하고 통신할 수 있습니다.
다만, 멀티프로세스 환경에서는 한 프로세스에 문제가 생겨도 다른 프로세스에는 지장이 없거나 적지만, 멀티스레드 환경에서는 한 스레드에서 문제가 발생하면 프로세스 전체에 영향을 줄 수 있습니다.
프로세스 간 통신(IPC)
프로세스는 기본적으로 자원을 공유하지 않지만, 프로세스 간에도 데이터를 주고받거나 자원을 공유하는 방법이 있습니다. 이를 프로세스 간 통신(IPC, Inter-Process Communication)라고 합니다.
IPC 방식은 크게 두 가지로 나뉩니다.
- 공유 메모리(Shared Memory)
- 메시지 전달(Message Passing)
공유 메모리(Shared Memory)
공유 메모리는 여러 프로세스가 함께 접근할 수 있는 메모리 영역을 두고 데이터를 주고받는 방식입니다.
각 프로세스는 마치 자신의 메모리를 읽고 쓰는 것처럼 동작하며, 커널의 개입이 적어 통신 속도가 빠릅니다.
하지만 여러 프로세스가 동시에 공유 메모리를 읽고 쓰는 경우 데이터 일관성이 깨질 수 있습니다.
이를 레이스 컨디션(Race Condition)라고 합니다. 이를 방지하기 위해 뮤텍스(Mutex), 세마포어(Semaphore)와 같은 동기화 기법을 사용합니다.
메시지 전달(Message Passing)
메시지 전달은 커널을 통해 프로세스 간 데이터를 메시지 형태로 주고받는 방식입니다.
- 메시지를 보내는
send()
- 메시지를 받는
recv()
이러한 시스템 콜을 사용하여 프로세스 간 데이터를 주고받습니다. 커널이 메시지를 중재하기 때문에 동기화 문제를 신경 쓸 필요가 적지만, 공유 메모리보다 속도가 느립니다.
대표적인 메시지 전달 방식
메시지 전달 기반 IPC를 위한 대표적인 수단으로는 파이프, 시그널, 소켓, 원격 프로시저 호출(RPC) 등이 있습니다.
1) 파이프(Pipe)
단방향(One-Way) 프로세스 간 통신을 위한 도구로, 한 프로세스가 파이프의 한쪽에서 데이터를 쓰면, 다른 프로세스가 반대쪽에서 데이터를 읽습니다.
양방향 통신이 필요할 경우 두 개의 파이프(읽기용, 쓰기용)를 사용하는 방식이 일반적입니다.
2) 시그널(Signal)
시그널은 프로세스에게 특정 이벤트가 발생했음을 알리는 비동기적인 신호입니다.
시그널은 IPC만을 위한 개념이 아니라, 운영체제에서 프로세스의 상태를 제어하기 위해 사용됩니다.
시그널이 발생하면 프로세스는 기존 작업을 멈추고, 시그널 핸들러(Signal Handler)를 실행한 뒤 다시 원래 작업을 수행합니다.
3) 원격 프로시저 호출(RPC)
한 프로세스 내의 특정 코드 실행이 로컬 프로시저 호출이라면, 다른 프로세스의 원격 코드 실행이 원격 프로시저 호출(RPC)입니다.
RPC를 이용하면 언어나 플랫폼과 관계없이 성능 저하를 최소화하고, 메시지를 주고받을 수 있기에 대규모 트래픽 처리 환경, 특히 서버 간 통신 환경에서 사용되는 경우가 많습니다.
'운영체제' 카테고리의 다른 글
운영체제 파헤치기6 - 파일 시스템 (0) | 2025.02.25 |
---|---|
운영체제 파헤치기5 - 메모리 관리 (0) | 2025.02.23 |
운영체제 파헤치기4 - CPU 스케줄링 (0) | 2025.02.13 |
운영체제 파헤치기3 - 동기화와 교착 상태 (0) | 2025.02.11 |
운영체제 파헤치기1 - 시스템 콜과 이중 모드 (0) | 2025.02.06 |
느리더라도 단단하게 성장하고자 합니다!
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!