CHAPTER 3 프로세스와 스레드
- 프로세스(process): 컴퓨터에서 실행되고 있는 프로그램/ CPU 스케줄링의 대상이 되는 작업(task)
- 스레드: 프로세스 내 작업의 흐름
1. 프로세스와 컴파일 과정
프로세스: 프로그램으로부터 인스턴스화된 것
ex. 프로그램: 구글 크롬 프로그램(chrome.exe)와 같은 실행 파일, 2번 클릭 -> 구글 크롬 '프로세스' 시작
프로그램: 컴파일러가 컴파일러 과정 거쳐 컴퓨터 이해 O 기계어로 번역되어 실행될 수 있는 파일이 되는 것
- 전처리: 소스 코드의 주석 제거/ `#include` 등 헤더 파일 병합하여 매크로를 치환
- 컴파일러: 오류 처리, 코드 최적화 작업/ 어셈블리어로 변환
- 어셈블러: 목적 코드(object code)로 변환
- 확장자는 운영체제마다 다름
- 링커: 프로그램 내에 있는 라이브러리 함수 or 다른 파일들과 목적 코드 결합하여 실행 파일 만듦
- 확장자: `.exe` or `.out`
- 정적 라이브러리: 프로그램 빌드 시 라이브러리가 제공하는 모든 코드를 실행 파일에 넣는 방식
- (+) 시스템 환경 등 외부 의존도 ↓ / (-) 코드 중복 등 메모리 효율성 ↓
- 동적 라이브러리: 프로그램 실행 시 필요할 때만 DDL이라는 함수 정보 ~> 참조하는 방식
- (+) 메모리 효율성 / (-) 외부 의존도 ↑
2. 프로세스의 상태
- 생성 상태(create): 프로세스가 생성된 상태 의미
- `fork()` or `exec()` 함수 ~> 생성/ PCB 할당
- `fork()`: 부모 프로세스의 주소 공간을 그대로 복사하며, 새로운 자식 프로세스를 생성하는 함수
- 주소 공간만 복사 O/ 부모 프로세스의 비동기 작업 등을 상속 X
- `exec()`: 새롭게 프로세스를 생성하는 함수
- `fork()`: 부모 프로세스의 주소 공간을 그대로 복사하며, 새로운 자식 프로세스를 생성하는 함수
- `fork()` or `exec()` 함수 ~> 생성/ PCB 할당
- 대기 상태(ready)
- 메모리 공간 충분 -> 메모리 할당
/ 아니면 아닌 상태로 대기하고 있으며 CPU 스케줄러로부터 CPU 소유권이 넘어오기기다리는 상태
- 메모리 공간 충분 -> 메모리 할당
- 대기 중단 상태(ready suspended): 메모리 부족으로 일시 중단된 상태
- 실행 상태(running): CPU 소유권, 메모리 할당 받고 인스트럭션을 수행 중인 상태
- CPU burst가 일어났다고도 표현
- 중단 상태(blocked): 어떤 이벤트가 발생한 이후 기다리며 프로세스가 차단된 상태
- I/O 디바이스에 의한 인터럽트로 발생하기도 함
- ex. 프린트 인쇄 버튼 -> 프로세스 잠깐 멈춘 듯할 때
- I/O 디바이스에 의한 인터럽트로 발생하기도 함
- 일시 중단 상태(blocked suspended) ~= 대기 중단
- 중단된 상태엥서 프로세스가 실행되려고 했지만 메모리 부족으로 일시 중단된 상태
- 종료 상태(terminated): 메모리와 CPU 소유권을 모두 놓고 가는 상태
- 자연스럽게 종료/비자발적 종료(abort)
- 자식 프로세스에 할당된 자원의 한계치 넘어섬
- 부모 프로세스 종료
- 사용자) `process.kill` 등 명령어로 종료
- 자연스럽게 종료/비자발적 종료(abort)
3. 프로세스의 메모리 구조
- 스택(stack): 위 주소부터 할당
- 지역변수, 매개변수, 함수 저장
- 컴파일 시에 크기 결정/ 동적
- 함수가 함수를 재귀적으로 호출하면서 동적으로 크기가 늘어날 수 있음
- 힙과 스택의 메모리 영역이 겹치면 X -> 공간을 비워둠
- 힙(heap): 아래 주소부터 할당
- 동적 할당할 때 사용, 런타임 시 크기가 결정
- ex. 벡터 같은 동적 배열 -> 힙에 동적 할당
- 데이터 영역(BSS segment, Data segment)
- 전역변수, 정적변수 저장
- 정적인 특징을 갖는 프로그램 종료 -> 사라지는 변수가 들어 있는 영역
- BSS 영역: 초기화 X 변수가 0으로 초기화되어 저장
- Data 영역: 0이 아닌 다른 값으로 할당된 변수들이 저장
- 전역변수, 정적변수 저장
- 코드 영역(code segment)
- 프로그램에 내장되어 있는 소스 코드가 들어가는 영역
- 수정 불가능한 기계어로 저장/ 정적
4. PCB(Process Control Block): 프로세스 제어 블록
운영체제에서 프로세스에 대한 메타데이터를 저장한 데이터
프로세스 생성 -> 운영체제) 해당 PCB 생성
프로그램 실행 -> 프로세스 생성 -> 프로세스 주소 값들이 스택, 힙 등의 구조 기반으로 메모리 할당
-> 프로세스의 메타데이터들이 PCB에 저장되어 관리
프로세스의 중요한 정보 포함 -> 일반 사용자 접근 X -> 커널 스택의 가장 앞부분에서 관리
구조
- 프로세스 스케줄링 상태: 준비, 일시중단 등 프로세스가 CPU에 대한 소유권을 얻은 이후의 상태
- 프로세스 ID: 프로세스 ID, 해당 프로세스의 자식 프로세스 ID
- 프로세스 권한: 컴퓨터 자원 or I/O 디바이스에 대한 권한 정보
- 프로그램 카운터: 프로세스에서 실행해야 할 다음 명령어의 주소에 대한 포인터
- CPU 레지스터: 프로세스를 실행하기 위해 저장해야 할 레지스터에 대한 정보
- CPU 스케줄링 정보: CPU 스케줄러에 의해 중단된 시간 등에 대한 정보
- 계정 정보: 프로세스 실행에 사용된 CPU 사용량, 실행한 유저의 정보
- I/O 상태 정보: 프로세스에 할당된 I/O 디바이스 목록
컨텍스트 스위칭(context switching)
- PCB를 교환하는 과정
- 한 프로세스에 할당된 시간 끝나거나 인터럽트에 의해 발생
- 싱글코어 기준으로 설명
- 한 개의 프로세스 A가 실행하다 멈추고 프로세스 A의 PCB 저장, 다시 프로세스 B를 로드하여 실행
-> 프로세스 B의 PCB를 저장하고 프로세스 A의 PCB를 로드 - 컨텍스트 스위칭이 일어날 때 유휴 시간(idle time) 발생
- 캐시미스(비용): 컨텍스트 스위칭이 일어날 때 프로세스가 가지고 있는 메모리 주소가 그대로 있으면 잘못된 주소 변환 생김
-> 캐시클리어 과정 겪게 되고 캐시미스 발생 - 스레드에서도 발생
- 스레드: 스택 영역 제외한 모든 메모리 공유 -> 비용 ↓, 시간 ↓
5. 멀티프로세싱
여러 개의 프로세스(멀티프로세스) ~> 동시에 2가지 이상의 일을 수행할 수 있는 것
-> 하나 이상의 일을 병렬로 처리 O 특정 프로세스의 메모리
, 프로세스 중 일부 문제 발생 -> 다른 프로세스 이용해서 처리 -> 신뢰성 ↑
웹 브라우저
- 브라우저 프로세스: 주소 표시줄, 북마크 막대, 뒤로 가기 버튼, 앞으로가기 버튼 등 담당. 네트워크 요청 or 파일 접근 권한 담당
- 렌더러 프로세스: 웹 사이트가 보이는 부분의 모든 것 제어
- 플러그인 프로세스: 웹 사이트에서 사용하는 플러그인 제어
- GPU 프로세스: GPU 이용해서 화면 그리는 부분 제어
IPC(Inter Process Communication)
- 프로세스끼리 데이터를 주고받고 공유데이터를 관리하는 메커니즘
- 멀티프로세스는 IPC 가능
- ex. 클라이언트) 데이터 요청 / 서버) 클라이언트 요청에 응답
- 종류: 공유 메모리, 파일, 소켓, 익명 파이프, 명명 파이프, 메시지 큐
- 메모리가 완전히 공유되는 스레드보다 속도 ↓
- 공유 메모리(shared memory): 여러 프로세스에 동일한 메모리 블록에 대한 접근 권한 부여
-> 프로세스가 서로 통신할 수 있도록 공유 버퍼를 생성하는 것- (기본) 각 프로세스의 메모리를 다른 프로세스가 접근 X/ 공유 메모리 ~> 여러 프로세스가 하나의 메모리 공유 O
- 어떠한 매개체 ~> 데이터 주고 받음 X 메모리 자체 공유 O -> 불필요한 데이터 복사의 오버헤드 발생 X
- 가장 빠름, 같은 메모리 영역 여러 프로세스가 공유 -> 동기화 필요
- 하드웨어 관점: RAM
- 파일: 디스크에 저장된 데이터 or 파일 서버에서 제공한 데이터
- 이를 기반으로 프로세스 간 통신
- 소켓: 동일한 컴퓨터의 다른 프로세스 or 네트워크의 다른 커뮾터로 네트워크 인터페이스 ~> 전송하는 데이터
- TCP, UDP
- 익명 파이프(unamed pipe): 프로세스 간에 FIFO 방식으로 읽히는 임시 공간인 파이프를 기반으로 데이터를 주고 받음
- 단방향 방식 읽기 전용, 쓰기 전용 파이프 만들어서 작동하는 방식
- 부모, 자식 프로세스 간에만 사용/ 다른 네트워크상에서 사용 X
- 명명된 파이프(named pipe): 파이프 서버, 하나 이상의 파이프 클라이언트 간의 통신을 위한 명명된 단방향 or 이중 파이프
- 클라이언트-서버 통신을 위한 별도의 파이프 제공/ 여러 파이프 동시 사용 O
- 서버용 파이프 / 클라이언트용 파이프 구분해서 작동, 하나의 인스턴스 열거나 여러 개의 인스턴스 기반으로 통신
- 컴퓨터의 프로세스끼리 or 다른 네트워크상의 컴퓨터와도 통신 O
- 클라이언트-서버 통신을 위한 별도의 파이프 제공/ 여러 파이프 동시 사용 O
- 메시지 큐: 메시지를 큐 데이터 구조 형태로 관리하는 것
- 커널 전역 변수 형태 등 커널에서 전역적으로 관리
- 다른 IPC 방식에 비해 사용 방법 직관적, 간단/ 다른 코드 수정 X 몇 줄 코드 추가 -> 간단하게 메시지 큐에 접근
- 공유 메모리 ~> IPC 구현할 때 쓰기/읽기 빈도 ↑ -> 동기화 때문에 구현하는 것 복잡 => (대안) 메시지 큐
6. 스레드와 멀티스레딩
스레드: 프로세스의 실행 가능한 가장 작은 단위
프로세스는 여러 스레드(메인, 워커, 컴포지터, 레지스터) 가질 수 있음
멀티 스레딩
프로세스 내 작업 -> 여러 개의 스레드, 멀티스레드로 처리하는 기법
- 스레드끼리 서로 자원 공유 -> 효율성 ↑
- ex. 웹 요청 처리 -> 새 프로세스 생성하는 대신 스레드 사용하는 웹 서버
- (+) ↓ 리소스 소비, 한 스레드가 중단(blocked)
But, 다른 스레드는 실행(running) 상태일 수 있음 -> 중단 X 빠른 처리 가능 + 동시성
동시성: 서로 독립적인 작업들 -> 작은 단위로 나눔, 동시에 실행되는 것처럼 보여주는 것 - (-) 한 스레드에 문제 -> 다른 스레드에도 영향 => 스레드로 이뤄져 있는 프로세스에 영향 O
- (+) ↓ 리소스 소비, 한 스레드가 중단(blocked)
7. 공유 자원과 임계 영역
공유 자원(shared resource)
시스템 안에서 각 프로세스, 스레드가 함께 접근할 수 있는 모니터, 프린터, 메모리, 파일, 데이터 등의 자원이나 변수 등 의미
경쟁 상태(race condition): 2개 이상의 프로세스가 동시에 읽거나 쓰는 상태
동시에 접근을 시도할 때 접근의 타이밍 or 순서 등 -> 결괏값에 영향 O 상태
임계 영역(critical section)
둘 이상의 프로세스, 스레드가 공유 자원에 접근할 때 순서 등의 이유로 결과가 달라지는 코드 영역
-> 해결 방법: 뮤텍스, 세마포어, 모니터
-> (상호 배제, 한정 대기, 융통성) 조건 만족
-> 토대가 되는 메커니즘: 잠금(lock)
<뮤텍스(mutex)>
- 프로세스 or 스레드 공유 자원을 `lock()` ~> 잠금 설정, 사용한 후에 `unlock()` ~> 잠금 해제
- 잠금 설정 -> 다른 프로세스 or 스레드 잠김 코드 영역에 접근 X
<세마포어(semaphore)> : 일반화된 뮤텍스
- 간단한 정수 값과 2가지 함수 `wait(P함수)`, `signal(V함수)`로 공유 자원에 대한 접근 처리
- `wait()`: 자신의 차례가 올 때까지 기다리는 함수
- `signal()`: 다음 프로세스로 순서를 넘겨주는 함수
- 프로세스 or 스레드 공유 자원에 접근 -> 세마포어에서 `wait()` 작업 수행
/ 공유 자원 해제 -> 세마포어에서 `signal()` 작업 수행 - 조건 변수 X 프로세스 or 스레드가 세마포어 값 수정할 때 다른 프로세스 or 스레드 동시에 수정 X
- 바이너리 세마포어
- 0과 1의 2가지 값만 가질 수 있는 세마포어
- 구현의 유사성 -> 뮤텍스는 바이너리 세마포어라고 할 수 있음
- 뮤텍스: 잠금을 기반으로 상호 배제가 일어나는 잠금 메커니즘
- 세마포어: 신호를 기반으로 상호 배제가 일어나는 신호 메커니즘
- 카운팅 세마포어
- 여러 개의 값을 가질 수 있는 세마포어
- 여러 자원에 대한 접근을 제어하는데 사용
<모니터>
- 둘 이상의 스레드 or 프로세스가 공유 자원 안전하게 접근-> 공유 자원 숨기고 해당 접근에 대해 인터페이스만 제공
- 세마포어보다 구현하기 쉬움
- 상호 배제 자동 (<-> 세마포어: 명시적으로 구현)
8. 교착 상태(deadlock)
2개 이상의 프로세스들이 서로 가진 자원을 기다리며 중단된 상태
ex. 프로세스 A가 프로세스 B의 어떤 자원 요청할 때 프로세스 B도 프로세스 A가 점유하고 있는 자원 요청
원인
- 상호 배제: 한 프로세스가 작원 독점하고 있으며 다른 프로세스들은 접근 X
- 점유 대기: 특정 프로세스가 점유한 자원을 다른 프로세스가 요청하는 상태
- 비선점: 다른 프로세스의 자원을 강제적으로 가져올 수 X
- 환형 대기: 서로가 서로의 자원을 요구하는 상황
해결 방법
- 자원을 할당할 때 애초에 조건이 성립되지 않도록 설계
- 교착 상태 가능성이 없을 때만 자원 할당, 프로세스당 요청할 자원들의 최대치
~> 자원 할당 가능 여부를 파악하는 은행원 알고리즘 사용
은행원 알고리즘: 총 자원의 양과 현재 할당한 자원의 양을 기준으로 안정/불안정 상태로 나누고 안정 상태로 가도록 자원 할당 - 교착 상태 발생 -> 사이클이 있는지 찾아보고 이에 관련된 프로세스 한 개씩 지움
- 교착 상태 매우 드물게 일어남 -> 처리하는 비용 ↑ -> 교착 상태 발생하면 사용자가 작업 종료
SECTION 4 CPU 스케줄링 알고리즘
1. 비선점형 방식(non-preemptive)
프로세스가 스스로 CPU 소유권을 포기하는 방식
강제로 프로세스 중지 X -> 컨텍스트 스위칭으로 인한 부하 ↓
- FCFS(First Come, First Served): 가장 먼저 온 것을 가장 먼저 처리하는 알고리즘
- (-) 길게 수행되는 프로세스 -> 준비 큐에서 오래 기다리는 현상(convoy effect)
- SJF(Shortees Job First): 실행 시간 가장 ↓ 프로세스 가장 먼저 실행하는 알고리즘
- (-) 긴 시간을 가진 프로세스가 실행 X 현상(starvation)
- (+) 평균 대기 시간 가장 ↓
- But, 실제로는 실행 시간 X -> 과거의 실행했던 시간을 토대로 추측해서 사용
- 우선순위: 긴 시간 가진 프로세스 실행 X 현상(SJF 스케줄링) 보완
- 오래된 작업일수록 우선순위 높이는 방법(aging)
2. 선점형 방식(preemptive)
현대 운영체제가 쓰는 방식
지금 사용하고 있는 프로세스를 알고리즘에 의해 중단, 강제로 다른 프로세스에 CPU 소유권 할당하는 방식
- 라운드 로빈(RR; Round Robin): 각 프로세스는 동일한 할당 시간/ 그 시간 안에 끝나지 X -> 다시 준비 큐의 뒤로 감
- 현대 컴퓨터가 쓰는 스케줄링인 우선순위 스케줄링(priority scheduling)의 일종
- ex. q만큼의 할당 시간 부여, N개의 프로세스 운영 -> $(N-1)*q$ 시간이 지나면 자기 차례가 옴
- 할당 시간 너무 ↑ -> FCFS / ↓ -> 컨텍스트 스위칭 잦아짐 -> 오버헤드(비용 ↑)
- 전체 작업 시간 ↑, 평균 응답 시간 ↓
- 로드밸런서에서 트래픽 분산 알고리즘으로 사용
- SRF: 중간에 더 짧은 작업 들어오면 수행하던 프로세스 중지하고 해당 프로세스 수행하는 알고리즘
- SJF: 중간에 실행 시간이 더 짧은 작업이 들어와도 기존 짧은 작업 모두 수행 후 그다음 짧은 작업 이어감
- 다단계 큐: 우선순위에 따른 준비 큐 여러 개 사용, 큐마다 라운드 로빈 or FCFS 등 다른 스케줄링 알고리즘 적용
- 큐 간의 프로세스 이동 X -> (+) 스케줄링 부담 ↓, (-) 유연성 ↓