프로세스와 스레드
프로세스
- 프로그램을 메모리 상에서 실행중인 작업단위(메모리에 올라와
실행되고 있는 프로그램의 인스턴스
) - Code, Data, Stack, Heap 구조로 되어 있는 독립된 메모리 영역
스레드
- 경량 프로세스, 프로세스 하나에는 최소한 한 개의 스레드가 있음
- 프로세스가 할당받은 자원을 이용하는 실행의 단위
- 스레드는 프로세스에서 stack만 따로 할당받고 code, data, heap 영역은 공유한다.
- 스레드는 한 프로세스 내에서 동작되는 여러 실행의 흐름으로, 프로세스 내의 주소 공간이나 자원들(힙 공간 등)을 같은 프로세스 내에서
스레드끼리 공유하면서 실행
된다.- 👉 동시성 문제 발생의 원인
Process에서의 데이터 구조
위에서도 살펴보았듯이 프로세스는 Code, Data, Stack, Heap
으로 구성되어 있다.
이제 여기에 대해서 세부적으로 살펴보자면
- 코드 영역 : 코드영역은 텍스트 영역이라고도 부르며 말 그대로 실행할 수 있는 코드, 즉 기계어로 이루어진 명령어가 저장되는 공간을 의미한다.
- 데이터 영역 : 잠깐 썻다가 없앨 데이터가 아닌 프로그램이 실행되는 동안 유지할 데이터가 저장되는 공간을 의미한다..
- 예를 들어
전역변수
가 여기에 해당된다.
- 예를 들어
- 힙 영역 : 프로그래머가 직접 할당하는 공간. 메모리 누수를 일으킬 수 있는 공간.
- 스택 영역 : 각 스레드 1개당 1개가 생성되는 공간, 데이터를 일시적으로 저장하는 공간을 의미하며
매개 변수, 지역 변수
가 여기에 해당한다.
Spring에서의 쓰레드
- 자바 메인 메서드를 처음 실행하면 main이라는 이름의 쓰레드가 실행
Thread 단점
- Thread는 생성비용이 너무 많이 들어서 컨텍스트 스위칭 비용이 발생한다.
- Thread 생성에 제한이 없다.
- 고객 요청이 너무 많이 오면 CPU, 메모리 임계점을 넘어서 서버가 죽을 수 있다.
Thread Pool
기본적으로 Thread Pool이라는 곳에서 Thread를 여러개 생성을 해놓는다. (스레드 생성 비용을 줄이기 위해서)
Pool을 초과하는 요청에 대해서는 대기하거나 거절하는 방식으로 처리한다.
사용방법
쓰레드가 필요하면, 이미 생성되어 있는 쓰레드를 쓰레드 풀에서 꺼내서 사용한다. 사용을 종료하면 쓰레드 풀에 해당 쓰레드를 반납한다.
실무 팁
- WAS의 주요 튜닝 포인트는 최대 쓰레드수이다.
- 너무 적으면 서비스가 죽고
- 👉 요청은 100개 들어왔는데 쓰레드 풀이 10개면 사실상 90명한테는 서비스가 안되는 상태
- 너무 많으면 서버가 죽는다.
- 👉 쓰레드 풀이 10000개인데 요청이 10000개 들어왔을 때 서버의 CPU, 메모리 한계를 초과하면 서버가 죽는다.
그럼 어떻게 튜닝을 하지?
- 성능 테스트
- 툴 : 아파치 ab, 제이미터, nGrinder
PCB
프로세스의 정보를 담고 있는 운영체제에서 사용하는 자료구조, 프로세스 한 개당 하나씩 존재한다. 메모리에서 프로세스는 User 영역에, PCB는 Kernel 영역에 존재한다.
PCB에 저장되는 정보
- Process State (프로세스 상태)
- Program Counter (프로그램 실행주소, 가령 가장 마지막으로 실행한 주소가 어디인지를 나타냄)
- CPU Registers (Register의 상태)
- CPU-scheduling information
- Memory-management information
- Accounting Information (실행한 유저정보)
- IO status information (어떤 자원을 락을 걸어놨는지, 어떤 자원을 보고 있는지)
컨텍스트 스위칭이란
간단하게 말하자면 프로세스가 실행되고 종료될 때 일어나는 작업을 말하지만 좀 더 자세히 말하자면
CPU의 한 코어에 다른 프로세스가 들어오게 될 때 실행중이던 정보를 실행중이던 프로세스의 PCB에 저장하고, 새로 실행될 프로세스의 PCB에 저장된 정보를 CPU에 불러오는 작업을 말한다.
프로세스 생성 기법
프로세스는 다른 프로세스를 생성할 때 fork
라는 시스템 콜을 해서 자기 자신을 복제하고 exec
시스템 콜을 통해서 새로운 프로그램을 실행하는 방법을 사용한다.
파이썬 Process처리
from multiprocessing import Process
import os
def foo() :
print('child process', os.getpid())
print('my parent is', os.getppid())
if __name__ == '__main__' :
print('parent process', os.getpid())
child = Process(target=foo).start()
실행결과
parent process 28150
child process 28161
my parent is 28150
멀티 프로세스
def foo() :
print('child process', os.getpid())
print('my parent is', os.getppid())
def bar() :
print('child process', os.getpid())
print('my parent is', os.getppid())
def baz() :
print('child process', os.getpid())
print('my parent is', os.getppid())
if __name__ == '__main__' :
print('parent process', os.getpid())
child = Process(target=foo).start()
child = Process(target=bar).start()
child = Process(target=baz).start()
입출력이 동시에 실행되어서 어질어질하게 출력이 되는것을 볼 수 있다.
실행결과
parent process 32542
child process 32553
my parent is 32542
child processchild process 32554
32555my parent is
my parent is32542
32542