프로세스(Process)
- 컴퓨터에서 실행되고 있는 프로그램
- 프로그램으로부터 인스턴화된 것
- chrome.exe (프로그램) -> 두 번 클릭(실행시킴) -> chrome process
- CPU 스케쥴링의 대상이 되는 작업(task)
프로세스 실행 과정
1. 프로그램이 메모리에 올라감
2. 인스턴스화 (프로그램 -> 프로세스)
3. CPU 스케줄러에 따라 CPU가 해당 프로세스 실행
프로그램
- 컴파일러가 컴파일 과정을 거쳐 컴퓨터가 이해할 수 있는 기계어로 번역되어 실행될 수 있는 파일
- 컴파일: 사람이 작성한 소스코드를 컴퓨터가 이해할 수 있게 기계어로 해석하는 과정 (프로그래밍 언어 -> 기계어)
컴파일 과정
1. 전처리
2. 컴파일러
3. 어셈블러
4. 링커
1. 전처리
- 소스 코드의 주석 제거
- #include 등 헤더 파일 병합하여 매크로 치환

2. 컴파일러
- 오류 처리
- 코드 최적화 작업
- 어셈블리어로 변환
- https://blog.itcode.dev/posts/2021/06/03/compiler-interpreter (컴파일러 vs 인터프리터)
3. 어셈블러
- 목적 코드(object code)로 변환
- 목적 코드: 프로세서가 이해할 수 있는 명령어의 형태 (<-> 원시코드)
- 1. 명령어 생성: 원시 프로그램에 있는 기호 명령어를 분석하여 기계어 명령어로 변경 ex) MOV -> 10110100(B4)
- 2. 기계 주소 할당: 원시 프로그램에 있는 기호 번지(변수)나 상수의 기억 장소(절대 번지)를 할당 ex) KOR -> [01000], 65 ->[0100001]
- 3. 의사 명령어 처리: 프로그램의 시작과 종료, 재배치 정보 등 프로그램의 안내자 역할을 하는 명령어들 처리 ex) START, END
4. 링커
- 프로그램 내의 라이브러리 함수 또는 다른 파일들과 목적 코드를 결합하여 실행 파일 생성
- .exe 또는 .out이라는 확장자
라이브러리
| 정적 라이브러리 | 동적 라이브러리 | |
| 방식 | 프로그램 빌드 시(컴파일 타임), 라이브러리가 제공하는 모든 코드를 실행 파일에 넣는 방식 라이브러리 내용 모두 메모리에 로드 |
프로그램 실행 시(런타임), 필요할 때만 DLL이라는 함수 정보를 통해 참조하는 방식 메모리에 이미 존재하는 경오, 로드 시간/공간 절약, BUT 매번 메모리에 접근해야함 -> 오버헤드 존재(느림) |
| 의존도 | 외부 의존도 낮음(시스템 환경 등) | 외부 의존도 높음 |
| 효율성 | 메모리 효율성 낮음(코드 중복 등) | 메모리 효율성 높음 |
프로세스 상태
1. 생성 상태
2. 대기 상태
3. 대기 중단 상태
4. 실행 상태
5. 중단 상태
6. 일시 중단 상태
7. 종료 상태
1. 생성 상태(create)
- 프로세스가 생성된 상태
- fork() or exec() 함수를 통해 생성됨
- PCB(Process Control Block)가 할당됨
fork()
- 부모 프로세스의 주소 공간을 그대로 복사
- 새로운 자식 프로세스를 생성하는 함수, 원래 프로세스는 기존의 하던 작업 실행
- -> 새로운 프로세스를 위한 메모리 할당
- -> fork()의 결과는 프로세스 하나가 더 생김 = PID(Process Id)가 완전히 다른 또 하나의 프로세스가 생김
- 단순 주소 공간만 복사
- 부모 프로세스의 비동기 작업 등을 상속 X
exec()
- 새로운 프로세스를 위한 메모리 할당 X
- exec()를 호출한 프로세스가 아닌 exec()에 의해 호출된 프로세스만 메모리에 남게됨
- exec()의 결과는 생성된 새로운 프로세스는 업속, exec()를 호출한 프로세스의 PID가 그대로 새로운 프로세스에 적용
- -> exec()를 호출한 프로세스는 새로운 프로세스에 의해 덮어 쓰여짐
2. 대기 상태(ready)
- 메모리 공간이 충분하면 메모리 할당 받음
- 충분하지 않으면 할당받지 않은 상태로 대기
- CPU 스케줄러로부터 CPU 소유권이 넘어오길 기다리는 상태
3. 대기 중단 상태(ready suspended)
- 메모리 부족으로 일시 중단된 상태
4. 실행 상태(running)
- CPU 소유권과 메모리를 할당받고 인스트럭션을 수행 중인 상태, CPU burst가 일어났다고 표현
- 인스트럭션: 컴파일된 소스 코드로 실행 파일 한 줄 한 줄의 명령어
5. 중단 상태(blocked)
- 어떤 이벤트가 발생한 후, 기다리며 프로세스가 차단된 상태
- I/O 디바이스에 의한 인터럽트로 자주 발생
- ex) 프린트 인쇄 버튼 클릭 시, 프로세스가 잠깐 멈추는 경우
6. 일시 중단 상태(blocked suspended)
- 대기 중단 상태와 유사
- 중단된 상태에서 프로세스가 실행되려고 했지만 메모리 부족으로 일시 중단된 상태
7. 종료 상태(terminated)
- 메모리와 CPU 소유권 모두 놓고 가는 상태
- 자발적 종료: 자연스럽게 종료됨
- 비자발적 종료(abort)
- ex) 부모 프로세스가 자식 프로세스를 강제로 종료
- ex) 자식 프로세스에 할당된 자원의 한계치를 넘어섬
- ex) 사용자가 process.kill 등의 명령어로 프로세스 종료
* 위의 7가지 단계에 대한 내용은 책의 그림 참고하면 좋을 것 같음 *


프로세스의 메모리 구조
1. 스택
2. 힙
3. 데이터 영역
4. 코드 영역
1. 스택(Stack)
- 지역 변수, 매개 변수, 함수가 저장됨
- 컴파일 시에 크기가 결졍되는 동적인 특징
- 함수가 함수를 재귀적으로 호출
- -> 동적으로 크기 증가
- -> 힙과 스태그이 메모리 영역이 겹치면 안되기 때문에 힙과 스택 사이의 공간은 비워둠
- 스택은 위의 주소부터 할당됨
2. 힙(Heap)
- 동적 할당할 때 사용됨 (new @@)
- 런타임 시 크기가 결정됨 (<-> 스택: 컴파일 시 크기 결정됨)
- ex) 벡터와 같은 동적 배열
- 아래의 주소부터 할당됨
3. 데이터 영역(BSS segment + Data segment)
- 전역 변수, 정적 변수 저장됨
- 정적인 특징을 갖는 프로그램이 종료되면 사라지는 변수들의 영역
- BSS 영역, Data 영역으로 나뉨
- BSS(Block Started by Symbol) 영역: 초기화되지 않는 변수가 0으로 초기화되어 저장됨
- Data 영역: 0이 아닌 다른 값으로 할당된 변수들이 저장됨
4. 코드 영역(code segment)
- 프로그램에 내장되어 있는 소스 코드가 들어있는 영역
- 수정 불가능한 기계어로 저장되어 있는 정적인 영역
PCB(Process Control Block)
- 프로세스에 대한 메타데이터를 저장한 데이터
- 프로세스가 생성되면 운영체제에서 해당 PCB를 생성
- 프로그램 실행
- -> 프로세스 생성
- -> 프로세스 주소 값들에 메모리 할당
- -> 프로세스의 메타데이터들이 PCB에 저장 및 관리됨
- 프로세스의 중요 정보이기 때문에 일반 사용자 접근 불가 -> 커널 스택의 가장 앞부분에서 관리
메타데이터
- 데이터에 관한 구조화된 데이터
- 데이터를 설명하는 작은 데이터
- 대량의 정보 가운데에서 찾고 있는 정보를 효율적으로 찾아내서 이용하기 위해 일정한 규칙에 따라 콘텐츠에 대해 부여되는 데이터
PCB 구조
1. 프로세스 스케줄링 상태
- '준비', '일시중단' 등 프로세스가 CPU에 대한 소유권을 얻은 이후의 상태
2. 프로세스 ID(PID)
- 해당 프로세스의 자식 프로세스 ID ???
- 유닉스, 맥 OS X 또는 마이크로소프트 윈도우 등의 운영 체제 커널이 사용되는 번호
- PID를 통해 어떤 한 프로세스를 일시적으로 식별 가능
3. 프로세스 권한
- 컴퓨터 자원 또는 I/O 디바이스에 대한 권한 정보
4. 프로그램 카운터(PC)
- 프로세스에서 실행해야 할 다음 명령어의 주소에 대한 포인터
5. CPU 레지스터
- 프로세스를 실행하기 위해 저장해야 할 레지스터에 대한 정보
- 레지스터: 컴퓨터의 프로세서 내에서 자료를 보관하는 아주 빠른 기억 장소
6. CPU 스케줄링 정보
- CPU 스케줄러에 의해 중단된 시간 등에 대한 정보
7. 계정 정보
- 프로세스 실행에 사용된 CPU 사용량, 실행한 유저의 정보
8. I/O 상태 정보
- 프로세스에 할당된 I/O 디바이스 목록
컨텍스트 스위칭(Context Switching)
- PCB 교환 과정
- 기존의 프로세스에 할당된 시간이 끝나거나 인터럽트에 의해 발생
- 다수의 프로그램이 동시에 실행되는 것처럼 보이지만, 실상은 어떠한 시점에서 실행되고 있는 프로세스는 단 1개
- 아주 빠른 속도로 컨텍스트 스위칭이 일어나고 있는 것
- 현대 컴퓨터는 멀티코어의 CPU를 갖고 있기 때문에 위의 설명은 틀린 설명이지만, 싱글 코어 기준으로 설명
- 컨텍스트 스위칭이 일어나는 과정에서 유휴시간(idle time) 발생
- 컨텍스트 스위칭에 발생하는 비용 존재(캐시미스)
- 프로세스 뿐만 아니라 스레드에서도 컨텍스트 스위칭 발생
- 스레드는 스택 영역을 제외한 나머지 모든 메모리 공간을 공유하기 때문에 보다 비용이 적고 시간이 적게 소요됨
캐시미스(비용)
- 컨텍스트 스위칭이 일어날 때 프로세스가 가지고 있는 메모리 주소가 그대로 있다면, 잘못된 주소 변환이 생기므로 캐시클리어 과정을 겪게 되는데 이 과정에서 캐시미스가 발생
멀티프로세싱
- 동시에 두 가지 이상의 일(프로세스)을 진행 가능한 것
- 하나 이상의 일을 병렬로 처리 가능
- -> 특정 프로세스의 메모리, 프로세스 중 일부에 문제가 발생하더라도, 다른 프로세스 이용가능하기 때문에 처리 가능
- -> 신뢰성 높음
- 웹 브라우저는 멀티프로세스 구조을 갖고 있음
ex) 웹 브라우저의 멀티프로세싱
1. 브라우저 프로세스
- 주소 표시줄, 북마크 막대, 뒤로 가기 버튼, 앞으로 가기 버튼 등을 담당하며 네트워크 요청이나 파일 접근 같은 권한 담당
2. 랜더러 프로세스
- 웹 사이트가 "보이는" 부분의 모든 것을 제어
3. 플러그인 프로세스
- 웹 사이트에서 사용하는 플러그인을 제어함
4. GPU 프로세스
- GPU를 이용해서 화면을 그리는 부분을 제어
IPC(Inter Process Communication)
- 프로세스끼리 데이터를 주고 받고, 공유 데이터를 관리하는 메커니즘
- ex) 클라이언트와 서버 간의 요청, 응답 과정
IPC 종류
- 메모리가 완전히 공유되는 스레드보다는 비교적 속도가 느림
1. 공유 메모리
2. 파일
3. 소켓
4. 익명 파이프
5. 명명 파이프
6. 메시지 큐
1. 공유 메모리(Shared Memory)
- 여러 프로세스에 동일한 메모리 블록에 대한 접근 권한이 부여되어 프로세스가 서로 통신할 수 있도록 공유 버퍼를 생성
- 원래 각 프로세스의 메모리에 다른 프로세스가 접근 불가능하지만, 공유 메모리를 통해 여러 프로세스가 특정 메모리 공유 가능
- 어떤 매개체를 통해 데이터를 주고 받는 방식이 아니라 메모리 자체를 공유함
- -> 불필요한 데이터 복사의 오버헤드 발생 X -> 가장 빠름
- -> 같은 메모리 영역을 여러 프로세스가 공유하기 때문에 동기화 필요
- ex) CPU가 접근할 수 있는 큰 랜덤 메모리인 RAM도 하드웨어 관점에서 공유 메모리
2. 파일
- 디스크에 저장된 데이터 또는 파일 서버에서 제공한 데이터
- 이를 기반으로 프로세스 간 통신
- 텍스트 파일(혹은 다른 포멧의 파일)을 통해 데이터를 주고 받는 것도 IPC 기법 중 하나
- -> 실시간으로 직접 원하는 프로세스에 데이터를 전달하는 것이 어려움 -> 디스크에서 데이터 파일을 읽고 프로세스에 적재되는 과정에서 컨텍스트 스위칭, 인터럽트 등의 일 처리 필요 -> 문제 많음
3. 소켓
- 동일한 컴퓨터의 다른 프로세스나 네트워크의 다른 컴퓨터로 네트워크 인터페이스를 통해 전송하는 데이터
- 서버와 클라이언트가 특정 포트를 통해 실시간으로 양방향 통신하는 방식
- 패킷 단위로 주고 받음으로서 직관적으로 이해하기 쉬움
- 서버 소켓: 서버 프로그램에서만 사용하는 소켓, 클라이언트로부터 연결 요청이 오기를 기다렸다가 연결 요청이 들어오면, 클라이언트와 연결을 맺고 다른 소켓을 만드는 일을 함
- 클라이언트 소켓: 대기하지 않고 클라이언트 소켓 생성, 클라이언트 프로그램에서 클라이언트 소켓은 서버 프로그램으로 연결 요청하는 것과 데이터를 전송하는 일을 함, 실제로 송수신이 일어나는 소켓
- TCP, UDP
4. 익명 파이프(Anonymouse pipe)
- 프로세스 간에 FIFO 방식으로 읽히는 임시 공간인 파이프를 기반으로 데이터를 통신
- 단방향 방식의 읽기 전용, 쓰기 전용 파이프를 만들어서 작동하는 방식
- 부모, 자식 프로세스 간에만 사용 가능, 부모와 자식 프로세스만이 파일 디스크립터를 공유하므로
- 다른 네트워크 상에서는 불가능
파일 디스크립터: 특정한 파일로 접근하기 위한 추상적인 키
5. 명명된 파이프(named pipe)
- 파이프 서버와 하나 이상의 파이프 클라이언트 간의 통신을 위한 명명된 단방향 또는 이중 파이프
- 클라이언트/서버 통신을 위한 별도의 파이프 제공
- 다수의 파이프를 동시 사용 가능
- 컴퓨터의 프로세스끼리 뿐만 아니라 다른 네트워크 상의 컴퓨터와 통신 가능 (지정한 이름으로 파일 디스크립터를 열 수 있기 때문)
- 서버용 파이프와 클라이언트용 파이프로 구분하여 작동
- 하나의 인스턴스를 열거나 다수의 인스턴스를 기반으로 통신
6. 메시지 큐(queue)
- 메시지를 큐(queue) 데이터 구조 형태로 관리
- 입출력 방식은 익명 파이프와 동일하지만, 커널에서 관리하며 메모리를 사용한 파이프, 구조체를 기반으로 통신
- 파이프와 다르게 데이터의 흐름이 아니라 메모리 공간
- 다른 IPC 방식에 비해 사용방법이 매우 직관적, 간단함
- 다른 코드의 수정 없이 몇 줄의 코드만 추가하여 간단히 메시지 큐에 접근 가능
- 공유 메모리(1.) 동기화 때문에 기능 구현이 매우 복잡하여, 이를 대안으로 메시지 큐 사용
보다 자세한 설명: https://doitnow-man.tistory.com/110
스레드
- 프로세스의 실행 가능한 가장 작은 단위
- 다수의 스레드를 가질 수 있음
- 코드, 데이터, 힙은 스레드끼리 서로 공유 -> 각각 생성하는 프로세스보다 비교적 빠름
- 그 외의 영역은 각자 생성
멀티스레딩
- 프로세스 내의 작업을 여러 개의 스레드로 처리하는 기법
- 스레드끼리 자원을 공유하기 떄문에 효율성 높음
- ex) 웹 요청 처리 시, 새로운 프로세스를 생성하는 대신 스레드를 사용하면 웹 서버의 경우 훨씬 적은 리소스 소비
- 한 스레드가 중단(blocked)되어도 다른 스레드는 실행(running) 상태 일 수 있어서 중단되지 않고 빠른 처리 가능
- 동시성 장점
- 한 스레드에 문제가 생기면 다른 스레드에도 영향을 끼칠 수 있음(자원을 공유하고 있기 때문) -> 스레드로 이뤄진 프로세스에 영향을 줄 수 있는 단점
동시성
- 서로 독립적인 작업들을 작은 단위로 나누고 동시에 실행되는 것처럼 보여주는 것
멀티스레드 예시(웹 브라우저의 렌더러 프로세스)
1. 메인 스레드
2. 워커 스레드
3. 컴포지터 스레드(레이어 합성)
4. 레스터 스레드(화면을 픽셀로 변환)
공유 자원(Shared Resource)
- 시스템 안에서 각 프로세스, 스레드가 함께 접근할 수 있는 모니터, 프린터, 메모리, 파일, 데이터 등의 자원이나 변수
- 2개 이상의 프로세스가 동시에 읽거나 쓰는 상황 == 경쟁 상태(race condition)
- 동시에 접근을 시도할 때, 접근의 타이밍이나 순서 등이 결과값에 영향을 줄 수 있는 상태 (동시성 문제)
임계 영역(critical section)
- 둘 이상의 프로세스, 스레드가 공유 자원에 접근할 때 순서 등의 이유로 결과가 달라지는 코드 영역
- 위 문제를 해결하기 위한 방법
- 1. 뮤텍스 2. 세마포어 3. 모니터
- -> 상호 배제, 한정 대기, 융통성이라는 조건 만족
- 잠금(lock) 메커니즘을 토대로 사용
1. 상호 배제
- 한 프로세스가 임계 영역에 들어갔을 때, 다른 프로세스는 들어가지 못함
2. 한정 대기
- 특정 프로세스가 영원히 임계 영역에 들어가지 못하면 안됨, 언젠가는 들어가야 함
3. 융통성
- 한 프로세스가 다른 프로세스의 일을 방해해서는 안됨
뮤텍스(Mutex)
- 프로세스나 스레드가 공유 자원을 lock()을 통해 잠금 설정하고 이용한 후, unlock()을 통해 잠금 해제하는 객체
- 잠금 또는 잠금 해제라는 상태만 가짐
세마포어(Semaphore)
- 일반화된 뮤텍스
- 간단한 정수 값 + 2가지 함수(wait, signal)로 공유 자원에 대한 접근 처리
- wait(), P 함수: 자신의 차례가 올 때까지 기다리는 함수
- signal(), V 함수: 다음 프로세스로 순서를 넘겨주는 함수
- 프로세스나 스레드가 공유 자원에 접근하면 세마포어에서 wait() 작업 수행
- 프로세스나 스레드가 공유자원을 해제하면 세마포어에서 signal() 작업 수행
- 조건 변수 없음, 프로세스나 스레드가 세마포어 값 수정할 때 다른 프로세스나 스레드 접근 불가
뮤텍스, 세마포어 차이점
- 뮤텍스는 동기화 대상이 하나 ex) 사람(프로세스, 스레드), 화장실(공유 자원), 화장실 키(공유자원에 접근하기 위한 필요 오브젝트)
- 세마포어는 동기화 대상이 여러개 ex) 사람(프로세스, 스레드), 화장실(공유자원), 화장실 빈칸 수(현재 공유자원에 접근 가능한 프로세스, 스레드 개수)
바이너리 세마포어
- 0과 1만 갖는 세마포어
- 구현상 뮤텍스와 유사
- 뮤텍스: 잠금을 기반으로 상호배제가 일어나는 "잠금 메커니즘"
- 세마포어: 신호를 기반으로 상호 배제가 일어나는 "신호 메커니즘"
- ex) 노래 -> 전화 -> 노래 중지, 통화 처리 인터페이스
카운팅 세마포어
- 다수의 값을 가질 수 있는 세마포어
- 여러 자원에 대한 접근을 제어
모니터
- 둘 이상의 스레드나 프로세스가 공유 자원에 안전하게 접근할 수 있도록 고유 자원을 숨기고, 해당 접근에 대해 인터페이스만 제공
- 모니터 큐를 통해 공유 자원에 대한 작업들을 순차적으로 처리
- 세마포어보다 구현 쉬움
- 상호 배제는 자동 <-> 세마포어는 상호 배제를 명시적 구현해야 함
교착상태(Deadlock)
- 2개 이상의 프로세스들이 서로가 가진 자원을 기다리며 중단된 상태
- 프로세스 A와 프로세스 B가 각자의 점유하고 있는 자원들을 요청하며 대기하는 상황
교착 상태 원인
1. 상호배제
2. 점유 대기
3. 비선점
4. 환형 대기
1. 상호 배제
- 한 프로세스가 자원을 독점, 다른 프로세스들이 접근 불가능
2. 점유 대기
- 특정 프로세스가 점유한 자원을 다른 프로세스가 요청한 상태
3. 비선점
- 다른 프로세스의 자원을 강제적으로 가져올 수 없음
4. 환형 대기
- 2개의 프로세스가 서로가 서로의 자원을 요구하는 상황
교착상태 해결 방법
1. 자원 할당시, 교착상태 조건에 성립되지 않도록 설계
2. 교착 상태 가능성이 없을 때만 자원 할당 -> 은행원 알고리즘 사용
3. 교착 상태가 발생하면 사이클 유무 확인 -> 관련 프로세스를 1개씩 삭제
4. 교착 상태 처리 비용이 크기 때문에, 교착 상태가 발생하면 사용자가 작업을 종료( 현재 운영체제 채택 방법 )
ex) "응답 없음"
은행원 알고리즘
- 총 자원의 양과 현재 할당한 자원의 양을 기준으로 안정 또는 불안정 상태로 나누고, 안정 상태로 가도록 자원을 할당하는 알고리즘
- 프로세스당 요청할 자원들의 최대치를 통해 자원 할당 가능 여부 확인