10-1 프로세스 개요
프로세스(process): 실행중인 프로그램
프로세스 직접 확인하기
ps 명령어를 통해 확인 가능
포그라운드 프로세스(foreground process): 사용자가 보는 앞에서 실행
백그라운드 프로세스(background process): 사용자가 보지 못하는 뒤에서 실행
- 데몬(daemon): 유닉스 체계의 운영체제의 백그라운드 프로세스
- 서비스(service): 우니도우 운영체제에서의 백그라운드 프로세스
프로세스 제어 블록
PCB(Process Control Block, 프로세스 제어 블록)
- 프로세스와 관련된 정보를 저장하는 자료 구조
- 해당 프로세스를 식별하기 위해 꼭 필요한 정보들이 저장
- 메모리에 있는 커널 영역에서 생성
- 프로세스 생성 시에 만들어지고 실행이 끝나면 폐기
PCB에 담기는 정보
- PID(프로세스 ID, Process ID)
- 특정 프로세스를 식별하기 위해 부여되는 고유한 번호
- 레지스터 값
- 이전까지 사용했던 레지스터의 중간값
- 프로그램 카운터 등의 레지스터 값
- 프로세스 상태
- 입출력장치를 사용하기 위해 기다리는지, CPU를 기다리는지, CPU를 이용하는지 등
- CPU 스케줄링 정보
- 프로세스가 언제, 어떤 순서로 CPU를 할당받았는지
- 메모리 관리 정보
- 프로세스가 어느 주소에 저장되어 있는지
- 베이스 레지스터, 한계 레지스터 값 등
- 페이지 테이블 정보
- 사용한 파일과 입출력장치 목록
- 실행과정에서 특정 입출력장치나 파일을 사용하는지
문맥 교환
- 프로세스 실행에 대한 중간 정보를 저장해야, 다음 차례가 왔을 때 이전까찌 실행했던 내용에 이어 다시 실행을 재개할 수 있음
- 문맥(context)
- 해당 프로세스의 PCB에 표현
- 문맥 교환(context switching)
- 기존 프로세스의 문맥을 PCB에 백업하고, 새로운 프로세스를 실행하기 위해 문맥을 PCB로 복구하여 새로운 프로세스를 실행하는 것
- 프로세스 A 실행 → A 문맥 저장 → B 문맥 로드 → 프로세스 B 실행 → B 문맥 저장 → …
- 너무 자주 하면 오버헤드가 발생하여 부정적인 효과
프로세스의 메모리 영역
PCB는 커널 영역에 생성
사용자 영역에 프로세스가 배치되는 형식
- 정적 할당 영역
- 코드 영역 + 데이터 영역
- 프로그램이 실행되는 동안 유지되어 크기가 고정된 영역
- 동적 할당 영역
- 힙 영역 + 스택 영역
- 프로그램 실행 과정에서 그 크기가 변할 수 있는 영역
코드 영역
- 코드 영역(code segment) = 텍스트 영역(text segment)
- 실행할 수 있는 기계어로 이루어진 명령어가 저장
- 쓰기가 금지된 읽기 전용 영역
- OS는 쓸 수 있고, 프로세스는 쓸 수 없다는 뜻
데이터 영역
- data segment
- 프로그램이 실행되는 동안 유지할 데이터
- ex) 전역 변수(global variable)
힙 영역
- heap segment
- 프로그램을 만드는 사용자(프로그래머)가 직접 할당할 수 있는 공간
- 할당했다면 언젠가는 반환해야 함
- 메모리 누수(memory leak): 메모리 공간을 반환하지 않아 남아있는 할당된 공간으로 인해 낭비되는 것
- 현대 운영체제에서는 프로세스가 종료되면 해당 프로세스의 PCB를 폐기하고 이때 이 정보를 바탕으로 관련된 메모리를 청소한다.
- 하지만 종료되지 않는다면?
- 단순 대기
- 다음 실행을 위해 차지하고 있을 뿐, 누수가 아니다.
- 하지만 영영 실행될 일이 없는 프로세스라면, OS 관점에선 대기지만 개발자 관점에선 누수!
- 누수
- 메모리 주소를 잃어버려서 할당된 메모리를 반환하지 못하는 등…
할당을 반환하지 않고 함수가 종료된다거나
똑같은 변수에 여러번 메모리 주소를 할당한다거나..
1 2 3 4 5 6 7int* ptr; ptr = malloc(4); // 1. 힙 영역에 '메모리 A'를 만들고, ptr이 그 주소를 가짐. // (현재 ptr은 A의 위치를 알고 있음) ptr = malloc(4); // 2. 힙 영역에 '메모리 B'를 새로 만듦. // 3. ptr에 B의 주소를 넣음. (덮어쓰기!)
- 메모리 주소를 잃어버려서 할당된 메모리를 반환하지 못하는 등…
- 단순 대기
스택 영역
- stack segment
- 데이터를 일시적으로 저장하는 공간
- ex) 매개 변수, 지역 변수 등
- 저장할 데이터는 PUSH, 필요하지 않은 데이터는 POP
- 우리가 아는 그 후입선출 스택 구조. 왜?
- 한 프로세스에서 여러 함수가 순차적으로 호출된다면 나중에 호출된 함수가 먼저 종료된다.
- 따라서 나중에 호출된 함수에서 할당된 지역변수 등이 먼저 해제된다.
- 따라서 가장 최근에 PUSH된 것이 가장 먼저 POP된다.
10-2 프로세스 상태와 계층 구조
프로세스 상태
프로세스 상태 다이어그램(process state diagram)
생성 상태
생성 상태(new): 프로세스를 생성 중인 상태. 이후 준비 상태가 됨
준비 상태
준비 상태(ready): CPU를 할당받아 실행할 수 있지만 차례가 아니어 기다리고 있는 상태
dispatch: 준비 상태인 프로세스가 실행 상태로 전환되는 것
실행 상태
실행 상태(running): CPU를 할당받아 실행중인 상태. 타이머 인터럽트가 발생하면 다시 준비 상태로 변경
대기 상태
대기 상태(blocked): 입출력장치의 작업이 끝나는 등 특정 이벤트를 기다리는 상태. 이후 준비 상태로 변경
종료 상태
종료 상태(terminated): 운영체제가 PCB와 프로세스가 사용한 메모리를 정리
프로세스 계층 구조
프로세스는 실행 도중 시스템 콜을 통해 다른 프로세스를 생설할 수 있다.
부모 프로세스(parent process): 새 프로세스를 생성한 프로세스
자식 프로세스(child process): 부모 프로세스에 의해 생성된 프로세스
PPID(Parent PID): 부모 프로세스의 PID
프로세스 계층 구조
- 결국 컴퓨터는 최초의 프로세스 하나가 자식 프로세스를 생성하고 그 프로세스가 다시 자식 프로세스를 생성하는 것이 반복된다.
- 최초의 프로세스: init(unix), systemd(linux), launchd(macOS)
프로세스 생성 기법
fork(복제)
- 자신의 복사본을 자식 프로세스로 생성
- 메모리 내의 내용, 열린 파일의 목록이 자식 프로세스에 상속
- PID 값이나 저장된 메모리 위치는 다름
exec(옷 갈아입기)
- 자신의 메모리 공간을 다른 프로그램으로 교체
- fork 로 만들어진 자식 프로세스는 exec 시스템 콜을 통해 새로운 프로그램으로 전환
부모가 자식 프로세스를 생성하여 계층이 추가되는 것은은 fork → exec → fork → … 의 반복
예제
| |
을 실행하면
| |
이렇게 뜸. 내부에서 함수가 실행되면서 자식 프로세스가 생성되어 실행된 것
| |
이렇게 하면
| |
이렇게 한 부모에서 동일한 동작을 하는 3개의 자식 프로세스가 호출됨
10-3 스레드
스레드(thread)
- 프로세스를 구성하는 실행의 흐름 단위
- 하나의 프로세스는 여러 개의 스레드를 가질 수 있음
프로세스와 스레드
단일 스레드 프로세스: 하나의 프로세스가 한 번에 하나의 일만 처리
스레드
- 프로세스를 구성하는 여러 명령어를 동시에 실행할 수 있음
- 프로세스 내에서 각기 다른 스레드 ID, PC 값을 비롯한 레지스터 값, 스택으로 구성
- 따라서 각기 다른 코드를 실행할 수 있음
- 최소한의 정보만으로 실행
- 그외 프로세스 자원들은 공유
태스크(task)
- 리눅스에선 프로세스와 스레드를 구분하지 않음
- 모두 실행의 문맥일 뿐
멀티프로세스와 멀티스레드
멀티프로세스(multiprocess): 여러 프로세스를 동시에 실행
멀티스레드(multithread): 여러 스레드로 프로세스를 동시에 실행
| 멀티프로세스 | 멀티스레드 | |
|---|---|---|
print("hello, OS”) 실행 | 여러 개 출력 | 여러 개 출력 |
| 자원 | 코드 영역, 데이터 영역, 힙 영역 등 모든 자원이 복제되어 메모리에 적재되므로 중복 발생(쓰기 복사(copy on write)로 방지 가능) | 레지스터 값, 스택은 독립. 코드 영역, 데이터 영역, 힙 영역, 열린 파일 등은 공유 |
| 문제 발생 | 다른 프로세스에 영향 X | 프로세스 전체에 문제 가능성 O |
| 메모리 | 독립된 메모리 공간 | 프로세스 메모리 공유 |
| 1. 코드 | 복제 (논리적 분리) | 공유 |
| 2. 전역 변수 | 독립적 (변경해도 남에게 영향 없음) | 공유됨 (변경 시 다른 스레드 즉시 반영) |
| 3. 지역 변수 | 독립적 | 독립적 (Stack 분리) |
| 4. DB 연결 | 공유 불가 (프로세스별 재연결 필수) | 공유 가능하나 동시 사용 시 충돌 위험 (Lock 필요) |
| 5. DB 풀 | 공유 불가 (각자 풀을 만들어야 함) | 하나의 풀을 공유하여 사용 |
프로세스 간 통신
프로세스 간 통신(Inter-Process-Communication)
- 프로세스 간의 자원을 공유하고 데이터를 주고 받는 것
통신
- 네트워크를 통해 데이터를 주고 받는 방식
- 같은 컴퓨터 내의 서로 다른 프로세스나 스레드끼리 데이터를 주고 받는 것도 포함
- ex) 파일을 통한 프로세스 간 통신, 공유 메모리(shared memory)