본문 바로가기
프로그래밍/컴퓨터 과학(CS)

2편] 멀티 프로세스, 멀티 쓰레드, 멀티 플렉싱에 대해서...

by Hwan2 2020. 9. 8.
728x90
반응형

앞전에 동기, 비동기, 블로킹, 논 블로킹에 대해 설명을 했습니다.


이와 이어지는 내용입니다.


예제의 이해를 쉽개 하기 위해서 통신할때를 가정으로 두고 진행하겠습니다.


1. 멀티 프로세스

말그대로 프로세스를 여러게 이용하는 것입니다. 
클라이언트당 프로세스 하나를 물리는 것이죠.


이런식으로 프로세스를 늘리는 것이죠. 

하지만 이런 방식은 굉장히 비효율적입니다. 프로세스 자체는 하나의 프로그램단위로 굉장히 큽니다.

또한 Context switching으로 인해 시스템 저하도 발생하죠.


흔히 알고있는 fork()로 만드는 것이 이 프로세스입니다. 

위와 같은 단점때문에 쓰레드라는 개념으로 통신을 하기 시작합니다.



2. 멀티 쓰레드

쓰레드는 프로세스보다 작은 단위이며 프로세스 안에서 논리적으로 동작하는 하나의 작업단위입니다.


프로그래밍에선 쓰레드를 하나의 task로 부르기도 합니다. 메모리 전체를 복사하는 프로세스와는 다르게,


쓰레드는 스택만 복사하며 나머지 힙, 코드영역, 데이터영역은 모두 공유를 하게되죠.


때문에 서로간의 통신할 때 전역변수나 힙메모리 영역을 사용합니다.


특히 전역변수를 사용할 때는 데이터의 일관성 때문에 mutex를 활용해 동기화를 시켜주죠.


이런 쓰레드를 통신할 때 클라이언트당 하나씩 일처리를 맡기는 것을 멀티 쓰레드라고 합니다.


즉 task를 쓰레드에게 1:1로 할당하는 것이죠.




해당 멀티 쓰레드 서버는 1:1, 1:10, 1:100 까지는 문제가 없습니다.


100개의 쓰레드를 관리하는 것도 사실상 문제가 되긴 하지만 간단한 채팅서버를 만들 경우 100명 정도는 충분히 커버가능합니다.


문제는 그 이상.... 1000개, 10000개 등.....


쓰레드를 사용할 때 가장 주의해야할 점은 Context Switching입니다. 이것들이 하나하나 모이면 엄청난 성능 저하를 일으키게되죠.


뿐만아니라 공유변수를 둘 경우 접근하려는 쓰레드 수가 많아질 수록 임계영역(critical section) 시간이 늘어나게 됩니다.


이렇게 쓰레드가 필요한 이유는 읽기, 쓰기 때문입니다. 


그래서 이 문제를 해결하기 위해 논 블로킹 개념이 등장하면서 멀티 플렉싱이 생겨납니다.



3. 멀티 플렉싱

멀티 플렉싱은 멀티 쓰레드에서의 읽기, 쓰기 쓰레드에 대한 문제점을 해결하기위해 등장했습니다.

서버에 접속한 각 클라이언트별로 read() 쓰레드를 할당할 경우 데이터가 오지않으면 계속 블로킹상태가 됩니다.

또한 쓰레드의 생성은 많은 비용이 듭니다. 이런 아까운 자원이 고작 읽기만을 위해 계속 기다리고 있는건 비효율적이죠.

쓰레드를 늘릴 수 있는 한계도 있습니다. OS마다 다르긴 하지만 대략 1000개 언저리입니다.

그래서 이런 블로킹되는 함수들을 운영체제에 도움을 받기로 합니다.

"클라이언트에게 read() 요청이 온다면 운영체제 너가 좀 알려줘라~~"
이런 식으로 말이죠.


이런식으로 쓰레드 하나가 여러개의 클라이언트를 관리할 수 있게 됩니다.


굉장히 효율적이죠. 쓰레드 하나로 모든 클라이언트를 관리할 수 있으니 말이죠.


하지만 이렇게 설계하는 것은 굉장히 코드가 복잡해 집니다.


논 블로킹을위해 핸들러를 만들어야하며, 콜백의 개념도 이해해야 합니다.


또한 하나의 쓰레드에게 작업량이 많아지는 것을 고려해 Thread poll을 만들어 task들을 분산처리해야 합니다.


이런 멀티플렉싱 기능을 해주는 함수들이 존제합니다.


C++에선 select(), IOCP, epoll, kqueue, asio::io_context 가 있습니다.


이 함수들 모두 운영체제의 도움을 받아 콜백 함수로 이벤트처리를 통해 논 블로킹을 구현하고 있죠.



다음 3편엔 위 함수들에 대해 알아보도록 하겠습니다.












반응형

댓글


스킨편집 -> html 편집에서