[멀티 쓰레드] 프로세스와 쓰레드
Core
Central Processing Unit(이후 CPU)에서 Core는 연산을 처리하는 H/W 입니다. 때문에 CPU의 성능은 곧 Core의 단위시간(1초)에 얼마나 명령어를 처리하는지에 따라 판별됩니다. CPU 제조사들은 CPU의 성능을 높이기 위해 Core의 성능을 꾸준하게 높여왔습니다.
Core 내부 구조는 CU(제어장치), ALU(연산장치), Register(레지스터)로 구성되어 있고 (Multi Core CPU에서) 각 Core는 L1 Cache와 연결되어 있고 그 다음 차례로 L2, L3가 연결되어 있습니다.
Core 성능 향상과 함께 가정용 컴퓨터 보급이 확대되었습니다. 그러나 성능이 향상될수록 전기신호에 의한 발열 관리 문제가 심각해져 갔습니다. 특수목적(서버실 등)으로 사용되어 고성능 쿨러가 장착된 고성능 CPU는 발열을 잘 관리했지만 가정용 컴퓨터의 CPU 쿨러에 한계가 있었기 때문입니다.
따라서 제조사들은 가정용CPU의 성능 향상을 위해 단일 Core의 연산속도를 향상시키는 것이 아니라 Dual Core, Quad Core 등 Core의 개수를 늘려가는 방식(Multi Core)으로 성능을 향상 시켰습니다.
Multi Core가 가정용으로 보급되기 이전에도 병렬 컴퓨터 방식은 존재했습니다. Symmetric Multi Processor (SMP)라는 이름으로 여러 개의 CPU를 사용하는 방식이였습니다.
Multi Core CPU 역시 한 개의 작업을 빠르게 처리하기 위해 설계되었습니다. 그러나 Single Core에서 동작하던 프로그램을 Multi Core에서 그대로 실행하면 성능향상은 커녕 오히려 성능이 저하될 수 있습니다.
여러 개의 프로그램을 동시에 처리하려면 고가의 Multi Core CPU가 아닌 저가의 Single Core CPU를 여러 개 사용하는 것이 경제적인 측면에서 더 이득입니다.
Multi Core의 목적으로 알 수 있듯이 여러 Core가 함께 한 개의 프로그램을 빠르게 처리하기 위함이기 때문에 서로 다른 Core가 연산을 하면서 충돌이 발생할 수 있어 Single Core에서 설계된 프로그램으로는 Multi Core에서 성능향상을 이룰 수 없습니다. 따라서 Multi Core CPU에 맞춘 프로그램 방식이 필요하게 되었습니다.
Parallel Program
계산 영역을 나누어 각 영역을 여러 Core가 함께 수행 하기 위해 등장한 것이 병렬 프로그램입니다.
병렬 프로그램은 한 프로세스 내부의 여러 쓰레드에서 함께 실행됩니다. 여러 쓰레드가 함께 연산을 처리하기 때문에 실행되는 객체(Context) 사이의 협업(동기화) 가 필수 입니다. 병렬 프로그램은 크게 공유메모리 모델과 메시지 패싱 모델이 존재하며 앞으로 다룰 주제는 공유메모리 모델 입니다.
그러나 Core의 수만큼 항상 성능의 배로 향상되는 것은 아닙니다. 프로그래머가 원하는 만큼 Core의 개수를 조절하여 사용하는 것이 아니라 운영체제가 직접 관리하기 때문에 어느 상황에 Context Switch가 발생할지 예상할 수 없으며 어느 상황에 사용하능한 Context가 증가할지 모르기 때문입니다.
따라서 고성능 병렬 프로그램을 작성하기 위해서는 여러 조건들 중 정확성과 성능이 중요합니다. 여러 Context에서 동시 다발적으로 호출해도 정상 실행되는 알고리즘을 기반으로 Context 증가에 따른 성능 향상이 가능해야 합니다.
Process
보조기억장치에서 주기억장치로 복사되어 실행중인 프로그램 을 Process라고 합니다.
주기억장치로 복사된 프로그램인 Process의 메모리 구조는 5가지로 이루어져 있습니다.
영역 | 설명 |
---|---|
Code | 읽기 전용 데이터로 Process 코드는 기계어로 번역되어 있습니다. Process가 실행되는 동안 메모리의 고정된 위치에 있기 때문에 Data, Heap, Stack과 충돌이 없습니다. |
Data | 컴파일 타임에서 정의된 정적 변수와 전역 변수가 저장되어 있는 영역입니다. 내부는 초기화 변수 영역인 DATA와 초기화 되지 않은 변수 영역인 BSS로 구성되어 있습니다. |
Heap | 런타임에서 동적 메모리 할당에 의해 생성되는 변수들을 저장하는 영역입니다. |
Stack | 런타임에서 함수 호출로 이동할 때, 이동 전 지금까지의 실행 정보를 저장하고 함수가 반환되는 주소를 저장하며, 전역이 아닌 지역에서 선언되는 변수들을 저장하는 영역입니다. 모든 쓰레드는 고유 Stack을 소유합니다. |
PCB | 이 Process를 CPU가 관리하기 위해 필요한 정보들을 담아둔 영역입니다. |
(Windwos에서) Process를 생성할 때 운영체제가 (부모)Process를 생성합니다. 최초 Process는 자식 Process를 생성할 수 있으며 서로 트리 형태의 관계를 갖습니다. 자식 프로세스의 메모리 공간은 부모 프로세스와 독립된 공간에 있기 때문에 물리 주소와 가상 주소 모두 다른 값이 할당됩니다. 그리고 부모 프로세스는 자식 프로세스를 종료시킬 수 있지만 자식 프로세스는 부모 프로세스를 종료시킬 수 없습니다.
시분할 운영체제는 여러 Process를 고속으로 이동(Context Switch)하면서 연산을 처리해 컴퓨터 사용자에게는 동시에 실행되는 것처럼 보입니다.
Thread
프로세스 내부 실행 흐름 단위 를 Thread라고 부르며 프로세스가 생성되면서 1개가 함께 생성됩니다.
Thread는 또 다른 Thread를 생성할 수 있고 고유의 Stack을 소유합니다. 그리고 모든 Thread들은 Code, Data, Heap은 다른 Thread와 공유합니다. Thread의 고유 Stack의 경우 소유권이 없는 Thread가 접근할 수 있지만 데이터의 주소로 접근하여 수정하므로 데이터 변조 위험이 있어 주의가 필요합니다. 그리고 Heap에 생성된 동적 메모리를 생성한 Thread가 아닌 다른 Thread가 해제할 수 있지만 동적 메모리 관리(Memory Leak 등)에 문제가 발생할 수 있어 주의가 필요합니다.
마지막으로 Thread를 생성할 때 Kernel 호출이 필요하기 때문에 많은 자원이 소비되어 성능저하가 발생할 수 있습니다. 이 문제를 예방하기 위해서 최초 Thread를 생성한 뒤 프로그램이 종료될 때 까지 제거하지 않고 Thread를 재사용합니다.
(Windows에서) Process생성과 함께 Thread의 개수를 프로그래머가 직접 설정하는 것은 불가능합니다.
OS에 따라 Thread의 관리 방식에 차이가 존재합니다. 데스크탑에서는 CPU가 H/W 방식으로 관리하지만 핸드폰에서는 운영체제가 모든 Thread를 관리합니다.
게임 서버에서 생성한 Thread를 종료할 시기는 게임 서버가 종료되는 시점입니다. 또한 Thread의 재사용을 위해 병렬 프로그래밍 방법을 반드시 적용해야 합니다.
[Multi Process VS Multi Thread] [장점] -생성 Overhead가 적다.(프로세스 생성과 쓰레드 생성 속도의 차이) -Context Switch Overhead 가 적다.(쓰레드끼리는 같은 Virtual Memory, TLB switch Overhead) -Thread 간 통신이 프로세스의 통신보다 간단하다. [단점] -Multi Thread에서 단 한 개의 Thread에 문제가 발생한 경우 프로세스 자체가 문제가 됨. -디버깅이 어렵다. (누가 어디서 문제를 발생시켰는지 찾기 어려움.)