[기술면접] 프로세스와 스레드, 그리고 싱글 스레드와 멀티 스레드

윤후·2022년 6월 21일
0

기술면접

목록 보기
5/28

프로세스와 스레드, 그리고 싱글 스레드와 멀티 스레드

JavaScript는 싱글스레드로 동작하는 언어이다.

프로세스

코어가 1개인 CPU는 본질적으로 한 순간에 1개의 일밖에 할 수 없다. 그런데 현대의 컴퓨터는 컴퓨터를 킨 순간부터 여러개의 작업을 수행한다. 물론 현대의 컴퓨터는 1코어보다는 멀티코어가 대중화되어 있긴하다. 내 맥북의 경우 CPU의 코어가 8개 이므로 내 맥북의 경우에는 한 순간 8개의 작업을 수행할 수 있겠다.

하지만 실상 보면 이 보다 더 많은 일들을 수행하는 것같다. 어떻게 이러한 일이 가능한 것일까?

우리가 컴퓨터에서 실행할 수 있는 파일 (MacOS의 경우에는 .dmg, window의 경우에는 .exe)파일들을 정적 프로그램 혹은 그냥 프로그램이라고 한다. 이 프로그램은 그저 코드의 덩어리일 뿐이고, 이것을 실행하는 순간 프로그램이 움직일 수 있는 공간이 생겨야한다.

이런 공간을 프로세스라고하며 프로그램을 실행 시켰을때, 프로그램이 메모리에 올라가게 되고, 할당되게 된다. 이렇게 인스턴스화된 프로그램 즉, 정적인 프로그램이 동적인 프로그램으로 변할 때 프로그램이 돌아가고 있는 상태를 프로세스라고 하는 것이다.

따라서 프로세스는 운영체게로 부터 자원을 할당받는 작업의 단위라고도 할 수 있다. 프로세스를 CPU 스케줄러에 맞춰 CPU에서 실행하게 되어 우리가 프로그램을 사용할 수 있게 되는 것이다.

프로세스의 상태(state)

프로세스는 상태(state)가 변경되며 작업을 수행하게 된다.

  • 실행
    CPU에서 실행 중인 상태.

  • 준비
    CPU를 할당 받기 위해 기다리는 상태.

  • 대기
    입출력 등의 이벤트 발생을 기다리는 상태.
    CPU를 주어도 당장 명령을 수행할 수 없는 상태.
    Process 자신이 요청한 event가 즉시 만족되지 않아 이를 기다리는 상태

  • 생성
    프로세스가 생성되어 메모리에 할당된 상태

  • 종료
    프로세스가 종료된 상태

프로세스의 메모리 구성

각각 프로세스는 CPU로 부터 메모리 공간을 할당 받는다. 이 할당된 메모리 공간 안에서 프로세스 자신만의 데이터와 명령어를 가질 수 있다. 따라서 하나의 프로세스에서 발생한 문제가 다른 프로세스에는 영향을 주지 않으며 각각의 프로세스는 독립적으로 실행된다.

하나의 프로세스는 위의 그림과 같이 코드, 데이터, 스택, 을 가지고 있다.

  • 코드
    프로그램 코드가 저장되는 공간이다. 실행할 프로그램의 코드나 명령어들이 기계어 형태로 저장된 영역이다. CPU는 코드영역에 저장된 명령어를 하나씩 처리한다.

  • 데이터
    전영 변수 등의 데이터가 저장되는 공간이다. 코드에서 선언한 전역 변수와 정적 변수가 저장되는 영역이며 프로그램이 실행되면서 할당되고 종료되면서 소멸한다.

  • 스택
    지역 변수와 함수 호출시 생성되는 스택 프레임이 저장되는 공간이다. 함수 안에서 선언된 지역변수, 매개변수, 리턴값등이 저장된다. 함수 호출시 기록되고 종료되면 제거된다.


  • 동적으로 할당된 메모리가 저장되는 공간이다. 관리가 가능한 데이터 이외의 다른 형태의 데이터를 관리하기 위한 자유공간이다.

프로세스간 통신 IPC

IPC는 Inter-Process Communication의 약자로 프로세스 간 통신을 의미한다. 운영체제에서 제공하는 기능으로 독립적인 특징을 가진 프로세스끼리 데이터를 주고 받을 수 있게 해준다. IPC를 위해서는 파이프, 파일, 소켓 등의 기능을 통해 프로세스간의 데이터를 주고 받을 수 있다.

  • 파일
    프로세스가 파일을 생성하여 데이터를 저장하고, 다른 프로세스가 이 파일을 읽어들이게 된다.

  • 파이프
    두 개의 프로세스 간에 단방향 통신을 위한 방식으로 사용된다.

  • 소켓
    네트워크를 통해 두 개의 프로세스 간의 양방향 통신을 위한 방식이다.

  • 메시지 큐
    운영체제에서 제공하는 큐를 이용해 데이터를 주고 받는 방식이다.

  • 공유 메모리
    두 개의 프로세스가 동일한 메모리 영역을 공유하여 데이터를 주고 받는 방식이다.

프로세스의 계층구조

운영체제에서 부모 프로세스가 자식 프로세스를 생성하면 이 자식 프로세스는 또 다른 자식 프로세스를 생성할 수 있는 계층구조를 형성한다. 계층 구조를 통해 프로세스는 계층적으로 관리할 수 있으며 부모 프로세스는 자식 프로세스의 실행 상태를 감시하고 제어할 수 있다.

프로세스의 Context Switching

하나의 시스템에서 여러개의 프로세스가 동시에 실행될 수 있으며 이러한 프로세스들을 CPU를 공유한다. 하지만, CPU는 동시에 하나의 작업만 처리할 수 있으므로 운영체제는 여러개의 프로세스들이 동시에 실행될 수 있도록 CPU를 번갈아가며 할당하는 작업이 필요하게 된다. 이러한 작업을 수행하는 것이 Context Switching이다.

Context Switching은 운영체제에서 멀티태스킹을 가능하게 하기 위한 기술이다. 싱글 코어 기반 컴퓨터는 많은 프로그램들을 동시에 실행하는 것처럼 보이지만, 어떠한 시점에서 실행되고 있는 프로세스는 단 한 개이며, 많은 프로세스가 동시에 구동되는 것 처럼 보이는 것은 다른 프로세스와의 Context Switching이 아주 빠른 속도로 실행되기 때문이다.

Context SwitchingPCB*를 교환하는 과정으로 한 프로세스에 할당된 시간이 끝나거나 인터럽트에 의해 발생하게 된다.

프로세스 A가 실행되다 멈추고, 프로세스 A의 PCB*를 저장하고 다시 프로세스 B를 로드하여 실행한다. 그리고 다시 프로세스 B의 PCB*를 저장하고 프로세스 A의 PCB*를 로드한다. 이러한 과정에서 Context Switching이 일어날때 유휴시간이 발생한다.

PCB*(Process Control Block)
운영체제에서 프로세스에 대한 메타 데이터를 저장한 데이터를 말한다. 프로세스 제어 블록이라고 한다.

프로그램이 실행되면 프로세스가 생성되고 프로세스 주소값들에 스택, 등의 구조로 메모리가 할당된다. 이 프로세스의 메타 데이터들이 PCB에 저장되어 관리된다.

프로세스 스케줄링 상태, 프로세스 ID, 프로세스 권한, 프로그램 카운터, CPU레지스터, CPU 스케줄링 정보, 계정정보, I/O상태 정보등을 PCB에 담고 있게 된다.

위의 과정을 예시로 설명해보자. 프로세스 A,B가 각각 있다고 하자.
롤을 하면서 op.gg를 보려고 한다. 이 경우 운영체제는 롤에서 실행중인 프로세스 A의 상태를 자신의 PCB에 저장하고 op.gg를 실행하기위한 프로세스 B의 상태를 PCB로 부터 불러와 CPU를 할당한다. 이 과정이 ConText Switching이 되겠다.

이제 op.gg가 실행되고 있는 동안 롤 관련 정보를 얻고, 다시 롤로 돌아오게 되면 운영체제는 이전에 저장한 롤의 상태를 불러와 CPU를 할당한다. 롤은 이전에 진행상태를 계속해서 보여주며 유저는 롤을 할 수 있게 된다.

이제 컴퓨터는 위에서 예시로 든 8개의 일보다 더 많은 일을 할 수 있게 된다. 하지만 이것만으로는 충분하지 않다. 예를 들어 웹 서버가 100명의 클라이언트밖에 수용할 수 없다면 하루 방문자수가 수천만에 달하는 곳들은 운영을 할 수 없을 것이기 때문이다. 즉, 프로세스안의 세분화된 또 다른 개념의 도입이 필요해진 것이다.

더 세분화된 작업을 처리하기 위해 컴퓨터는 프로세스마다 자원을 분할해서 할당하 스레드라는 개념을 도입하게 되었다.

스레드란?

스레드는 프로세스 안에서 만들어지는것으로, 프로세스들의 자원을 공유한다. 한 프로세서를 위해 일해야하므로 프로세스에 지정된 코드데이터, 을 공통적으로 접근하여 공통적인 업데트이가 가능하도록한다.

예로 들었던 웹 서버라는 프로세스가 멀티 스레드로 클라이언트에 대해 응답하는 방법을 살펴보면 아래와 같다.

여기에서는 크게 3가지의 스레드를 생성한다.

  • 클라이언트의 요청을 listen하는 스레드
  • 요청에 대해 서비스할 스레드
  • 추가적인 요청을 listen하는 스레드

즉 웹 서버에 동시에 여러 사람이 요청을 보내올 때 하나의 프로세스가 각각의 클라이언트에게 응대하는 것이 아니라, 각각의 반복적인 업무를 가지고 있는 여러개의 스레드를 생성하여 동시다발적으로 클라이언트에 대응하는 형태인것이다.

정리하자면 스레드는 프로세스에서 이뤄지는 여러가지 반복되는 일들을 세분화하여 작업을 수행하는 것이다.

스레드를 사용하면서 얻는 장점은 아래와 같이 있겠다.

  • 메모리 공유: 스레드는 프로세스 내에서 각각 stack만 따로 할당 받고 Code,Data,Heap영역은 서로 공유한다. 스레디는 한 프로세스 내에서 동작되는 여러 실행의 흐름으로 프로세스 내의 주소 공간이나 자원들을 같은 프로세스 내에서 스레드끼리 공유하면서 실행되게 된다.

  • 경량성: 프로세스에 비해 스레드는 더 적은 자원을 사용한다. 스레드는 프로세스 내에서 메모리와 자원을 공유하기 때문에 프로세스를 생성하는 것보다 더 적은 메모리를 사용한다. 또한 스레드 간의 전환은 프로세스 간의 전환보다 빠르기 때문에 더 경량적이다.

  • 상호 협력성: 스레드는 다른 스레드와 상호 협력하면서 작업을 수행할 수 있다. 프로세스간의 통신에서는 IPC기능을 통한 통신이 이루어지는 반면, 스레드는 Data, Heap영역을 공유함으로 데이터를 주고 받을 수 있다. 이러현 협력성은 프로세스와는 달리 더 높은 수준의 동시성을 제공해준다.

여기까지만 보면 스레드를 사용하면서의 장점만 돋보인다.
위에서 언급했듯 프로세스 안의 스레드는 코드, 데이터, 을 공통적으로 접근하여 공통적인 업데이트가 가능하다고 했다.

이 말은 장점이 될 수 있으나 단점이 될 수도 있는 말이다. 프로세스 안에서 공유되는 특정 변수를 스레드 2개가 동시에 손을 대게 되면 에러가 발생하는 것이다.

마치 2명의 개발자가 기존 코드를 변경 후, commit을 한 후 push했을 때, conflict가 발생하면서 resolve가 필요한것과 같은 상황인 것이다.

이러한 문제로 발생하는 에러를 예상하고 방지해야하기 때문에, 스레드를 사용하는 프로그래밍은 코드를 짜기도, 디버깅을 하기도 아주 까다롭다. 다행히도 이런 작업을 더 쉽고 안전하게 하기 위한 도구들이나 프로그래밍 방식들이 있다.

대표적으로 클로져를 사용하는 방법이나, 함수형 프로그래밍, Lambda 등이 있겠다.

싱글 스레드와 멀티 스레드


싱글 스레드와 멀티 스레드의 차이는 위의 그림을 통해서 차이점을 확연하게 볼 수 있다. 하나의 프로세스안에서 스레드가 한개이기 때문에 한개의 레지스터와 한개의 스택이 있는 것을 볼 수 있다.

반면 멀티 스레드의 경우에 여러개의 스레드가 존재하기 때문에 스레드의 갯수에 맞는 레지스터와 스택을 가지고 있는 것을 볼 수 있다.

그런데 여기서 의문이든다. 위의 예제에서는 하나의 프로세스에서 여러 스레드를 거쳐 복잡한 작업을 처리했다. 그렇다면 싱글 스레드의 경우에는 스레드가 하나이기 때문에 여러작업을 처리하지 못하고 단 한가지의 작업만 처리한다는 것인데, 왜 싱글 스레드를 사용하는 것이냐는 것이다.

하지만 언제나 현실은 그리 간단한 방법으로 흘러가지 않는 것. 싱글 스레드와 멀티 스레드 각각의 장점과 단점에 따른 비용이 들기 때문이다. 이 때문에 가장 효율적인 방법을 찾고 적용시키는 것이다.

스레드에대한 설명은 앞에서 했다. 싱글 스레드라고 하면 이름에서부터 짐작이 올 것이다. 그렇다. 하나의 프로세스에서 오직 단 하나의 스레드를 가지고 처리하는 것이다. 그렇기에 하나의 레지스터와 하나의 스택을 가지게 되는데 여기에서 위의 그림과 같이 순차 실행이라는 말이 나오게 된다.

순차 실행은 스레드를 하나만 가지고 작업을 처리하기 때문에 나타나는 특징인데 하나의 작업을 처리해야 다음 작업으로 넘어갈 수 있게 된다는 뜻이다.

즉, 작업 A가 끝나지 않으면 뒤에 있는 작업 B는 실행할 수 없게 되는 것이다.

하지만 멀티 스레드는 다르다. 각각의 스레드가 병렬적으로 일을 수행하기 때문에 여러가지 작업을 한번에 동시에 작업할 수 있게 되는 것이다.

이러한 작업은 컨텍스트 스위칭(Context Switching)을 통해 이뤄지게 된다. 스레드에서 다음 스레드로 이동하면서 컨텍스트 스위칭이 일어나게 되는 것이다. 이를 통해 한번에 완성되는게 아닌 부분적으로 조금씩 스레드의 작업을 끝내게 된다.

정리하자면 위의 그림처럼 2개의 스레드가 작업을 처리하는데, 스레드1이 A라는 작업을 부분적으로 처리하고 컨텍스트 스위칭이 일어나면 다음 스레드2가 B의 작업을 부분적으로 수행하게 된다. 이후 컨텍스트 스위칭이 일어나게 되고, 다시 스레드1이 이전에 했던 작업을 이어서 처리하게 되는 것이다.

이러한 컨텍스트 스위칭이 엄청나게 빠르게 일어나게 되면서 사람들의 눈에는 작업이 동시에 수행되는 것처럼 보이게 되는 것이다.

이러한 차이로인해 싱글 스레드와 멀티 스레드 장점과 단점이 나타나게 된다.

싱글 스레드

싱글 스레드의 장점

  1. 컨텍스트 스위치(context switching) 작업을 요구하지 않는다.

멀티 스레드는 CPU의 최대 활용을 위해 프로그램의 둘 이상을 동시에 실행하게 되는데 이러한 작업은 컨텍스트 스위칭(context switching)을 통해서 이뤄지게 된다. 이는 멀티 스레드의 장점이 될 수 있지만, 또한 단점이 될 수 있다.

컨텍스트 스위칭이 일어날때 프로세스가 하나의 프로세서*를 공유할때 발생하는 작업으로 생각보다 많은 리소스가 들기 때문이다.

프로세서*
프로세서 혹은 CPU는 제어장치, 연산장치, 레지스터 그리고 데이터 버스로 구성된 디지털 시스템의 핵심 부분으로, 프로그램을 기억장치로부터 읽어 연산 처리, 비교 처리, 데이터 전송, 편집, 변환, 테스트와 분기 등의 데이터를 처리하고, 각종 장치를 구동하는 역할을 한다.

  1. 자원 접근에 대한 동기화를 신경쓰지 않아도 된다.

여러개의 스레드가 공유된 자원을 사용할 경우 각 스레드가 원하는 결과를 얻게 하려면 공용 자원에 대한 접근이 통제되어야 하며 이 작업은 프로그래머에게 많은 노력을 요구하고 많은 비용을 발생시킨다.

모든 스레드가 일정 자원에 동시에 접근한다거나, 똑같은 작업을 실행하려는 경우 에러가 발생하거나 원하는 출력값이 나오지 않게 된다. 그렇기 때문에 스레드들이 동시에 같은 자원에 접근하지 못하도록 제어해줘야만 한다.

  1. 단순 CPU만을 사용하는 계산작업이라면 멀티 스레드보다 싱글 스레드가 더 효율적이다.

이 또한 컨텍스트 스위칭이 일어나면서 생기는 단점인데, 2개의 작업을 2개의 스레드가 처리한다고 했을 때, 2개의 스레드가 번갈아가면서 작업을 수행한다고 하면 더 빨리 수행할 수 있을 것이라고 생각한다. 하지만 컨텍스트 스위칭이 일어나면서 작업 전환이 일어나게 되면서 지연시간이 생기게 되고 결국 싱글 스레드보다 느린 경우가 생기게 되는 것이다.

다시 말해서 단순히 CPU만을 사용하는 작업은 싱글 스레드가 멀티 스레드보다 빠르게 처리가 가능하다는 것이다.

  1. 프로그래밍 난이도가 쉽고, CPU, 메모리를 적게 사용한다.

하나의 프로세스 상의 하나의 스레드이기 때문에 하나의 CPU만을 사용하며 이에 따른 메모리의 사용도 적어지기 때문에 컴퓨터의 리소스의 사용이 자연스럽게 줄어들게 된다. 또한, 멀티 스레드와는 다르게 프로세스 안에서 공유되는 자원이 없기 때문에 일정 자원에 대한 동시접근에 대해 통제를 두지 않아도 되기때문에 프로그래밍의 난이도가 좀 더 쉬워지게 된다.

싱글 스레드의 단점

  1. 여러개의 CPU를 활용하지 못한다.

프로세서를 최대한 활용하게 하려면 cluster모듈을 사용하거나 외부에서 여러개의 프로그램 인스턴스를 실행시키는 방법을 사용해야한다. 하지만 프로세스끼리의 자원 공유는 어렵기 때문에 Redis와 같은 부가 인프라가 필요하다는 단점이 있다.

  1. 연산량이 많은 작업을 하는 경우 그 작업이 완료되어야 다른 작업을 수행할 수 있다.

위의 싱글 스레드의 그림을 봤을때에도 설명했듯, 순차적인 실행으로 작업을 처리한다고 했다. A작업을 끝내야 B작업을 시작할 수 있는 것이다. 이러한 흐름은 앞선 A작업이 끝나지 않으면 B가 시작조차 될 수 없다는 것이다.

이러한 여러 선행 작업이 100개가 있고, 각 10초씩 걸린다고 하더라도 마지막 작업을 끝내기 위해서는 1000초의 시간이 걸리게 되는 것이다.

  1. 에러처리를 하지 못하는 경우 그 뒤의 작업은 All Stop

앞서 단점의 연장선이다. 순차적인 실행으로 작업을 처리하다 앞선 작업에서 에러가 발생하면 뒤의 작업들은 실행을 하지도 못한채 프로그램이 일부분 중단되거나 뻗게 되는 단점이 있다.


멀티 스레드

멀티 스레드의 장점

  1. 응답성이 좋다.

스레드중 하나가 중단되거나 긴 작업을 수행하더라도 다른 스레드의 작업이 계속 수행될 수 있기 때문에 사용자에 대한 응답성이 증가한다. 다시 말해 멀티 스레드는 에러가 발생하면 새로운 스레드를 사용하여 이를 핸들링 할 수 있게 되는 것이다.

예를 들어 멀티 스레드로 구성된 프로그램에서 하나의 스레드가 파일을 다운로드 받고 있는 동안 다른 스레드는 사용자와 상호작용하면서 응답을 할 수 있게 되는 것이다.

다만, 새로운 스레드를 생성하는데에 있어 리소스가 들게 되며, 에러를 대비하기 위한 스레드에 대한 비용이 발생하게 된다.

  1. 메모리 공간과 시스템 자원 소모가 줄어들게 된다.

사실 이 장점은 어느정도가 넘어가게 되면 장점이 아니게 되어 버린다. 앞서 싱글 스레드는 하나의 스레드이기 때문에 자원을 적게 소모하기 때문이라고 했다. 멀티 스레드의 경우엔 프로세스 내의 자원들과 메모리를 공유하기 때문에 메모리 공간과 시스템 자원의 소모가 줄어들게 되며, 스레드간 콘텍스트 스위칭은 캐시 메모리를 비울 필요가 없기 때문에 스레드간 통신이 필요한 경우에 쉽게 데이터를 주고 받을 수 있게 되겠다.

다만, 스레드가 많아지게 되면 공유되는 메모리 공간과 시스템 자원의 소모가 많아지기 때문에 너무 많은 스레드의 사용은 지양하는 것이 좋겠다.

  1. 멀티 프로세서의 활용이 가능하다

컨텍스트 스위칭에서 오는 장점이 되겠다. 다중 CPU 구저에서는 각각의 스레드가 다른 프로세서에서 병렬로 수행될 수 있으므로 많은 연산이 요구되는 작업에서 싱글 스레드보다 더 빠른 처리가 가능하다.

멀티 스레드의 단점

멀티 스레드의 단점은 싱글 스레드의 장점이 되겠다.

  1. 컨텍스트 스위칭과 동기화 등의 이유로 인해 싱글 코어에서의 멀티 스레딩은 스레드 생성시간이 오히려 싱글 스레드보다 느리다.

단일 코어에서(단순 CPU 만을 사용)는 컨텍스트 스위칭이 일어날때의 지연시간과, 공유된 시스템 자원과 메모리의 동기화로 인해서 스레드가 생성되는 시간이 느려질 수 있으며 결과적으로 싱글 스레드보다 느려지는 현상이 발생한다.

  1. 공유하는 자원에 대한 동기화를 신경써야 한다.

멀티 스레드는 싱글 스레드와는 다르게 데이터와 힙 영역을 공유하기 때문에 어떤 스레드가 다른 스레드에서 사용중인 변수나 자료구조에 접근하여 엉뚱한 값을 읽거나 수정되는 현상이 발생할 수 있다. 따라서 각 자원에 대한 동기화 작업이 필요하다.

  1. 프로그래밍의 난이도가 높으며 스레드가 많아지면 자원을 더 많이 사용하게 된다.

멀티 스레드의 경우 위의 2번의 단점의 연장선이 되겠다. 프로그래밍 시 프로세스 안에서 공유되는 자원에 대한 동기화처리가 매우 까다로우며 스레드가 많아지면 많아질 수록 리소스가 들게 된다. 또한 사용하지 않는 스레드에 대한 처리와 비용문제가 발생할 수도 있겠다.

멀티 스레드에서는 여러개의 스레드가 동시에 실행되기 때문에 디버깅이 어려울 수 있다. 스레드 간의 의존성의 복잡하게 얽혀 있을 경우 디버깅이 더 어려워지겠다.

profile
궁금한걸 찾아보고 공부해 정리해두는 블로그입니다.

0개의 댓글