프로세스와 스레드, 스레드가 나타난 이유와 이점

Yukicow·2024년 4월 9일
0

스레드와 프로세스는 개발 공부를 함에 있어서 매우 중요한 내용이다.

이전에 Java의 Virtual Thread에 대해 공부했는데 스레드와 프로세스에 대해 잘 모르는 사람들이 있지는 않을까, 또 나는 제대로 알고 있는가? 에 대해 의문이 생겼다.

이번에 스레드와 프로세스를 박살내 보는 시간을 가져 보겠다.





프로세스

프로세스란 무엇일까?

개발자는 프로그래밍을 통해서 프로그램을 만들어 낸다.

즉, 프로그램이란 개발자가 구축한 명령어들의 집합이 되고 이러한 프로그램이 운영체제 의해 실행되면 프로세스가 된다.

이러한 프로세스는 운영체제에서 실행의 단위에 해당하기도 한다.



스레드가 나온 이유

그렇다면 스레드는 어쩌다가 나오게 된 것일까?

스레드가 없던 시절을 생각해 보면 운영체제에서 모든 실행의 단위는 프로세스였을 것이다.

하지만 프로세스는 각기 다른 독립적인 메모리 공간을 할당 받아 동작하기 때문에 운영체제가 프로세스를 다루는 것은 매우 많은 자원을 소모하는 행위였다.

이러한 프로세스를 사용하는 것이 어떤 경우에 효율이 나쁜지 알아보도록 하자.

1. 멀티 프로그래밍

옛날에는 하나의 컴퓨터에서 하나의 프로그램만을 실행할 수 있었다.

다른 프로그램을 실행하고 싶다면 컴퓨터를 끄고 새로운 프로그램을 새롭게 시작해야 했다고 한다.

이러한 문제를 해결하고자 하나의 CPU로 여러 개의 프로그램을 실행시키기 위한 방법을 생각해냈다.

바로 여러 프로그램을 병행 처리 하는 것이다.

병행처리라는 둘 이상의 작업을 한 꺼번에 처리하는 것을 의미한다.

한 꺼번에 처리한다고 해서 동시에 수행한다는 뜻은 아니다.

순차적으로 여러 가지 일을 수행하여 두 가지 이상의 일을 함께 수행해가는 것이다.

병행과 많이 헷갈려 하는 단어로 병렬이라는 단어가 있다. 병렬은 말 그대로 동시에 다른 작업을 수행하는 개념이다.

A와 B라는 작업이 있을 때 A와 B를 동시에 수행하면 병렬 A를 했다가 B를 했다가 순차적으로 이것저것 처리하면 병행이라는 뜻이다.

CPU는 연산처리를 하다가 I/O 상황이 발생하면 해당 I/O가 끝날 때까지 대기하는 상황이 발생한다.

이렇듯 컴퓨터 시스템이 실제로는 사용 가능한 상태이나 작업이 없는 시간을 유휴 상태라고 표현한다.

CPU를 최대한 효율적으로 사용하기 위해서는 이러한 유휴 상태를 최대한 없애는 것이 좋다.

그래서 병행 처리를 통해 특정 프로세스가 유휴 상태에 빠지면 다른 프로세스가 CPU를 사용하여 연산할 수 있도록 하였고 그것이 멀티 프로그래밍이다.

CPU의 수를 물리적으로 늘리지 않는 선에서 여러 프로그램을 돌리는 방법은 병행 처리밖에 없다. 병렬이란 개념은 연산처리가 가능한 물리적 요소가 추가되어야만 성립되는 개념이다.

2. 멀티 태스킹

하지만 위의 방식에는 한계점이 존재했다.

멀티 프로그래밍에서 특정 프로세스가 유휴 상태에 잘 빠지지 않는 경우에 문제가 생겼다.

다른 프로세스가 동작하기 위해서는 먼저 CPU를 선점한 프로세스에서 CPU 사용을 멈추어야 하는데 해당 프로세스가 CPU 연산을 너무 필요로 하는 프로세스여서 놓아 주질 않는 상황이 발생하는 것이다.

이러한 문제점을 해결하고자 멀티 태스킹이라는 개념이 등장하였다.

멀티 태스킹이란 프로세스의 연속성을 차단하는 개념이다.

보통은 태스크를 짧게 나누어 번갈아 가며 실행하는 것으로 설명을 하지만 원래의 본질적인 의미로는 짧게 나눈다 라는 개념 보다는 연속적이지 않게 한다 라는 개념이 맞다는 듯 하다. ( 아닐 수도 있음ㅋ )

위와 같이 프로세스 하나가 CPU를 연속적으로 점유하는 것을 막아, 모든 프로세스가 공평하게 작업을 수행하여 동시에 실행되는 것처럼 가능하게 한다.

3. 스레드

이제 여러 프로세스를 하나의 CPU에서 수행하는 것은 가능해졌는데, 아직도 문제점이 남아 있었다.

위에서 말했듯 프로세스는 각자 독립적인 메모리 공간을 갖고 동작한다.

만약에 같은 프로그램 내에서 동시에 실행해야 하는 로직이 있다고 하면 어떻게 해야 할까?

3-1 순차적으로 처리하도록 프로그래밍 한다

스레드라는 개념이 없는 경우 자동으로 컨텍스트 스위칭이 발생하지 않기 때문에 직접 작업에 대한 관리를 코드로 해결 해야 한다.

가장 간단한 방법으로는 왼쪽의 그림처럼 하나의 작업이 끝나고 나머지 하나를 실행하도록 구현하는 방법이 있다. 하지만 이는 CPU의 유휴 시간이 길어질 수 있어 비효율적이다.

A작업에서 CPU에 대한 유휴 시간이 발생해도 B작업을 처리할 수 없기 때문이다. B는 A가 완전히 끝난 후에 동작한다.

그렇다면 이러한 유휴 시간을 줄이기 위해 A에서 유휴 시간이 발생하는 지점을 예측하고 B작업을 수행하도록 오른쪽 그림과 같이 개발해야 하는데, 이는 현실적으로 프로그래밍 기법으로는 불가능해 보인다.

3-2 프로세스를 여러 개 실행한다

다음은 여러 작업이 운영체제에 의해 관리될 수 있게 프로세스를 여러 개 실행하는 방식이다.

이렇게 프로세스를 여러 개 실행하면 CPU의 유휴 시간에 운영체제가 컨텍스트 스위칭을 통해 적절하게 병행처리하기 때문에 동시에 여러 작업들을 처리할 수 있다.

하지만 우리가 원하는 것은 하나의 프로세스에서 필요한 작업을 나누는 것이고, 필요한 자원을 공유할 수 있으면 효율적이다.

프로세스는 독립적인 메모리 공간을 갖기 때문에 중복되는 자원을 생성하게 될 가능성이 크고 프로세스 간에 자원 공유를 위해서는 IPC같은 기술을 사용해야 한다. 서로 다른 메모리 공간 간에 특정 데이터를 동기화하는 것도 쉽지 않다.

즉, 메모리 사용 + 여러 오버헤드(컨텍스트 스위칭 비용 등)로 인해 효율이 좋지 않은 문제가 발생하는 것이다.

3-3 스레드라는 개념을 생성한다

위와 같은 문제점을 보완하고자 스레드라는 개념이 나온 것이다.

스레드는 하나의 프로세스 내에서 실행의 흐름을 나누는 개념이다.

그렇기 때문에 같은 자원을 공유가 가능하고 컨텍스트 스위칭 비용이 훨씬 저렴하다.

여기서 자원을 공유한다는게 어떻게 가능한 일이고 컨텍스트 스위칭 비용이 왜 저렴한지 알아 보자.



스레드 효율

아래는 간단하게 프로세스의 구조를 나타낸 것이다.


프로세스는 위와 같은 구조로 주기억장치(RAM)에 적재된다.

스레드라는 개념이 없었을 때에는 실행의 단위가 프로세스였지만, 스레드가 생긴 이후부터는 프로세스란 적어도 하나의 스레드를 갖고 실행되는 개념으로 바뀌었다.

하나의 프로세스 내에 여러 개의 스레드를 생성하는 것을 멀티 스레드라고 하는데 이러한 멀티 스레드가 멀티 프로세스 보다 효과적인 이유에 대해 알아 보자.



자원 공유

프로세스가 아닌 스레드를 생성하면, 위의 프로세스 구조에서 Heap영역과 Data영역 그리고 Text 영역을 공유하여 사용할 수 있다.

각각의 스레드는 프로세스의 해당 영역들을 공유하며 사용하고, 독립적인 Stack 영역만을 갖는다.

그렇기 때문에 프로세스를 새롭게 시작하는 것에 비해 하나의 프로세스 내에서 스레드를 생성하는 것이 중복되는 자원을 방지하기 때문에 효과적이다.

위의 그림을 보면 알겠지만, 프로세스를 두 개 생성할 때에는 Heap, Data,Text 영역데 대한 중복이 발생한다.

물론 각 영역에서 중복이 아닌 데이터들도 존재하겠지만, 중복인 데이터들이 존재한다는 사실만으로도 낭비되는 메모리 공간이 있다는 뜻이 된다.

컨텍스트 스위칭 비용

자원을 공유하고 있다는 것은 CPU가 자원을 참조하기 위해 수행하는 추가적인 오버헤드가 없다는 뜻이기도 하다.

만약 프로세스가 두 개라면 컨텍스트 스위칭이 발생했을 때, CPU는 A프로세스에 대한 메모리 위치를 참조하다가 B프로세스의 메모리 위치로 참조를 바꿔야 한다.

두 프로세스가 결과적으로는 같은 프로그램에서 실행된 것이라고 하더라도 프로세스가 다르기 때문에 이 과정을 피할 수 없다.

이게 무슨소리인지 그림으로 확인해 보자.

A프로세스가 수행되다가 B프로세스로 컨텍스트 스위칭이 발생했고, B프로세스를 수행하기 위해서는 B프로세스의 Heap영역 Data영역 Text영역에 대한 정보가 필요하다. 그래서 CPU가 참조하는 메모리 주소를 001x에서 002x로 변경하였다.

반대로 스레드의 경우, A스레드에서 B스레드로 컨텍스트 스위칭이 발생해도 위와 같이 참조 주소를 바꿔줄 필요가 없다. 서로 같은 자원을 공유하기 때문이다.

( 개념적으로 이렇다는 것이지 실제로 이렇게 쉽게 동작하진 않는다.. )

이 외에도 스레드를 사용했을 때 여러 가지 절약되는 부분이 있겠지만 가장 이해하기 쉬운 부분이 해당 내용이지 않을까 싶다.



스레드가 무조건 좋은거 아님?

위의 내용을 보면 어떻게 봐도 스레드가 무조건 좋다고 생각할 수 있다.

하지만 그렇진 않다. 프로세스를 생성하는 것과 스레드를 생성한다는 것의 차이를 정확히 알아야 한다.

스레드라는 것은 말 그대로 하나의 프로세스에서 작업의 흐름을 나눈 것이다.

작업의 흐름을 나누는 개념이 아니고 진짜 독립적으로 작동해야 하는 상황이라면 어떨까?

예를 들어 웹 브라우저의 경우에는 창을 여러 개 띄워 놓고 사용할 수 있다.

지금까지 웹 브라우저를 사용하면서 창 하나가 동작을 안 하는데 다른 창도 동작을 안 한적이 있는가..?

( 있다면 그건 웹 브라우저 뿐 아니라 다른 프로세스도 문제가 있었을 확률이 높다.. )

이는 브라우저의 각각의 창은 독립적인 프로세스로 동작하기 때문이다.

스레드를 사용한다는 것은 프로세스 내의 작업 흐름을 분리하는 것 뿐이기 때문에, 하나의 스레드가 잘못 동작하면 프로세스 전체에 문제가 생길 가능성도 있다. 물론 무조건 그렇다는 것은 아니고 각 작업들이 서로 상호 연관이 있다면 그럴 확률이 높다.

하지만 프로세스는 서로 독립적인 메모리 공간을 갖고 동작하기 때문에 하나의 프로세스가 문제가 발생해도 서로 영향이 없다.

이렇듯 각각의 필요에 따라 프로세스와 스레드라는 개념이 사용되어야 하는 것이지 스레드가 무조건적으로 프로세스 보다 좋고 프로세스를 완전히 대체하기 위해 나온 개념은 아니라는 것이다.



용어가 헷갈려요

가끔 보면 멀티 프로그래밍, 멀티 프로세싱, 멀티 프로세스, 멀티 프로세서, 멀티 스레딩, 멀티 스레드 등등..

너무 비슷한 단어들이 너무 많다. 필자의 개인적인 생각과 검색해서 종합한 내용들을 추합하여 간단하게 정리해 보겠다.


1. 멀티 프로세서

멀티 프로세서는 말 그대로 프로세서(CPU)가 여러 개 달린 컴퓨터를 말한다.

멀티 코어 프로세서를 멀티 프로세서와 혼용해서 말하기도 하는 듯 하다.

2. 멀티 프로세싱(= 멀티 프로세스)

여러 프로세서를 통해 프로세스를 수행하는 것을 말한다. 이 때 프로세스는 하나 이상을 말하고 보통 요즘 컴퓨터에서는 하나의 프로세스만 실행되지 않기 때문에 사실상 여러 CPU로 여러 프로세스를 수행하도록 하는 것을 멀티 프로세싱이라고 이해하면 되지 않을까 싶다.

멀티 프로세싱은 프로세서가 여러 개인 상황을 가정하기 때문에, 실질적으로 병렬처리가 가능하다.

말 그대로 진짜 동시에 여러 작업을 처리할 수 있다는 뜻이다.

3. 멀티 프로그래밍

하나의 프로세서로 여러 개의 프로세스를 수행할 수 있도록 하는 것을 말한다. 멀티 프로그래밍은 병행처리에 해당한다.

현재는 멀티 프로세싱 + 멀티 프로그래밍이 더해져 여러 CPU들에 의해 여러 프로세스들이 병렬 + 병행 처리된다.

4. 멀티 스레딩(=멀티 스레드)

하나의 프로세스에서 여러 개의 스레드 작업을 수행하는 경우를 말한다. 이러한 멀티 스레드는 병렬 또는 병행으로 처리될 수 있다.

5. CPU의 코어

CPU에서 코어란 연산을 담당하는 핵심 부분이라 생각하면 된다.

코어가 여러 개라는 것은 CPU가 여러 개라는 것과도 사실상 같다.

6. CPU의 스레드

CPU에서 말하는 스레드는 우리가 위에서 공부한 스레드와는 조금 다른 개념으로 보는 것이 맞다. 좀 더 물리적인 개념에 가깝기 때문이다.

보통 1코어 2스레드 이런식으로 불리는 것을 볼 수 있는데, 이는 프로세스의 스레드와는 상관이 없고 CPU의 코어를 더 효과적으로 사용하여 마치 코어가 2개인 것처럼 동작시키는 기술을 사용한 경우에 붙여진다.

간단히 원리를 설명하면, CPU도 연산을 수행하는 유닛이 여러 개 존재하는데 이것들은 수행 가능한 명령어가 달라서 어떤 녀석은 일이 많을 수도 있고 어떤 녀석은 적을 수도 있다.

CPU는 한 번에 하나의 프로세스(스레드)만을 수행하는데 유닛마다 쓰이는 정도가 달라, 특정 유닛이 많은 연산을 처리하는 동안 다른 유닛이 쉬는 시간이 발생하기도 한다. 이는 연산 유닛을 효율적으로 사용하지 못 한다고 볼 수 있다.


이러한 문제점을 해결하기 위해 연산을 많이 필요로 하는 유닛이 추가된 프로세서를 개발하는 등의 시도가 있었다.

그 외에도 하이퍼 스레딩이라는 기술이 존재하는데, 하나의 쉬고 있는 남는 유닛들의 쉬는 시간을 줄이기 위해 한 번에 여러 스레드를 처리할 수 있도록 하는 방법이다.

CPU가 하나의 스레드가 아닌 여러 개의 스레드를 연산처리 하도록 적재하고 남는 유닛들을 최대한 활용하여 동시에 여러 개의 스레드가 동작하는 것처럼 만든 것이다.

이런 문제 때문에 서로 다른 스레드에서 같은 연산 유닛에 대한 필요도가 높아지면 크게 효과를 발휘하지 못 하는 경우가 많고, CPU가 여러 개인 것 보다는 성능이 떨어질 수 있다.

이렇듯 하이퍼 스레딩과 같은 SMT 기술을 사용하여 하나의 코어에서 여러 개의 스레드를 동작시킬 수 있도록 하였기 때문에 1코어 2스레드 등으로 불리는 것이다.

실제로 코어는 하나지만, 여러 개의 스레드를 처리할 수 있는 경우 1코어 2스레드 3스레드 이런식으로 불리는 것이다.

마치 CPU의 물리적인 한계를 극복한 과정이 멀티 프로그래밍과 비슷하다. 연산 유닛의 유휴 시간을 줄이고자 하는 시도이기 때문이다.

아마 프로세서라는 단어는 이미 CPU에서 사용중이고, 프로세스라는 단어는 프로세서랑 비슷하고, 프로그램이라는 단어는 물리적인 개념과 맞지 않으니 스레드라는 단어를 갖고 가서 사용한 것이 아닐까 싶다. 1코어 2프로그래밍 이런 표현 보다는 1코어 2스레드라는 표현이 적합한 것 같긴 하다..

하여튼 CPU에서 말하는 스레드와 혼동하지 않아야 한다.

하이퍼 스레딩에 대한 자세한 내용은 이곳를 참고하면 좋을 듯 하다.



정리

간단하게 프로세스와 스레드의 차이점에 대해서 알아 보았다.

사실 더 깊게 들어가려면 운영체제부터 시작해서 프로세스와 스레드의 구조 등등 공부할 것이 많지만 개발 세상에서는 이정도만 알고 있어도 크게 문제가 될 것 같진 않다.

더 자세한 내용을 알고 싶다면 따로 공부하도록 하자.

profile
자료를 찾다 보면 사소한 부분에서 궁금한 부분이 생기도 한다. 똑같은 복붙식 블로그 때문에 시간만 낭비되고 시원하게 해결하지 못 하는 경우가 많았다. 그런 부분들까지 세세하게 고민하고 함께 해결해 나가고자 글을 작성한다. 혼자서 작성하는 블로그가 아닌 함께 만들어 가는 블로그이다. ( 지식 공유를 환영합니다. )

0개의 댓글