목차
- 용어 정리
- 프로세스
- 멀티태스킹(Multitasking)
- 프로세스 상태
- PCB(Process Control Block)
- 문맥 교환(Context Switching)
- 멀티 프로세스
- 스레드
- 스레드 구조
- 멀티 프로세스 VS 멀티 스레드
- GIL
- 요약 정리
프로그램(Program)
✔ 작업을 위해서 실행할 수 있는 파일(= 애플리케이션, .exe 파일)
= 하드디스크, SSD와 같은 보조기억장치에 저장되어 있는 실행코드와 정적인 데이터
- 작업관리자에서 확인한 현재 실행중인 여러 프로그램(.exe)
프로세서(Processor)
✔ 하드웨어 측면 = 프로그램을 수행하는 유닛
✔ 소프트웨어 측면 = 데이터 포맷을 변환하는 역할을 수행하는 데이터 프로세싱 시스템
ex) 컴파일러, 어셈블러,워드프로세서 등이 포함
- 프로세서(Processor) or CPU(Central Processor Unit)은 제어장치, 연산장치, 레지스터, 데이터 버스로 구성된 디지털 시스템의 핵심 부분
- 프로그램을 기억장치(메모리)로부터 읽어 연산처리, 비교처리, 데이터 전송,편집,변환, 테스트와 분기 등의 데이터를 처리하고, 각종 장치를 구동하는 역할
프로세스(Process)
✔ 실제 메모리에 적재되어 프로세서에 의해 실행되고 있는 컴퓨터 프로그램
= 실제 CPU를 차지해서 수행중인 프로그램
멀티태스킹(Muti-tasking)
- Task = 프로세스의 개념에서 조금 확장된 개념
- 이러한 Task가 하나의 프로세서 상에서 OS의 스케쥴링 방식에 따라 아주 빠른 시간사이에, 조금씩 번갈아가면서 수행되는 것
- 유저가 느끼기에는 마치 동시(Concurrency)에 처리되는 것 처럼 보임
- 실제로는 동시에 처리되는 것이 X
- Ex) PC로 멜론 노래들으면서 한컴타자연습하는 것
멀티태스킹 스케쥴링 방식
- 멀티프로그래밍 방식(Multi-programming)
- 시분할 방식(Time-sharing)
- 실시간 시스템 방식(Real-time)
멀티태스킹 종류
- 비선점형(Non-preemptive) 방식
- 선점형(Preemptive) 방식
프로세스 상태
프로세스 기본상태
- New(생성) = 프로세스가 막 생성된 상태
- Ready(준비) = 프로세스가 CPU에 실행되기 위해 대기중인 상태
- Running(실행) = 프로세스에 포함된 명령어가 실행되고 있는 상태
- Waiting(대기) = 프로세스가 특정 자원이나 이벤트를 기다리고 있는 상태
- Terminated(종료) = 프로세스가 실행을 완료한 상태
상태 전이 동작
- Ready ⇒ Running = (Dispatch) 우선순위가 높은 프로세스 선점해서 명령어 실행
- Running ⇒ Ready = 클럭이 인터럽트 신호를 발생시켜서 CPU제어권을 빼앗음, 선점(Preemptive)
- Running ⇒ Waiting = (Block) 프로세서가 입출력, 자원 등을 기다리기 위해 대기상태로 전환
- Waiting ⇒ Ready = (Wake Up) 입출력이 완료되거나 자원이 할당되어 다시 실행가능한 상태
PCB(Process Control Block)
PCB = 프로세스 제어 블록
⇒ 특정한 프로세스를 관리할 필요가 있는 정보를 포함하는 OS 커널의 자료구조
⇒ OS가 프로세스 스케쥴링을 위해, 프로세스에 관한 모든 정보를 가지고 있는 데이터베이스 같은 것
- 각 프로세스가 생성(New)될 때마다 고유의 PCB가 생성되고, 프로세스가 완료(=종료,Terminated) 되면, PCB는 제거됨
- 프로세스는 CPU를 점유해서 작업을 처리하다보니, 다른 프로세스의 상태 전이가 되면, 진행하던 내용들을 모두 정리하고 CPU를 반환해야 됨
- 이때 진행하던 작업들을 모두 저장하지 않으면, 다음에 다시 자신의 순서가 왔을 때, 어떠한 작업을 해야하는지 알 수 없는 사태가 발생
- 따라서, 프로세스는 CPU가 처리하던 작업의 내용들을 자신의 PCB에 저장하고, 다음에 다시 CPU를 점유하여 작업을 수행할 때, PCB에 있는 정보들을 CPU에 넘겨와서, 진행 중이던 작업을 이어서 진행함
PCB(Process Control Block) 안에 정보들
- 프로세스 식별자(Process ID, PID)
- 프로세스 상태(Process State)
- 생성, 준비, 실행, 대기, 완료 상태가 표시
- 레지스터(Register)
- CPU에서 명령이나 주소를 들고있는 CPU에 한 부분
- 카운터(Counter)
- 해당 프로세스가 다음에 실행할 명령어의 주소를 나타냄
- CPU 스케쥴링 정보
- 우선 순위, 최종 실행시각, CPU 점유시간 등
- 메모리 관리 정보(Memory limits(
- 포인터(Pointer)
- 부모프로세서에 대한 포인터, 자식 프로세스에 대한 포인터, 프로세스가 위차한 메모리 주소에 대한 포인터, 할당된 자원에 대한 포인터 정보 등
문맥 교환(Context Switching)
하나의 프로세스가 실행(Running)중이던 == CPU를 사용 중이던 상태에서 다른 프로세스가 CPU를 사용하도록 하기 위해, 이전에 프로세스의 상태(=문맥, State)를 보관하고, 새로운 프로세스의 상태를 적재하는 작업
시스템 콜(System Call)
: OS(커널)의 서비스를 호출하여 사용하는 것
응용 프로그램이 시스템 자원을 사용해서 작업을 하려고 하면 시스템 콜을 사용해야 함
입터럽트(Interrupt)
: 프로세서가 프로그램을 실행(Running) 도중 HW 또는 SW 문제 때문에 프로그램 실행 순서를 우선 순위가 급한 이벤트를 수행 후, 다시 원래 프로그램으로 복귀하여 나머지 프로그램을 수행하는 것
- Block( Running ⇒ Waiting ) 된 프로세스의 정보는, 해당 프로세스 PCB에 기록됨
문맥 교환 시점
- 멀티태스킹
- 인터럽트 핸들링
- 사용자 모드 ↔ 커널 모드 전환
프로세스 구조
크게, Stack, Heap, Data, Code 로 나뉜다.
- Stack(스택) : 지역변수, 임시데이터 관리
- Heap(힙) : 동적 메모리 관리 ( C의 malloc, Node, JVM의 힙 메모리)
- Data(데이터) : 전역변수, 초기화 된 데이터, 변수에 메모리 영역이 저장되는 공간
- Code(코드) : 실행 가능한 코드를 관리(컴파일 된 결과물)
스레드(Thread)
✔ 프로세스 내에서 실행되는 흐름의 단위, 실제로 작업을 수행하는 주체
- 모든 프로세스는 한 개 이상의 스레드(메인 스레드)가 존재하고, 작업을 수행함
- 또한, 2개 이상의 스레드를 가지는 프로세스를 멀티스레드 프로세스라고 함
스레드 구조
- 스레드는 Stack만 따로 할당 받음
- Code, Data, Heap 영역은 공유
- 각각의 스레드는 별도의 Register 와 Stack 을 갖고 있지만, Heap 메모리는 서로 읽고 쓸 수 있음
- 한 스레드가 프로세스 자원을 변경하면, 다른 이웃 스레드(Sibling Thread)도 그 변경 결과를 즉시 볼 수 있다. ( ★ 장점이자 단점 )
동시성 vs 병렬성
동시성(Concurrency)
- 동시성은 적어도 두 개의 스레드가 진행 중일 때 존재하는 조건임
- 시분할(Time-Slicing)을 포함한다.
- 우리가 "동시"라고 말하지만, 컴퓨터(코어, Core)는 실제로는 한번에 하나의 명령어만 처리할 수 있음
- 즉, 2개 이상의 알고리즘(프로그램)이 하나의 Core내에서 스레드간에 아주 빠르게, 교차하면서(Context Switching) 하면서 실행되기 때문에 "동시"라고 느끼는 것일 뿐임
병렬성(Parallelism)
- 적어도 2개 이상의 코어(Core)가 있어야 함
- 이는 쉽게 말해서, 뇌가 2개 이상이라고 생각하는 것이 좋을 것 같음
- 실제로 그래서, 컴퓨터를 코어(Core) 수에 따라
- 듀얼 코어(Dual-Core)
- 트리플 코어(Triple-Core)
- 쿼드 코어(Quad-Core)
- ....
- 병렬성도 동시성을 의미하지만, 동시성과의 차이는 각 코어내의 스레드가 실제로 동시에 명령어를 실행할 수 있음을 의미함
- 따라서, 두개의 알고리즘(프로그램) 정확히 같은 시점에 실행될 때, 우리는 병렬적이라고 함
관련 추가 조사할만한 내용
멀티 프로세싱 vs 멀티 스레싱 차이
멀티 프로세싱(Muti-Process)
하나의 응용프로그램을 여러 개의 프로세스로 구성해서, 각 프로세스가 하나의 작업(Task)을 처리하도록 하는 것
- 장점
- 여러 개의 자식 프로세스 중 하나의 문제가 발생하면, 그 자식 프로세스만 죽이면 그만이다.
- 단점
- 빈번한 문맥교환(Context Switching)에서의 오버헤드 발생
- 어떤 면에서 오버헤드라는 것인가 ?
- Context Switching 과정에서, 실제로 캐쉬 메모리 초기화 등 무거운 작업이 진행되고 많은 시간(실제로는 아주 작은 시간이겠지만..)이 소모되는 등의 오버헤드가 발생
- 프로세스는 각각의 독립된 메모리 영역을 할당받았기 때문에, 프로세스 사이에 공유하는 메모리가 없어, Context Switcing이 발생하면, 현재 캐시에 있는 모든 데이터를 내리고, 다시 캐시 정보를 로드(Load)해야 함
- 프로세스 사이의 어렵고 복잡한 통신 기법(IPC)
- 프로세스는 각각 독립된 메모리 영역을 할당받았기 때문에 하나의 프로그램에 속하는 프로세스들 사이의 변수 공유가 X
멀티 스레싱(Muti-Threading)
하나의 응용프로그램을 여러 개의 스레드로 구성하고, 각 스레드로 하여금 하나의 작업을 처리하도록 하는 것
멀티 프로세싱 대신 멀티 스레드를 사용하는 이유
쉽게, 프로그램을 여러 개 키는 것보다 하나의 프로그램 안에서, 여러 작업을 해결하는 것
여러 프로세스(멀티프로세스) 로 할 수 있는 작업들을 하나의 프로세스에서 여러 스레드로 나눠가면서 하는 이유는 ?
- 자원의 효율성 증대
- 프로세스를 생성하여 자원을 할당하는 시스템 콜이 줄어들어 자원을 효율적으로 관리할 수 있다.
- 멀티프로세싱의 경우, 프로세스 간의 Context Switching 시 단순히 CPU 레지스터 교체 뿐만 아니라 RAM 과 CPU 사이의 캐시 메모리에 대한 데이터까지 초기화되므로 오버헤드가 크기 때문
- 스레드는 프로세스 내의 메모리를 공유하기 때문에, 독립적인 프로세스와 달리 스레드 간 데이터를 주고 받는 것이 간단해지고 시스템 자원 소모가 줄어들게 된다.
- 처리 비용 감소 및 응답 시간 단축
- 프로세스 간의 통신(IPC)보다 스레드 간의 통신의 비용이 적으므로 작업들 간의 통신의 부담이 줄어든다.
- 이는, 스레드는 Stack 영역을 제외한 모든 메모리를 공유하기 때문
- 프로세스 간의 전환 속도보다 스레드 간의 전환 속도가 더 빠름
- 이는, Context Switching 시 스레드는 Stack 영역만 처리하기 때문
단, 이럼에도 다시 한번 멀티스레딩 사용시 주의점
- 동기화 문제
- 스레드 간의 자원 공유는 전역변수(Data Segment)를 이용하므로 함께 상용할 때 충돌이 발생할 수 있다.
(추가) GIL
GIL(Global Interpreter Lock)
Python 인터프리터는 GIL을 가지며, 이로인해 한 번에 한 스레드만 파이썬 바이트 코드를 실행 하도록 제한함
- 각 스레드는 다른 스레드의 의해 GIL이 해제되길 "기다린" 후에야 실행 될 수 있음
- 즉, 멀티 스레드로 만들었음에도 불구하고, 본질적으로 싱글스레드로만 작동하는 것
- 코어(Core) 수는 점점 늘어만 가는데, 이 GIL 때문에 코어 수만큼 이용할 수는 없지만( 즉, 효율성이 떨어질 순 있지만 )
- 메모리 관리 방식에서 GIL 덕분에 오버헤드가 적다.
정리하자.
- 자원은 Process 단위로 받고, 작업/스케쥴링은 Thread 단위로 진행
- 프로세스
- 프로세서에 의해 동작하고 있는 프로그램
- 프로세스가 동작 한다는 것은 프로세스의 특정 스레드가 실행 중이고 그 특정 스레드는 프로세스가 가진 데이터를 참조함
- 스레드 단위 작업을 지원하기 위한 자원 할당의 단위
- 스레드
추가 조사할만한 주제
- 스케쥴링이란 ? / 스케쥴링 기법
- 다중화 방식
- IPC(Interprocess Communication)