이번 포스트에서는 프로세스와 스레드에 대해 자세히 알아보겠습니다.
1. 개요
프로세스의 유형
- 포그라운드 프로세스(foreground process): 사용자가 보는 공간에서 사용자와 상호작용하며 실행
- 백그라운드 프로세스(background process): 사용자가 보지 못하는 곳에서 실행
- 데몬: 백그라운드 프로세스 중, 사용자와 별다른 상호작용 없이 주어진 작업만 수행
- 서비스: 데몬을 윈도우 운영체제에서 부르는 말
프로세스 정보가 저장되는 영역
- 커널영역
- 프로세스 제어 블록(PCB)에 저장
- 사용자 영역
- 코드 영역, 데이터 영역, 힙 영역, 스택 영역으로 나뉘어 저장

- 정적 영역: 프로그램 실행 도중 크기가 변하지 않음
- 코드 영역(code segment, 텍스트 영역)
- 실행 가능한 명령어가 저장되는 공간
- CPU가 읽고 실행할 명령어가 담겨 있음 → “Read Only” 공간
- 데이터 영역(data segment)
- 프로그램이 실행되는 동안 유지할 데이터가 저장되는 공간
- 정적 변수, 전역 변수 등
- 추가 구분: BSS 영역 - 초기화 여부의 차이
- 초깃값이 있는 데이터: 데이터 영역에 저장
- 초깃값이 없는 데이터: BSS 영역에 저장
- 코드 영역(code segment, 텍스트 영역)
- 동적 영역: 프로그램 실행 도중 크기가 변할 수 있는 영역
- 힙 영역(heap segment)
- 개발자가 직접 할당 가능한 저장공간 → 프로그램 실행 중 비교적 자유롭게 할당 가능
- 힙 영역에 메모리를 할당하였다면, 언젠가는 할당 공간을 반환해야 함
- 반환하지 않는다면 메모리 누수(memory leak) 문제 발생: 할당한 공간이 계속 남아 메모리 낭비
- 가비지 컬렉션: 메모리 누수 문제 해결을 위해 프로그래밍 언어 자체에서 사용되지 않는 힙 메모리를 해제하는 기능
- 스택 영역(stack segment)
- 일시적으로 사용할 값들이 저장되는 공간
- 매개변수, 지역변수, 함수 복귀 주소 등이 저장
- 스택 트레이스 형태의 함수 호출 정보가 저장됨
- 스택 트레이스(stack trace): 특정 시점에 저장된 함수 호출 정보 → 문제 발생 지점을 추적하는 데 매우 유용(디버깅에 활용)
- 스택 트레이스(stack trace): 특정 시점에 저장된 함수 호출 정보 → 문제 발생 지점을 추적하는 데 매우 유용(디버깅에 활용)
- 일시적으로 사용할 값들이 저장되는 공간
- 힙 영역(heap segment)
- 코드 영역, 데이터 영역, 힙 영역, 스택 영역으로 나뉘어 저장
PCB와 문맥 교환
- PCB(문맥 제어 블록, Process Control Block): 프로세스를 식별할 수 있는 커널 영역 내의 정보
- OS가 메모리에 적재된 다수의 프로세스를 관리하기 위한 정보
- 프로세스에 관한 다양한 정보를 내포하는 구조체의 일종
- 구조체(struct): 서로 다른 자료형으로 이루어진 데이터를 하나로 묶어 활용할 수 있도록 하는 복잡 자료형의 일종.
- PCB의 생성 및 폐기
- 생성: 새로운 프로세스가 메모리에 적재 시(=프로세스 생성)
- 폐기: 프로세스 실행 종료시
- PCB가 담고 있는 내용
- PID(Process ID)
- (프로세스의 실행 과정에서 사용한)레지스터 값
- 프로세스 (현재)상태
- CPU 스케줄링(우선순위)
- 프로세스의 CPU 할당 순서
- 메모리 관련 정보
- 메모리 상 프로세스의 적재위치
- 파일 및 입출력 장치 관련 정보
- PCD example - task struct(Linux 운영체제의 PCB)

- PCD example - task struct(Linux 운영체제의 PCB)
- 프로세스 테이블
- 실행중인 PCB의 모음
- 다수의 PCB는 커널 내 프로세스 테이블의 형태로 관리되는 경우가 많음
- PCB 추가 및 제거
- 추가: 새롭게 실행되는 프로세스는 해당 프로세스의 PCB를 프로세스 테이블에 추가 / 필요 자원 할당
- 제거: 종료되는 프로세스는 사용중이던 자원 해제 / 해당 PCB도 삭제

- 좀비 프로세스: 프로세스 비정상 종료 상태, 종료된 프로세스의 PCB가 프로세스 테이블에 남아 있는 경우
프로세스의 실행
- 메모리에 적재된 프로세스들은 한정된 시간 동안 번갈아 가며 실행됨
- 프로세스의 “실행” = 운영체제에 의해 “CPU 자원을 할당”받음
- CPU → 프로세스를 구성하는 명령어 및 데이터 인출
- 인출한 명령어 및 데이터 실행
- 운영체제 → CPU 자원 할당
- 프로세스의 “실행” = 운영체제에 의해 “CPU 자원을 할당”받음
- 프로세스의 CPU 사용 시간
- 타이머 인터럽트(timer interrupt)에 의해 제한
- 타이머 인터럽트(=타임아웃 인터럽트): 시간이 끝났음을 알리는 인터럽트
- 프로세스의 CPU 사용 절차
- 정해진 시간만큼 CPU 이용
- 타이머 인터럽트 발생
- CPU를 사용하던 프로세스의 문맥 백업
- 다음 프로세스에 CPU 양보
- 뒤이어 실행할 프로세스의 문맥 복구
- ex) 프로세스 A 실행 → 타이머 인터럽트 → 프로세스 B 실행
- 프로세스 A는 타이머 인터럽트가 발생하면, 지금까지의 중간 정보를 백업해야함
- 그래야 다시 프로세스 A를 실행차례가 왔을 때, 작업 재개 가능
- 문맥(context): 다시 작업을 재개하기 위해 기억해야 할 정보
- 프로그램 카운터, 각종 레지스터 값, 메모리 정보, 실행을 위해 열었던 파일, 사용한 입출력 장치 등
- 프로세스의 문맥은 해당 프로세스의 PCB에 명시

- Context switching(문맥 교환)
- 기존 프로세스의 문맥은 PCB에 백업 하고, PCB에서 문맥 복구하여 새로운 프로세스 실행하는 일련의 과정
- Context switching의 맹점
- 잦은 context switching은 캐시 미스가 발생할 가능성이 높음
- 메모리로부터 실행할 프로세스 내용을 가져오는 작업이 빈번
→ 큰 오버헤드로 연결될 가능성 높음

- 메모리로부터 실행할 프로세스 내용을 가져오는 작업이 빈번
→ 큰 오버헤드로 연결될 가능성 높음
- 잦은 context switching은 캐시 미스가 발생할 가능성이 높음
- 타이머 인터럽트(timer interrupt)에 의해 제한
프로세스의 상태
- 하나의 프로세스는 여러 상태를 거치며 실행됨
- PCB를 통해 프로세스의 상태를 인식 및 관리
- 각 상태 별 다이어그램 관계도
- 생성 상태(new)
- 프로세스 생성 중인 상태
- 메모리에 적재되어 PCB를 할당 받은 상태
- 프로세스 실행 준비를 마치고 준비 상태로 보냄
- 준비 상태(ready)
- 당장 실행은 가능한 상태 but 본인의 차례가 아니라 기다리는 상태
- CPU를 할당 받으면, 그 즉시 실행 상태로 전환
- 위와 같은 과정을 디스패치(dispatch)라 함
- 실행 상태(running)
- CPU를 할당받아 실행 중인 상태
- 일정 시간만 CPU 사용 가능
- 타이머 인터럽트가 발생하면, 준비 상태로 돌아감
- 실행 도중 입출력 장치를 사용하여 입출력장치의 작업 종료시까지 기다려야 한다면, 대기상태가 됨
- CPU를 할당받아 실행 중인 상태
- 대기 상태(blocked)
- 곧장 실행이 불가능한 조건에 놓이는 상태
- 입출력 작업을 요청하는 경우
- 바로 확보할 수 없는 자원을 요청하는 경우
- 실행 가능한 상태가 되면, 다시 준비 상태가 되어 CPU 할당을 기다림
- 곧장 실행이 불가능한 조건에 놓이는 상태
- 종료 상태(terminated)
- 프로세스가 종료된 상태
- 운영체제는 PCB 및 프로세스가 사용한 메모리를 정리함

- 생성 상태(new)
- 블로킹 입출력 / 논블로킹 입출력
- 블로킹 입출력(blocking I/O): 일반적인 “실행 상태 → 대기 상태 → 준비 상태” 입출력 방식
- 논블로킹 입출력(non-blocking I/O): 대기 상태 없이, 입출력장치에게 입출력 작업을 맡기고 곧바로 이어질 명령어를 실행하는 입출력 방식

2. 멀티프로세스 / 멀티스레드
멀티프로세스
- 멀티프로세스(multi-process)
- 동시에 여러 프로세스가 실행되는 것
- 각기 다른 프로세스들은 자원을 공유하지 않음 → 독립적으로 실행 → 자원도 독립적으로 할당(PID, 파일 및 입출력장치 등) → 한 프로세스에 문제가 생겨도 다른 프로세스에 직접적 영향을 끼치지 않음

멀티스레드
- 멀티스레드(multi-thread)
- 여러 프로세스를 동시에 실행하는 다중 스레드
- 한 프로세스를 구성하는 코드를 동시에 실행하는 방법
- 하나의 스레드는 각각 식별 가능한 고유 정보들로 구성
- 스레드 ID / 프로그램 카운터 / 레지스터 값 / 스택 등
- 스레드마다 다음에 실행할 주소를 가질 수 있고, 연산 과정의 임시 저장 값 확보 가능

멀티프로세스 vs 멀티스레드
- 가장 큰 차이점: 자원 공유 여부
- 멀티프로세스: 서로 자원 공유 X
- 한 프로세스에 문제가 생겨도, 다른 프로세스에는 지장이 없거나 적음
- 멀티스레드: 같은 프로세스를 여러 스레드가 실행하기 때문에, 같은 프로세스의 자원 공유
- 쉽게 협력 및 통신이 가능함
- 한 스레드에 문제가 생기면 프로세스 전체의 문제가 될 수 있음

- 멀티프로세스: 서로 자원 공유 X
- 스레드 조인
- 스레드 생성시 흔히 보는 연산
- join(): 스레드를 생성한 주체가 ‘생성/실행된 스레드가 종료될 때까지 대기’해야함을 의미
- ex) ‘main’이라는 스레드가 ‘a’스레드를 생성하면서 join을 호출했다면? → ‘main’ 스레드는 ‘a’스레드가 종료될 때까지 실행되지 않고 대기함
- ex) ‘main’이라는 스레드가 ‘a’스레드를 생성하면서 join을 호출했다면? → ‘main’ 스레드는 ‘a’스레드가 종료될 때까지 실행되지 않고 대기함
3. 프로세스 간 통신
- 프로세스는 기본적으로 자원을 공유하지 않음
- IPC(Inter-Process Communication, 프로세스 간 통신) → 프로세스 간에 자원을 공유하고 데이터를 주고 받을 수 있도록 하는 방법
- IPC의 두 가지 유형
- 공유 메모리: 데이터를 주고 받는 프로세스가 공통으로 사용할 영역을 두는 방식
- 메시지 전달: 프로세스 간에 데이터를 주고 받을 때, 메시지의 형태로 주고받는 방식

공유 메모리
- 프로세스 간 공유하는 메모리 영역을 토대로 데이터를 주고 받는 통신 방식
- 프로세스는 각각의 메모리 공간을 갖고 있음 → 다른 프로세스의 메모리 공간을 임의로 수정할 수 없음
- 공유 메모리라는 특별 메모리 공간을 할당하여 프로세스 간 공유하며 읽고 쓸 수 있게 됨
- 공유 메모리 실행 방식
- 시스템 콜 기반: 프로세스가 공유하는 메모리 영역 확보하는 시스템 콜 실행
- 단순 공유변수 및 파일 활용: 공유 메모리 공간에 변수 및 파일을 쓰고 읽어들이는 방식
- 공유 메모리의 특징
- 통신을 주고 받는 각 프로세스가 자신의 메모리 영역을 읽고 쓰는 것처럼 통신
- 데이터를 주고 받는 과정에서 커널의 개입이 거의 없음
- 프로세스가 주고 받는 데이터는 커널 영역을 거치지 않는 경우가 다수
- 메시지 전달 방식보다 통신 속도가 빠름
- 데이터의 일관성 훼손 가능성
- 레이스 컨디션 문제 발생 → 서로의 공유 메모리를 동시에 읽고 쓰기 때문
- 레이스 컨디션 문제 발생 → 서로의 공유 메모리를 동시에 읽고 쓰기 때문
메시지 전달
- 프로세스 간 주고 받는 데이터가 커널을 통해 메시지를 송수신하는 통신방식
- 메시지를 보내는 수단과 받는 수단이 명확하게 구분되어 있음
- 메시지를 보내는 시스템 콜: send()
- 메시지를 받는 시스템 콜: recv()
- 공유 메모리 기반과 달리 커널의 도움을 적극적으로 받을 수 있음 → 레이스 컨디션 / 동기화 등의 문제를 고려할 일이 적음
- 메시지를 보내는 수단과 받는 수단이 명확하게 구분되어 있음
- 메시지 전달 IPC의 대표 수단
- 파이프(pipe)
- 단방향 프로세스 간 통신 도구
- ex) 프로세스 A와 B
- 프로세스 A가 파이프의 한 쪽 방향에서 데이터 write
- 프로세스 B는 파이프 반대쪽으로 그 데이터를 읽을 수 있음

- 익명 파이프 vs 지명 파이프
- 익명 파이프(unnamed pipe): 전통적인 단방향 통신 수단
- 지명 파이브(named pipe)
- FIFO, 양방향 통신 지원
- 부모-자식 프로세스 간 통신 뿐 아니라, 임의의 프로세스 간 통신에도 사용 가능
- 시그널
- 프로세스에게 특정 이벤트가 발생했음을 알리는 비동기적 신호
- “적절히 시그널을 활용해 IPC를 수행할 수 있다” 정도로 인지
- 시그널의 기본 동작
- 대부분 프로세스 종료 / 무시 / 코어 덤프 생성
- 코어 덤프
- 주로 비정상적으로 종료되는 경우에 생성되는 파일
- 프로그램이 특정 시점에 작업하던 메모리 상태 기록
- 디버깅에 많이 활용
- 시그널을 발생시키는 이벤트의 종류
- 대부분 인터럽트와 관련
- 사용자가 직접 정의 가능한 시그널도 있음
- ex) Linux OS의 대표 시그널

- 시그널 발생 시, 이를 처리하기 위한 과정
- 하던 일 잠시 중단
- 시그널 핸들러 실행 → 시그널 처리해줌
- 실행 재개
- 프로세스가 직접 특정 시그널 발생시킴
- 프로세스가 직접 일부 시그널 핸들러를 재정의 하기도
- 정리
- 다른 IPC 기법들과 다르게 직접적 메시지 상호작용은 하지 않음
- 비동기적으로 원하는 동작 수행이 가능한 좋은 수단

- 소켓
- 네트워크 관련 포스팅에서 추가 기술
- 원격 프로시저 호출(RPC)
- 원격 코드를 실행하는 IPC 기술
- 원격 코드를 실행하는 IPC 기술
- 파이프(pipe)
chat_bubble 댓글남기기
댓글남기기