멀티 프로세스와 멀티 스레드는 한 어플리케이션에 대한 처리 방식이다. 각각이 어떤 것이고, 장단점이 무엇인지 알아보자.
멀티 프로세스
❓ 멀티 프로세스, Multi Process
하나의 프로그램을 여러 개의 프로세스로 구성하여, 각 프로세스가 독립적으로 작업을 수행하는 것을 말한다.
보통은 하나의 프로그램 실행에 대해 하나의 프로세스가 메모리에 생성되지만, 부가적인 기능을 위해 여러 개의 프로세스를 생성하는 것이다.
내부 구조
멀티 프로세스의 내부를 보면, 하나의 부모 프로세스가 여러 개의 자식 프로세스를 생성함으로써 다중 프로세스를 구성하는 구조이다.
한 프로세스는 실행되는 도중 프로세스 생성 시스템 콜을 통해 새로운 프로세스들을 생성할 수 있는데, 다른 프로세스를 생성하는 프로세스를 부모 프로세스(Parent Process)라 하고, 다른 프로세스에 의해 생성된 프로세스를 자식 프로세스(Child Process)라 한다.
부모 프로세스와 자식 프로세스는 각각이 고유한 PID(Process ID)를 가지로 있다. 부모 프로세스는 자식 프로세스의 PID를 알고 있으며, 이를 통해 자식 프로세스를 제어할 수 있다. 반대로 자식 프로세스는 부모 프로세스의 PID, 즉 PPID(Parent Process ID)를 알고 있어, 이를 통해 부모와 통신이 가능하다.
fork()와 exec()의 차이점
한 프로세스가 다른 프로세스를 실행시키기 위해서 위 두 개의 시스템 콜을 사용할 수 있다. 그럼 둘의 차이점은 무엇일까?
fork()
우선 fork() 시스템 콜은 호출 시 새로운 프로세스를 위한 메모리를 할당한다. 그리고 fork()를 호출한 프로세스를 새로운 공간으로 전부 복사하고, 원래 프로세스는 원래 프로세스대로 작업을 실행하고, fork()를 이용해서 생성된 프로세스도 fork() 시스템 콜이 수행된 라인의 다음 라인부터 실행이 된다. 즉, 새로운 프로세스는 원래의 프로세스와 똑같은 코드, 똑같은 PC(Program Counter)를 가지고 실행된다.
exec()
exec()는 fork()처럼 새로운 메모리를 할당하지 않고, exec()를 호출한 프로세스 위에 exec()에 의해 호출된 프로세스가 덮어쓰여 메모리에 남아 실행된다.
즉, fork()의 결과는 PID가 다른, 똑같은 내용을 실행하는 프로세스가 하나 더 생기는 것이고, exec()의 결과는 PID가 같은 새로운 프로세스에 의해 기존 프로세스가 덮어 쓰여지는 것이다.
지금은 간단하게 살펴보았지만, 다른 포스팅을 통해 각각의 시스템 콜이 어떤 경우에 어떻게 쓰이는 지 자세히 작성할 것이다.
프로세스의 자원 공유
기본적으로 각 프로세스는 별도의 메모리 공간을 가지고 실행되기 때문에, 한 프로세스는 다른 프로세스의 변수나 자료구조에 접근할 수 없다. 하지만 다음과 같은 방법을 통해 프로세스가 다른 프로세스의 정보에 접근하는 것이 가능하다.
IPC, Inter-Process Communication
커널 영역에서 IPC라는 내부 프로세스간 통신을 제공하며, 프로세스는 커널이 제공하는 IPC 설비를 이용해서 프로세스간 통신을 할 수 있게 된다.
IPC는 다음과 같은 방식이 있다.
PIPE(Anonymous PIPE) | 익명 파이프 |
Named PIPE(FIFO) | 파이프 |
Message Queue | 메시지 큐 |
Shared Memory | 공유 메모리 |
Memory Map | 메모리 맵 |
Socket | 소켓 |
Semaphore | 세마포어 |
이 또한 다른 포스팅을 통해 좀 더 자세히 알아볼 것이다!
멀티 프로세스의 활용
웹 브라우저의 탭
부모 프로세스와 자식 프로세스는 독립적인 메모리 공간을 가지고 서로 다른 작업을 수행한다. 대표적인 예로 웹 브라우저의 탭을 들 수 있다. 각 브라우저 탭은 같은 브라우저 프로그램 실행이지만, 각기 다른 사이트를 실행하기 때문이다.
아래는 필자 컴퓨터의 작업 관리자이다. 하나의 프로그램인 크롬에 대해 한 개의 부모 프로세스와 여러 개의 자식 프로세스가 생성되어 있음을 확인할 수 있다.
그렇기 떄문에 여러 개의 탭을 띄운 뒤, 하나의 탭에서 F12로 개발자 도구를 열고 콘솔 탭에 while(1){} 라는 무한 루프 코드를 실행 시키면, 해당 탭에서는 클릭도 안 되고 먹통이 되지만, 다른 탭에서는 정상적으로 동작한다. 이는 탭마다 다른 프로세스로 동작하기 때문이다.
장점
프로그램 안정성
멀티 프로세스는 각 프로세스가 독립적인 메모리 공간을 가지므로, 한 프로세스가 비정상적으로 종료되어도 다른 프로세스에 영향을 주지 않는다. 따라서 프로그램 전체의 안정성을 확보할 수 있다.
예로 위에서 봤던 크롬의 탭을 들 수 있다. 크롬의 탭은 멀티 프로세스로 이루어지기 때문에, 하나의 탭이 비정상적으로 종료되더라도, 큰 문제가 아닌 이상 다른 탭에서는 문제 없이 동작한다.
시스템 확장성
멀티 프로세스는 각 프로세스가 독립적이므로, 새로운 기능이나 모듈을 추가하거나 수정할 때 다른 프로세스에 영향을 주지 않는다. 그래서 시스템 규모를 쉽게 확장할 수 있다.
예로 네트워크 분산 서버를 들 수 있다. 대규모 웹 서비스에서는 수많은 요청을 동시에 처리하기 위해 여러 대의 서버를 두고 로드 밸런서(Load Balancer)와 같은 장비를 이용하여 클라이언트 요청 트래픽을 분산 시킨다.
이때 여러 대의 서버는 컴퓨터 여러 대를 말할 수도 있고, 하나의 성능 좋은 컴퓨터에 여러 개의 서버 프로세스를 두는 것을 말하기도 한다. 멀티 프로세스의 상황은 후자이다.
이렇게 멀티 프로세스를 사용하여 여러 대의 서버에 요청을 분산시켜 처리함으로써 시스템 규모를 쉽게 확장할 수 있으며, 부가적으로 서버의 장애나 다운 타임을 최소화할 수 있게 된다.
단점
Context Switching Overhead
CPU는 다음 프로세스 정보를 불러오기 위해 메모리를 검색하고, CPU 캐시 메모리를 초기화하며, 프로세스의 상태를 저장하고 복구해야 한다. 이러한 비용을 Context Switching Overhead라 한다.
특히 프로세스는 각각 자원을 독립적으로 가지고 있기 때문에, 이러한 빈번한 Context Switching으로 오버헤드가 클 수 있다.
따라서 멀티 프로세스 환경에서는 Context Switching Overhead를 최소하하는 방법이 중요하다. 이를 위해 프로세스 수를 적정 수준으로 유지하거나, I/O 바운드 작업이 많은 프로세스와 CPU 작업이 많은 프로세스를 분리하여 관리하고, CPU 캐시를 효율적으로 활용하는 등의 방법을 고려해야 한다.
자원 공유 비효율성
멀티 프로세스는 각 프로세스가 독립적인 메모리 공간을 가지기 때문에, 프로세스 간 자원 공유가 필요할 경우 복잡한 통신 기법인 IPC를 사용해야 한다.
IPC는 그 자체로도 오버헤드가 발생한다. 예를 들어 파이프나 소켓과 같은 IPC 기법은 데이터를 복사하거나 버퍼링하는 과정에서 성능 저하가 발생하며, 코드의 복잡도도 증가시킨다.
멀티 스레드
❓ 멀티 스레드, Multi Thread
하나의 응용 프로그램의 하나의 프로세스에 여러 스레드를 구성해 각 스레드가 하나의 작업을 처리하는 것을 말한다. 스레드들이 공유 메모리를 통해 다수의 작업을 동시에 처리하도록 해준다.
멀티 스레드 활용 : 하나의 브라우저
위에서 멀티 프로세스는 웹 브라우저에서의 여러 탭이나 창이라고 하였다. 반면 멀티 스레드는 웹 브라우저의 단일 탭 또는 창 내에서 브라우저 이벤트 루프, 네트워크 처리, I/O 및 기타 작업을 관리하고 처리하는 데 사용된다고 볼 수 있다.
위 그림과 같이 사용자가 서버의 데이터베이스에 자료를 요청하는 동안 브라우저의 다른 기능을 이용할 수 있는 이유도 바로 멀티 스레드 기능 덕분이다. 즉, 하나의 스레드가 지연되더라도, 다른 스레드는 작업을 지속할 수 있게 된다.
장점
스레드는 프로세스보다 가벼움
스레드는 프로세스보다 가볍다. 이 이유는 다음과 같다.
- 스레드는 프로세스 내에서 생성되기 때문에 실행 환경을 설정하는 작업이 매우 간단하여 생성 및 종료가 빠르다.
- 스레드는 프로세스와 달리 스택 영역을 제외한 나머지 자원을 서로 공유하기 때문에 기본적으로 내장된 데이터 용량이 프로세스보다 작다.
이와 같은 이유로 스레드를 생성하고 제거할 때 프로세스 내부의 자원만 관리하면 되기 때문에 훨씬 가볍다.
자원 공유의 효율성
멀티 스레드는 하나의 프로세스 내에서 여러 개의 스레드를 생성하기 때문에, 코드, 데이터, 힙 영역에 대해 스레드 간 자원 공유가 가능하다. IPC와 같은 복잡한 기술 없이도 자원을 공유할 수 있기 때문에 훨씬 효율적이다.
Context Switching 비용 감소
프로세스, 스레드 모두 Context Switching Overhead가 존재하지만, 프로세스의 Context Switching Overhead보다는 훨씬 낮아 비용이 낮다.
단점
프로그램 안정성
멀티 프로세스에서는 각 프로세스가 독립적으로 동작하기 때문에 하나의 프로세스에서 문제가 발생해도 다른 프로세스에서 영향을 받지 않는다. 하지만 멀티 스레드 모델에서는 대부분의 메모리 공간을 공유하고 있기 때문에 하나의 스레드에서 문제가 발생하면 다른 스레드도 영향을 받아 전체 프로그램이 종료될 수 있다.
이는 프로그래머의 역량에 따라 극복이 가능하다. 예를 들어 스레드에 에러가 발생할 경우 적절한 예외 처리를 해놓는다던지, 에러 발생 시 새로운 스레드를 생성하거나 스레드 풀에서 잔여 스레드를 가져오는 방식으로 프로그램 종료를 방지할 수 있다. 다만 이렇게 추가적인 처리에 비용이 발생하게 된다.
디버깅이 어려움
멀티 스레드를 사용하면 여러 개의 스레드가 동시에 실행 되기 때문에, 각 스레드의 동작을 추적하기 어려울 수 있다.
예를 들어 코드를 디버깅하는 도중에 다른 스레드가 실행되어 예기치 않은 결과가 발생할 수 있으며, 어떤 스레드가 언제 어떤 자원에 접근하고, 어떤 순서로 실행되는지 등을 파악하기 어려울 수 있다.
따라서 스레드 간 상호작용과 동기화 기법을 잘 이해하고 디버깅 도구를 적극 활용해야 한다.
멀티 프로세스와 멀티 스레드의 공통점
멀티 프로세스와 멀티 스레드의 공통적인 장점과 단점이 있다.
장점
프로그램 병렬성
멀티 프로세스 및 멀티 스레드와 여러 개의 CPU 코어를 활용하여 둘의 시너지를 합치면, 다중 CPU 시스템에서 각 프로세스 및 스레드를 병렬적으로 실행하여 성능을 향상 시킬 수 있다.
단점
Context Switching Overhead
Context Switching을 수행하는 데에 있어서 프로세스와 스레드에 대해 추가 비용이 발생하는데, 이를 Context Switching Overhead라 한다.
- 프로세스 Context Switching Overhead
- CPU 캐시 초기화
- 프로세스 정보 저장 및 복구(메모리 전체 영역, register ..)
- 스레드 Context Switching Overhead
- 스레드 정보 저장 및 복구(register와 메모리 영역 중 stack만 교체)
위에서도 볼 수 있듯이, 스레드의 overhead가 더 작다.
공유 자원 동기화로 인한 성능 저하
프로세스와 스레드에서 공유 자원에 접근할 때, Race Condition이 발생할 수 있다.
❓ Race condition
Race 뜻 그대로 경쟁하는 상태, 즉 두 개의 프로세스(스레드)가 하나의 자원을 놓고 서로 경쟁하는 상황을 말한다.
좀 더 자세히 설명하자면, 두 개 이상의 프로세스 또는 스레드가 공통 자원을 동시에 읽거나 쓰는 동작을 할 때, 공통 자원에 대한 접근이 어떤 순서에 따라 이루어졌는지에 따라 그 실행 결과가 같지 않고 달라지는 상황을 말한다.
이러한 문제를 해결하기 위해 둘 이상의 프로세스(스레드)가 공용 자원에 동시에 접근하는 것을 막는 Mutual Exclution(상호배제)기법이 사용된다. 상호 배제 기법으로 다음 Critical Section(임계 영역)에 대해 Mutex(뮤텍스)와 Semaphore(세마포어)방식을 활용한다. 간단하게만 살펴보자.
❓ Critical Section, 임계 영역
멀티 프로세스(스레드) 프로그래밍에서 임계 영역은 공유 자원을 접근하는 코드 영역을 말한다.
Mutex, 뮤텍스
공유 자원에 대한 접근을 제어하기 위한 상호 배제 기법 중 하나로, 임계 영역에 진입하기 전에 락(lock)을 획득하고, 임계 영역을 빠져나올 때 락을 해제하여 오직 1개의 프로세스(스레드)만이 공유 자원에 접근할 수 있도록 한다.
Semaphore, 세마포어
뮤텍스의 상위호환으로, 동시에 접근 가능한 스레드 개수를 지정 가능하다. 세마포어 값이 1이면 뮤텍스와 동일한 역할을 하며, 값이 2 이상이면 동시에 접근 가능한 스레드의 수를 제어할 수 있다. 스레드가 임계 영역에 접근하기 전에 세마포어 값을 확인하고, 값이 허용된 범위 내에 있을 때만 락을 획득할 수 있는 방식이다.
하지만 상호 배제를 시행하면 교착 상태, 즉 Deadlock이 발생할 수 있다. 프로세스가 각자 프로그램을 실행하기 위해 두 자원 모두에 엑세스 해야 한다고 하면, 프로세스는 두 자원 모두를 필요로 하기 때문에 두 자원을 사용하여 프로그램을 수행하기 전까지 자신이 가진 자원을 놓지 않는다. 이러한 상황에서 두 프로세스는 서로 무한정 대기하는 교착 상태에 빠지게 된다.
해당 내용에 대해서도 다른 포스팅을 통해 좀 더 자세히 알아볼 것이다.
참고자료
Process Management
1. Process 란? 우리가 흔히 알고있는 프로그램은 해당 프로그램이 하드디스크에 존재할 때를 의미하고, 만약 그 프로그램이 실행되어 메인메모리로 올라오게 되면, 그 프로그램을 프로세스라고
gusdnd852.tistory.com
fork()와 exec()의 차이
fork()와 exec()는 모두 한 프로세스가 다른 프로세스를 실행시키기 위해 사용하게 됩니다.exec에는 execl, execv등 여러가지 함수군을 가지고 있습니다. exec의 함수군에 대해서는 아래쪽에서 차이를 간
jwprogramming.tistory.com
[운영체제] Race Condition과 예방할 방법(세마포어, 뮤텍스)
1. Race Condition에 대해 race condition이란 두 개 이상의 프로세스가 공통 자원을 병행적으로(concurrently) 읽거나 쓰는 동작을 할 때, 공용 데이터에 대한 접근이 어떤 순서에 따라 이루어졌는지에 따라
iredays.tistory.com
'Computer Science > Operating System' 카테고리의 다른 글
IPC, Inter-Process Communication (0) | 2024.04.15 |
---|---|
System Call : fork() & exec() (0) | 2024.04.15 |
JAVA의 Thread (0) | 2024.04.15 |
Thread, 스레드 (0) | 2024.04.15 |
CPU Scheduling, 스케줄링 (1) | 2024.04.15 |