지난 포스팅에서 Process에 대해서 다루어 보았다.
Disk에 있는 Program을 CPU로 처리하려면 RAM에 메모리를 할당해야 한다. 메모리 할당은 Process 단위로 이루어진다. 메모리라는 독립적인 공간이 확보되면 CPU로 처리될 준비가 되었다고 볼 수 있다.
여기서 한 가지 고민해보자.
많은 프로그램이 병렬처리를 한다. 병렬처리를 구현하려면 어떻게 해야할까?
1) 멀티 프로세스
2) 멀티 스레드
멀티 프로세스 ( Multi-Process )
가장 단순하게,
병렬처리를 하려면 프로세스의 개수를 늘리면 된다.
프로세스는 독자적인 메모리를 부여 받으므로, 하나의 프로그램에 여러개의 독립적인 메모리를 할당받는 것이다. 멀티 프로세스 방식을 사용하는 대표적인 예는 Chrome 브라우저이다.
크롬 브라우저의 각 탭은 별도의 프로세스를 갖는다. 각 탭마다 메모리가 할당되므로 모두 독립적이다. 프로세스간 통신이 필요할 때만 IPC 로 통신할 뿐, 독립적으로 유지된다. 그러므로 한가지 탭에 문제가 생겨도 다른 탭에는 어떤 영향이 가지 않는다. 이와 반대로, 멀티 스레드 방식을 사용했던 IE는 한 탭에 문제가 생기면 전체 탭에 영향을 준다.
이와 같이, 프로세스마다 독립된 메모리 공간을 할당받는 멀티 프로세스 방식의 장점은 바로 안정성이다.
그러나 단점도 있다.
CPU는 한번에 하나의 프로세스만 처리 가능하다. 처리 프로세스를 프로세스A에서 프로세스B로 변경하면, CPU에서는 컨텍스트 스위칭(Context Swiching)이 발생한다. 컨텍스트 스위칭이란 CPU의 레지스터 및 캐시메모리 등의 자원을 프로세스A에서 프로세스B로 변경하는 작업을 의미한다. 이는 커널프로세스가 커널메모리에 저장된 PCB를 참고하며 이루어지는 보안상 중요한 작업이므로 안정성을 위해 CPU가 다른 일을 하지 못하도록 막는다. CPU가 다른 일을 못하고 문맥만 자꾸 변경되니 CPU의 오버헤드(overhead)가 증가한다.
이렇듯, 멀티프로세스 방식은 메모리 공간이 줄어들고 컨텍스트 스위칭도 잦아지는 문제가 있다.
멀티 스레드 ( Multi-Thread )
- 멀티스레드란?
하나의 프로세스 안에 다수의 흐름을 만들어 병렬처리를 하는 것이 멀티 스레드 방식이다.
어떠한 처리 흐름을 만들려면 독립적인 메모리 공간이 필요하다. OS가 프로세스에게 메모리를 할당하면 프로그램은 할당받은 메모리 공간을 나누어 스레드에게 할당한다.
위 그림은 OS가 할당한 프로세스 메모리 구조이다. heap, data, code 영역은 공통영역이고 stack 영역은 스레드 별로 나누어 독립적으로 할당한다.
메모리에 독립적인 공간이 생기면 CPU의 처리단위가 될 수 있다. 대표적인 멀티스레드 방식의 프로그램에는 JVM이 있다. JVM은 하나의 프로세스로 동작하면서 여러 개의 스레드를 둔다. 개발자가 작성한 코드는 Main 스레드에서 동작한다. 그 과정에서 동적으로 Heap 영역에 객체를 생성하는데, 해당 객체가 필요 없어지면 가비지컬렉터가 동작하여 제거한다. 이것이 가능한 이유는 가비지컬렉터는 또다른 스레드로 동작하고 있어서, Heap 영역을 서로 공유하고 있기 때문이다.
- 멀티 스레드의 장점
멀티 스레드 방식의 장점은 컨텍스트 스위칭 비용이 낮다는 것이다.
스레드도 하나의 처리 단위이므로, 스레드A에서 스레드B로 처리대상이 변경되면 컨텍스트 스위칭이 발생한다. 그러나 멀티 프로세스와 달리, 스레드들은 하나의 PCB 안에 공유영역을 갖는다. 이런 이유로 컨텍스트 스위칭 과정에서 많은 데이터를 그대로 유지할 수 있다. 대표적으로 TLB가 있다. TLB는 일종의 캐시 메모리로, CPU가 RAM 안에서 특정 페이지를 찾을때 사용되는 하드웨어이다. 만약 프로세스가 다르면, TLB를 모두 비워야 하는데 그 과정에서 오버헤드가 증가한다. 스레드 간에는 동일한 메모리 범위를 가지고 있기에, TLB를 비울 필요가 없으므로 컨텍스트스위칭 비용이 낮아진다.
- 멀티 스레드의 단점
스레드는 '공유'하기에 이점을 갖지만 '공유'하기에 단점도 갖는다. 대표적으로 '동시성' 이슈가 있다.
앞서서 Main 스레드와 가비지컬렉터 스레드는 heap 영역을 공유하기에, main 스레드가 만든 객체를 가비지 컬렉터가 제거할 수 있다고 말했다. 이렇듯 공유되고 있는 영역의 데이터는 여러 스레드에 의해 동시에 조작될 수 있다.
스레드A가 Heap 영역에 저장된 객체의 멤버변수의 데이터를 5로 변경하였다. 그리고 5를 인출하려고 하는데, Round Robin 스케줄링 정책에 따라 할당받은 CPU 사용시간을 다 써서, 스레드B에게 CPU가 넘어갔다고 가정해보자.
스레드B도 객체의 멤버변수에 접근하여 데이터를 10으로 변경하였다. 이후 CPU가 다시 대기하던 스레드A에게 컨텍스트스위칭 되었고 스레드A는 이전에 못했던 멤버변수X를 인출하려고 한다.
그런데 스레드B가 이미 멤버변수X를 10으로 변경하였다. 스레드A는 멤버변수X를 5로 변경하고 인출하려고 했지만, 그 사이에 스레드B에 의해 10으로 변경된 것이다. 이렇듯 CPU 소유권은 랜덤하게 변경되고 여러 스레드가 동시에 공유자원에 접근하기에, 데이터 정합성은 언제든 깨질 위험에 처하게 된다.
- 상호배제(Mutex)
공유 자원의 정합성을 지키려면 상호배제(Mutex)를 구현해야 한다.
상호배제는 공유자원을 임계영역(Critical Section)으로 설정하고 Lock을 지닌 스레드만이 해당 영역에 접근 가능하도록 구현하는 기술이다. Lock을 갖지 못한 스레드는 CPU를 할당받아도 해당 객체에 접근하지 못하고 대기해야 한다. 대표적으로 JVM은 syncronized 키워드를 사용하여 임계영역을 설정하고 Monitor 방식으로 상호배제를 구현한다. 자세한 내용은 아래 포스팅을 참고하면 된다.
멀티 스레드 환경은 동시성 이슈가 항상 따라다닌다. 동시성 이슈를 고려하지 못한 개발은 데이터 정합성에 문제를 일으키고 이는 크나큰 사고로 이어진다.
정리하면,
멀티 프로세스는 독립적인 메모리 공간이 있어 안정적이지만 컨텍스트 스위칭을 비롯한 자원 및 성능이슈가 발생할 수 있고 멀티 스레드는 공유하는 메모리 공간이 있어 컨텍스트 스위칭을 비롯한 많은 부분에서 좋은 성능을 낼 수 있지만 동시성 이슈와 같은 데이터 정합성 문제를 일으킬 수 있다. 이렇듯, 멀티 프로세스와 멀티 스레드는 각각의 장점과 단점을 지니고 있므로, 환경과 목적에 따라 적절히 선택되어 사용되어 진다.
참고자료
'CS > OS' 카테고리의 다른 글
[OS] 가상메모리(Virtual Memory)란? (0) | 2024.01.25 |
---|---|
[OS] 비동기(Async)와 동기(Sync) (0) | 2024.01.21 |
[OS] 프로세스 스케줄링 ( Process Scheduling ) (0) | 2024.01.17 |
[OS] PCB란 무엇일까? (0) | 2024.01.15 |
[OS] 프로세스(Process)란 무엇일까? (0) | 2024.01.15 |