전에 심포지엄 주제로 멀티 프로세스와 멀티 스레드의 개념과 그 차이에 대해서 공부했었는데, 그때는 프로세스와 스레드가 뭔지 기본적인 개념 정도만 짚고 넘어갔었다.
그게 아쉬워서 오늘은 단순한 개념을 넘어, 프로세스의 상태 값들과 메모리 구조는 어떻게 되있는지, 그 각각의 구조는 뭔지 등을 공부해보면서 프로세스와 스레드에 대해 깊고! 자세하게 공부한 것을 기록해 볼 생각이다.
프로세스란?
프로세스(Process)는 컴퓨터에서 실행되고 있는 프로그램을 말한다.
* task (CPU 스케쥴링의 대상이 되는 작업) 라는 용어와 거의 같은 의미로 쓰인다.
문장으로만 보면 다소 추상적으로 느껴질 수 있기에 그림으로 예를 들어서 설명해보겠다.
위의 그림처럼, 프로그램이 메모리에 올라가면 프로세스가 되는 인스턴스화가 일어나고, 이후 운영체제의 CPU 스케줄러에 따라 CPU가 프로세스를 실행한다.
(CPU 스케줄러에 대해선 추후 포스팅 해볼 예정)
우리가 컴퓨터를 켜서 구글 크롬 프로그램을 실행시키는 과정을 생각해보자.
여기서 '프로그램'은 구글 크롬 프로그램(chrome.exe)과 같은 실행 파일을 의미한다. '프로세스'는 이를 두 번 클릭했을 때 구글 크롬 프로세스로 변환되는 것을 생각하면 이해하기 쉬울 것이다.
프로세스 상태 전이
프로세스가 시스템 내에 존재하는 동안 프로세스의 상태는 여러 가지 값으로 변할 수 있는데, 이를 '프로세스 상태 전이'라 한다.
🔹생성 상태 (create/new)
프로세스가 생성된 상태를 의미한다.
fork() 또는 exec() 함수를 통해 생성하고, 이 때 PCB가 할당된다.
fork()
- 부모 프로세스의 주소 공간을 그대로 복사하며, 새로운 자식 프로세스를 생성하는 함수
- 주소 공간만 복사할 뿐이지, 부모 프로세스의 비동기 작업 등을 상속하지는 않음
exec()
- 새롭게 프로세스를 생성하는 함수
🔹 대기 상태 (ready)
메모리 공간이 충분하면 메모리를 할당받고, 아니면 아닌 상태로 대기하고 있으면서 CPU 스케줄러로부터 CPU 소유권이 넘어오기를 기다리고 있는 상태이다.
🔹 대기 중단 상태 (ready suspended)
메모리 부족으로 일시 중단된 상태이다.
🔹 실행 상태 (running)
CPU 소유권과 메모리를 할당받고 인스트럭션을 수행 중인 상태를 의미한다.
이를 CPU burst가 일어났다고도 표현한다.
🔹 중단 상태 (blocked)
어떤 이벤트가 발생한 이후, 기다리며 프로세스가 차단된 상태를 말한다.
I/O 디바이스에 의한 인터럽트로 이런 현상이 많이발생하고는 하는데, 예를 들어 프린트 인쇄 버튼을 눌렀을 때 프로세스가 잠깐 멈춘 듯 할 때를 생각해보면 이해가 쉬울 것이다.
🔹 일시 중단 상태 (blocked suspended)
대기 중단 상태와 유사하다. 중단된 상태에서 프로세스가 실행되려고 했지만, 메모리 부족으로 일시 중단된 상태를 의미한다.
🔹 종료 상태 (terminated)
종료 상태는 메모리와 CPU 소유권을 모두 놓고 가는 상태를 말한다.
종료는 자연스럽게 종료되는 것도 있지만, 부모 프로세스가 자식 프로세스를 강제시키는 비자발적 종료(abort)로 종료되는 경우도 있다. 이는 자식 프로세스에 할당된 자원의 한계치를 넘어서거나 / 부모 프로세스가 종료되거나 / 사용자가 process.kill 등의 명령어로 프로세스를 종료할 때 발생된다.
프로세스의 메모리 구조
운영체제는 프로세스에 적절한 메모리를 할당한다. 그렇다면 어떤 구조로 메모리가 할당될까?
위에서부터 스택(stack), 힙(heap), 데이터 영역(BSS segment, Data segment), 코드(code segment) 영역으로 나눠진다. 스택은 위 주소부터 할당되고 힙은 아래 주소부터 할당된다.
🔹 스택과 힙
스택과 힙은 동적 할당이 되며, 동적 할당은 런타임 단계에서 메모리를 할당받는 것을 말한다.
- 스택: 지역 변수, 매개 변수, 실행되는 함수에 의해 늘어나거나 줄어드는 메모리 영역이다. 함수가 호출 될 때마다 호출될 때의 환경 등 특정 정보가 스택에 계속해서 저장된다.
- 힙: 동적으로 할당되는 변수들을 담는다. malloc(), free() 함수를 통해 관리할 수 있고, 동적으로 관리되는 자료 구조의 경우 힙 영역을 사용한다. ex) vector
🔹 데이터 영역, 코드 영역
이 영역은 정적 할당되는 영역이다. 정적 할당은 컴파일 단계에서 메모리를 할당하는 것을 말한다.
데이터 영역은 BSS segment와 Data segment, code/text segment로 나뉘어서 저장된다.
- BSS segment: 전역 변수 또는 static, const로 선언되어 있고 0을 초기화 또는 초기화가 어떠한 값으로도 되어 있지 않은 변수들이 이 메모리 영역에 할당된다.
- Data segment: 전역 변수 또는 static, const로 선언되어 있고 0이 아닌 값으로 초기화된 변수가 이 메모리 영역에 할당된다.
- code segment: 프로그램의 코드가 들어간다.
스레드란?
스레드(thread)는 프로세스 내에서 실행되는 여러 작업의 흐름을 지칭한다. 즉, 프로세스의 실행 가능한 가장 작은 단위라고 생각하면 되며 프로세스는 여러 스레드를 가질 수 있다.
스레드의 메모리 구조
Code, Data, Stack, Heap 을 각각 생성하는 프로세스와는 달리, 스레드는 프로세스 내에서 각각 Stack 만 할당 받고 Code, Data, Heap 영역은 공유된다.
즉, 같은 프로세스 내에 있는 여러 스레드들은 같은 Heap 공간을 공유하지만, 프로세스는 다른 프로세스의 메모리에 직접 접근은 불가하다.
이렇게 같은 프로세스 내 여러 스레드가 있고, 그 스레드끼리 자원을 공유하는 환경을 멀티스레드라 한다. 스레드끼리 자원을 공유하면 뭐가 좋을까?
멀티스레딩 환경의 장점
👉 당연히 효율성이 좋아진다.
예를 들어 웹 요청을 처리할 때, 새 프로세스를 생성하는 대신 스레드를 사용하는 웹 서버의 경우! 훨씬 적은 리소스를 소비하게 된다. 또, 한 스레드가 중단(blocked) 되어도 다른 스레드는 실행(running) 상태일 수 있기 때문에 중단되지 않는 빠른 처리가 가능하다.
하지만 자원을 공유함으로써 얻게 되는 단점도 존재할 것이다.
한 스레드에 문제가 생기면, 다른 스레드에도 영향을 미쳐 스레드로 이루어져 있는 프로세스에 영향을 줄 수 있다. 또한 멀티스레딩의 가장 큰 문제점인 자원의 공유 동기화 문제가 발생할 수 있다. (전역 변수 데이터 세그먼트를 이용하기 때문에)
동기화 문제는 매우 매우 중요하기에, 동기화 이슈로부터 발생할 수 있는 문제와 해결 방법 등등을 다음 포스팅에서 기술해보겠다!!
자바에서의 스레드
Java 프로그램은 JVM이 운영체제 위에서 작동하며 운영체제로부터 자원을 할당받아 프로그램을 실행한다.
JVM이 프로그램 시작점인 main() 메서드를 찾아 메인 스레드를 생성하고, 메인 스레드의 run()메서드를 통해 해당 main()메서드를 실행한다.
우리가 흔히 처음 프로젝트를 시작할 때 쓰는 public static void main(String[] args) { 을 통해 메인 스레드가 시작되게 되는 것이다.
이 시점에서 스레드는 메인 스레드 하나이기에 싱글 스레드라고 부를 수 있다.
(참고로 JVM 시작시 데몬 스레드도 시작되게 되는데, 로직을 실행하는 스레드는 메인스레드 하나이기 때문에 싱글 스레드라고 부른다.)
데몬 스레드
- 다른 스레드에 도움을 주는 스레드로, 대표적으로 가비지컬렉터가 있다. 메인스레드가 종료되면 데몬 스레드도 같이 종료된다.
아무튼 메인 메소드는 main() 메소드를 통해 실행되면 마지막 코드가 실행되거나 return문을 만나면 종료되게 된다.
이런 싱글 스레드 애플리케이션에서는 메인 스레드가 종료되면 프로세스도 그대로 종료되고 만다. 그러나 멀티스레드 애플리케이션에서는 실행중인 스레드가 하나라도 있으면 프로세스는 종료되지 않는다. 특히, 메인스레드가 작업 스레드보다 먼저 종료되어도 작업 스레드가 실행중이라면 프로세스는 종료되지 않는다.
[정리] 하루 일과를 프로세스와 스레드에 비유해보고, 차이 이해하기
필자의 하루 일과 ^_^
확실한 이해를 위해 가정을 해보겠다. 오늘 나와 당신이 할 일이 '밥 먹기, 운동하기, 공부하기' 라고 해보자.오늘 밥을 못 먹었다고 공부할 수 없는 것이 아니고, 운동을 못 해도 밥은 먹을 수 있으며, 공부를 못 해도 운동은 할 수 있다. 각각 서로 다른 일인 것이다. (그래도 운동이랑 공부는 밥 먹고 합시다 배고프니깐)
하지만 밥을 먹기 위해 해야하는 순서는 있다. 1. 밥 짓기 2. 반찬 준비하기 3. 수저 준비하기 중 하나를 하지 못했다면 '밥 먹기' 라는 일은 수행하기 어렵다.
이 쯤 되면 눈치챘을 것이다! 밥 먹기, 공부하기와 같은 일은 프로세스에 해당되고, 밥을 먹기 위한 일련의 작업들은 스레드에 해당된다. 또한 맛있는 밥을 먹기 위한 과정인 밥 짓기, 반찬 준비하기 등 이런 작업들을 동시에 처리하는 것을 멀티스레드 라고 이해하면 된다.
👉 결국 프로세스와 스레드의 가장 큰 차이점은 독립성이다. 각각의 프로세스는 다른 프로세스에 영향을 미치지 않지만, 스레드의 작업은 다른 스레드에 영향을 미칠 수 있다.
.
다음 글
[TIL][OS] 멀티 프로세스와 멀티 스레드의 차이, 컨텍스트 스위칭
같이 항해하는 친구들끼리 진행해보기로 한 CS 스터디! aka 심포지엄 (ㅋㅋ)이번에는 매번 읽고만 넘기고 자세히 짚어보지는 않았던 멀티 프로세스와 멀티 스레드의 차이에 대해 공부해보기로
developer-jinnie.tistory.com
Reference
https://gwanhyeon.github.io/OS-20201116-OS-Process-Thread/
[서적] 면접을 위한 CS 전공지식 노트
'Computer Science' 카테고리의 다른 글
[네트워크] HTTP의 4가지 특징 - 핵심 요약 총정리 (5) | 2024.12.26 |
---|---|
[TIL] 캐시와 Redis (Redis 특징과 장점, 사용 시 주의할 점) (0) | 2024.05.28 |
[TIL][CS] 운영체제(OS) 톺아보기 (0) | 2024.04.09 |
[TIL][CS] TCP/UDP와 동작 과정, TCP 3way handshake (0) | 2024.04.04 |
[TIL][CS] 트랜잭션과 ACID, 트랜잭션의 고립 수준 (0) | 2024.04.02 |