[Operating Systems] 스레드

🔄 멀티태스킹과 멀티프로세싱

멀티스레드에 대해서 제대로 이해하려면 먼저 멀티태스킹과 프로세스 같은 운영체제의 기본 개념들에 대해서 알아야한다.

💻 단일 CPU 환경

연산을 처리할 수 있는 CPU 코어가 1개만 있다고 가정하고, 프로그램을 2개 이상 동시에 실행한다고 가정해보자.

  • 프로그램의 실행이란 프로그램을 구성하는 코드를 순서대로 CPU에서 연산(실행)하는 일이다.
  • 여기서 CPU 코어는 하나로 가정하므로, 한번에 하나의 프로그램 코드만 실행할 수 있다.
  • 이때, 하나의 프로그램 안에 있는 코드를 모드 실행한 후에야 다른 프로그램의 코드를 실행할 수 있다면? 예를 들어 음악 프로그램이 끝난 뒤에야 워드 프로그램을 실행할 수 있다면 컴퓨터 사용자는 매우 답답할 것이다.
  • 실제로 초창기의 컴퓨터는 이처럼 한 번에 하나의 프로그램만 실행했다.
  • 이를 해결하기 위해 하나의 CPU 코어로 여러 프로그램을 동시에 실행하는 멀티태스킹 기술이 등장했다.

CPU 안에는 실제 연산을 처리할 수 있는 코어라는 것이 있다. 과거에는 하나의 CPU 안에 보통 하나의 코어만 들어있었다. 그래서 CPU와 코어를 따로 분리해서 이야기하지 않았다. 현대 CPU는 여러 코어를 가지며, 각가의 코어가 독립적으로 연산을 처리할 수 있다.


🔀 멀티태스킹

멀티태스킹은 시분할(Time Sharing)이라는 기법을 기반으로 작동한다. CPU가 초당 수십억 번 이상의 연산을 수행할 수 있기 때문에, 두 개 이상의 프로그램을 매우 빠르게 번갈아 가며 실행하면 사용자는 프로그램들이 동시에 실행되는 것처럼 느낀다.

이 방식은 애니메이션과 비슷하다. 애니메이션은 초당 30~60장의 사진을 교차로 보여줘서 사람이 움직임으로 인식하게 하는데, CPU도 이와 비슷한 방식으로 여러 프로그램을 짧은 시간 동안 교차해서 처리한다.

보통 0.01초(10ms) 간격으로 프로그램을 번갈아 실행하며, 사용자는 이 짧은 교체 주기를 인식하지 못한다. 결과적으로 여러 프로그램이 동시에 실행되는 것처럼 보이게 된다.

이렇게 하나의 컴퓨터 시스템이 동시에 여러 작업을 수행하는 능력을 멀티태스킹(Multitasking)이라 한다.

CPU에 어떤 프로그램이 얼마만큼 실행될지는 운영체제가 결정하는데, 이것을 스케줄링(Scheduling)이라 한다. 이때 단순히 시간으로만 작업을 분할하지는 않고, CPU를 최대한 활용할 수 있는 다양한 우선순위와 최적화 기법을 사용한다. 자세한 내용을 알려면 운영체제 이론을 공부해야 한다.


🖥️ 멀티프로세싱

멀티프로세싱(Multiprocessing)은 컴퓨터 시스템에서 둘 이상의 프로세서(CPU 코어)를 사용해 여러 작업을 동시에(병렬로) 처리하는 기술을 의미한다.

예를들어, 프로그램 A,B,C 3가지가 있고 CPU 코어는 2개라면:

  • CPU 코어가 2개이므로 물리적으로 동시에 2개의 프로그램을 처리할 수 있다.
  • 나머지 1개의 프로그램은 멀티태스킹 방식으로 처리될 수 있다.

멀티프로세싱 시스템은 하나의 CPU 코어만을 사용하는 시스템보다 동시에 더 많은 작업을 처리할 수 있어 전반적인 시스템 성능을 향상 시킨다.


⚖️ 멀티태스킹 vs 멀티프로세싱

멀티태스킹은 운영체제 소프트웨어의 관점이고, 멀티프로세싱은 하드웨어 장비의 관점이다.

  • 멀티태스킹
    • 단일 CPU(단일 CPU 코어)가 여러 작업을 동시에 수행하는 것처럼 보이게 하는 것을 의미한다.
    • 소프트웨어 기반으로 CPU 시간을 분할하여 각 작업에 할당한다.
    • 예: 현대 운영체제에서 여러 애플리케이션이 동시에 실행되는 환경
  • 멀티프로세싱
    • 여러 CPU(여러 CPU 코어)를 사용해 동시에 여러 작업을 수행하는 것을 의미한다.
    • 하드웨어 기반으로 성능을 향상시킨다.
    • 예: 다중 코어 프로세서를 사용하는 현대 컴퓨터 시스템보다

🧵 프로세스와 스레드

📁 프로세스

alt text

프로세스는 실행 중인 프로그램의 인스턴스이다. 프로그램이 단순히 정적 파일이라면, 프로세스는 그 프로그램이 메모리에 로드되어 실행 중인 동적인 상태를 나타낸다.

각 프로세스는 독립적인 메모리 공간을 갖고 있으며, 운영체제에서 별도의 작업 단위로 분리해서 관리된다. 각 프로세스는 별도의 메모리 공간을 갖고 있기 때문에 서로 간섭하지 않는다. 그리고 프로세스가 서로의 메모리에 직접 접근할 수 없다.

이렇듯 서로 격리되어 관리되기 때문에, 하나의 프로세스가 충돌해도 다른 프로세스에는 영향을 미치지 않는다. 쉽게 이야기해서 특정 프로세스(프로그램)에 심각한 문제가 발생하면 해당 프로세스만 종료되고, 다른 프로세스에 영향을 주지 않는다.

프로세스의 메모리 구성

  • 코드 섹션: 실행할 프로그램의 코드가 저장되는 부분
  • 데이터 섹션: 전역 변수 및 정젹 변수가 저장되는 부분(그림에서 기타에 포함)
  • 힙 (Heap): 동적으로 할당되는 메모리 영역
  • 스택 (Stack): 메서드(함수) 호출 시 생성되는 지역 변수와 반환 주소가 저장되는 영역(스레드에 포함)

🧶 스레드

스레드(Thread)는 프로세스 내에서 실행되는 작업의 단위다.
프로세스는 하나 이상의 스레드(Thread)를 반드시 포함한다. 한 프로세스 내에서 여러 스레드가 존재할 수 있으며, 이들은 프로세스가 제공하는 동일한 메모리 공간을 공유한다. 스레드는 프로세스보다 단순하므로 생성 및 관리가 단순하고 가볍다.

스레드의 메모리 구성

  • 공유 메모리: 같은 프로세스의 코드 섹션, 데이터 섹션, 힙(메모리)은 프로세스 내의 모든 스레드가 공유한다.
  • 개별 스택: 각 스레드는 자신의 스택을 갖고 있다.

❓ 여기서 잠깐 프로그램이 실행된다는 것은 어떤 의미일까?

프로그램이 실행된다는 것은 다음과 같은 과정을 거친다:

  1. 운영체제가 디스크의 프로그램 파일을 메모리로 로드한다.
  2. 프로세스가 생성된다.
  3. 프로세스 내의 스레드가 코드를 한 줄씩 실행한다.

스레드는 마치 실처럼 코드를 위에서 아래로 “꿰면서” 실행해 나간다. 일반적으로 main()함수부터 시작해 순차적으로 실행된다. 프로그램이 실행된다는 것은 사실 프로세스 안에 있는 코드가 한 줄씩 실행되는 것이다. 코드는 보통 main()부터 시작해서 하나씩 순서대로 내려가면서 실행된다.

단일 스레드 vs 멀티 스레드

  • 단일 스레드: 한 프로세스 내에 하나의 스레드만 존재
  • 멀티 스레드: 한 프로세스 내에 여러 스레드가 존재

멀티스레드가 필요한 이유
하나의 프로그램도 그 안에서 동시에 여러 작업이 필요하다.

  • 워드 프로그램으로 문서를 편집하면서, 문서가 자동으로 저장되고, 맞춤법 검사도 함께 수행된다.
  • 유튜브 영상을 보는 동안, 댓글도 달 수 있다.

운영체제 관점에서 보면 다음과 같이 구분할 수 있다.

  • 워드 프로그램 - 프로세스A
    • 스레드1: 문서 편집
    • 스레드2: 자동 저장
    • 스레드3: 맞춤법 검사
  • 유튜브 - 프로세스B
    • 스레드1: 영상 재생
    • 스레드2: 댓글

📊 스레드와 스케줄링

  • 프로세스는 실행 환경과 자원을 제공하는 컨테이너 역할을 한다.
  • 스레드는 실제로 CPU를 사용해 코드를 실행하는 단위다.
  • 운영체제는 스케줄링 큐를 사용해 실행 대기 중인 스레드들을 관리한다.

1️⃣ 단일 코어 스케줄링

단일 코어 환경에서는 한 번에 하나의 스레드만 실행할 수 있다. 운영체제는 시분할 방식으로 여러 스레드를 번갈아 실행한다.

예시 시나리오

  • CPU 코어: 1개
  • 프로세스 A: 스레드 1개 (A1)
  • 프로세스 B: 스레드 2개 (B1, B2)

스케줄링 과정

  1. 스레드 A1, B1, B2가 스케줄링 큐에서 대기한다.
  2. 운영체제가 스레드 A1을 큐에서 꺼내 CPU로 실행한다.
  3. 일정 시간 후, A1의 실행을 멈추고 다시 큐에 넣는다.
  4. 스레드 B1을 큐에서 꺼내 실행한다.
  5. B1의 실행을 멈추고, B2를 실행한다.
  6. 이 과정을 반복한다.

🔢 멀티 코어 스케줄링

멀티 코어 환경에서는 여러 스레드를 물리적으로 동시에 실행할 수 있다.

예시 시나리오

  • CPU 코어: 2개
  • 프로세스 A: 스레드 1개 (A1)
  • 프로세스 B: 스레드 2개 (B1, B2)

스케줄링 과정

  1. 스레드 A1, B1, B2가 스케줄링 큐에서 대기한다.
  2. 스레드 A1과 B1을 각각 다른 CPU 코어에서 동시에 실행한다.
  3. 스레드 B2는 큐에서 대기한다.
  4. 일정 시간 후, A1의 실행을 멈추고 큐에 다시 넣는다.
  5. B2를 큐에서 꺼내 빈 CPU 코어에서 실행한다.
  6. 이 과정을 반복한다.

🔁 컨텍스트 스위칭

컨텍스트 스위칭(Context Switching)은 멀티태스킹 운영체제에서 매우 중요한 메커니즘이다. CPU가 한 작업(프로세스 또는 스레드)에서 다른 작업으로 전환할 때 발생하는 과정으로, 작업의 실행 상태(컨텍스트)를 저장하고 다른 작업의 실행 상태를 불러오는 과정을 의미한다.

💡 컨텍스트 스위칭의 개념

컨텍스트는 CPU가 현재 작업을 실행하는 데 필요한 모든 상태 정보이다. 이 정보에는 CPU 레지스터 값, 스택 포인터, 프로그램 카운터, CPU 플래그 등이 포함되며, 작업 간 전환 시 이 상태 정보가 저장되고 불러와진다.

🎯 컨텍스트 스위칭이 필요한 이유

  1. 멀티태스킹: 여러 작업을 동시에 실행하는 것처럼 보이게 하기 위해, CPU는 여러 작업을 빠르게 전환하며 실행한다.
  2. 시분할 시스템: 여러 작업이 공평하게 CPU 시간을 나누어 사용하도록 하기 위해 필요하다.
  3. 인터럽트 처리: 시스템 콜이나 하드웨어 인터럽트를 처리하기 위해 실행 중인 작업을 일시 중지하고 우선적으로 인터럽트를 처리해야 할 때 사용된다.
  4. 우선순위 스케줄링: 높은 우선순위 작업을 즉시 처리하기 위해 컨텍스트 스위칭이 발생할 수 있다.

⏭️ 컨텍스트 스위칭의 과정

  1. 현재 작업의 상태 저장: 현재 실행 중인 작업 A의 모든 상태(컨텍스트)를 CPU에서 메모리로 저장한다.
  2. 다음 작업의 상태 불러오기: 다음 실행할 작업 B의 상태(컨텍스트)를 메모리에서 CPU로 불러온다.
  3. 작업 전환: CPU가 작업 B를 실행하기 시작한다.

이 과정은 매우 빠르게 이루어지지만, 자주 발생할 경우 오버헤드가 발생할 수 있다.


💰 컨텍스트 스위칭의 비용

컨텍스트 스위칭은 필수적이지만 비용이 발생하는 작업이다. 비용은 크게 다음과 같다:

  1. 시간 비용: 작업 A의 상태를 저장하고, 작업 B의 상태를 불러오는 데 시간이 필요하다. 이 과정이 매우 짧은 시간이지만, 빈번히 발생하면 전체 성능에 영향을 미친다.

  2. 캐시 미스(Cache Miss): 새로운 작업이 실행되면 CPU 캐시에 저장된 이전 작업의 데이터가 무효화되므로, 다시 데이터를 로드해야 하는 상황이 발생한다. 이는 성능 저하의 주요 원인 중 하나이다.

  3. TLB(Translation Lookaside Buffer) 플러시: 프로세스 간 컨텍스트 스위칭이 발생할 때, 메모리 주소 변환 정보를 저장한 TLB도 초기화되어야 하므로, 추가적인 메모리 접근 오버헤드가 발생한다.

  4. 동기화 비용: 특히 멀티 코어 시스템에서는 여러 스레드 간 동기화가 필요할 때 컨텍스트 스위칭의 부가적인 비용이 발생할 수 있다.


⚡ 멀티태스킹의 효율성

컨텍스트 스위칭은 멀티태스킹을 가능하게 하지만, 항상 효율적인 것은 아니다. 효율성은 여러 요인에 의해 달라진다:

  1. 작업의 특성: CPU 집중 작업이냐 I/O 집중 작업이냐에 따라 효율성이 다르다. I/O 집중 작업의 경우 CPU가 작업이 아닌 I/O 대기를 하면서 컨텍스트 스위칭을 자주 발생시켜도 성능 저하가 크지 않지만, CPU 집중 작업에서는 컨텍스트 스위칭 오버헤드가 더 뚜렷하게 나타날 수 있다.

  2. 하드웨어 자원: 사용 중인 시스템의 CPU 코어 수, 메모리 크기 등에 따라 컨텍스트 스위칭 빈도와 효율성이 달라진다. 멀티 코어 환경에서는 여러 작업을 동시에 실행할 수 있으므로 스레드 스케줄링이 더욱 효율적이다.

  3. 작업 간 데이터 공유: 스레드 간 데이터 공유가 많을수록 동기화와 컨텍스트 스위칭의 부가적인 비용이 커질 수 있다.


🔬 예시

예를 들어, 1에서 10000까지의 합을 구하는 작업을 살펴보겠다.

단일 코어 환경:

  • 단일 스레드: 컨텍스트 스위칭 없이 연속적으로 계산 작업을 완료할 수 있다. 이 경우 오버헤드가 없으므로 가장 효율적이다.
  • 멀티 스레드 (2개 스레드 사용): 스레드1은 1부터 5000까지 계산하고, 스레드2는 5001부터 10000까지 계산합니다. 그러나 단일 코어에서는 CPU가 두 스레드를 번갈아 실행해야 하므로, 컨텍스트 스위칭 오버헤드가 발생한다. 이 경우 단일 스레드보다 느릴 수 있다.

멀티 코어 환경:

  • 두 개의 스레드가 각각 다른 코어에서 동시에 실행될 수 있으므로, 컨텍스트 스위칭이 거의 발생하지 않고, 이론적으로 단일 스레드보다 2배 빠르게 작업을 완료할 수 있다. 다만, 스레드 간의 동기화가 필요하다면 성능이 저하될 가능성도 있다.

🛠️ 실무에서

🧮 CPU 코어 수와 스레드 수의 관계

시나리오 1: CPU 4개, 스레드 2개

  • 장점: 컨텍스트 스위칭 비용 감소
  • 단점: CPU 자원을 충분히 활용하지 못함

시나리오 2: CPU 4개, 스레드 100개

  • 장점: CPU 자원을 거의 100% 활용 가능
  • 단점: 스레드가 많아 컨텍스트 스위칭 비용 증가

시나리오 3: CPU 4개, 스레드 4개 (또는 5개)

  • 장점: CPU 자원을 효율적으로 사용하며 컨텍스트 스위칭 비용을 최소화할 수 있음
  • 권장 스레드 수: CPU 코어 수 + 1 (특정 스레드가 대기할 때 추가 스레드로 효율성 극대화)

📋 작업 유형에 따른 스레드 관리

CPU-바운드 작업 (CPU-bound tasks)

  • 정의: CPU의 연산 능력을 많이 요구하는 작업
  • 예시: 복잡한 수학 연산, 데이터 분석, 비디오 인코딩, 과학적 시뮬레이션
  • 최적 스레드 수: CPU 코어 수 + 1 (코어마다 스레드를 배정해 CPU 사용률 극대화)

I/O-바운드 작업 (I/O-bound tasks)

  • 정의: 디스크, 네트워크 등의 I/O 작업이 많은 작업으로 CPU 대기 시간이 많음
  • 예시: 데이터베이스 쿼리, 파일 읽기/쓰기, 네트워크 통신, 사용자 입력 처리
  • 특징: CPU 사용률이 낮고 I/O 작업 대기 시간이 많음
  • 최적 스레드 수: CPU 코어 수보다 많은 스레드를 생성해 CPU 사용률을 극대화 (성능 테스트를 통해 결정)

🌐 웹 애플리케이션 서버의 스레드 관리

  1. 특징:
    • 웹 애플리케이션 서버는 주로 I/O-바운드 작업이 많음
    • 요청 하나당 하나의 스레드가 필요하며, 여러 사용자의 요청을 동시 처리하기 위해 스레드가 더 많이 필요
  2. 스레드 수 결정 시 고려사항:
    • CPU 사용률
    • 동시 사용자 수와 요청 처리 시간
  3. 주의사항:
    • CPU 코어 수에 맞춰 스레드 수를 제한하면, 동시에 처리 가능한 사용자 요청 수가 제한되어 CPU 자원이 놀게 됨
    • 과도한 스레드 생성은 컨텍스트 스위칭 비용을 증가시킴
  4. 최적화 전략:
    • I/O-바운드 작업에서는 CPU 코어 수보다 많은 스레드를 생성해 CPU 자원을 최대한 활용해야 함
    • 성능 테스트를 통해 적정 스레드 수를 찾아 CPU 사용률과 성능을 최적화

🔧 실무 팁

  1. 작업 특성 파악:
    • CPU-바운드 작업과 I/O-바운드 작업의 비율을 정확히 이해하고 관리
  2. 성능 테스트:
    • 스레드 수를 늘려가며 CPU 사용률, 응답 시간, 처리량 등을 측정해 최적의 성능을 확인
  3. 실시간 모니터링:
    • 실시간으로 CPU와 스레드의 사용 상태를 모니터링해 필요에 따라 동적으로 스레드 수를 조정
  4. 하드웨어 업그레이드 vs 소프트웨어 최적화:
    • 하드웨어 업그레이드 전에 스레드 관리와 소프트웨어 최적화를 통해 성능 개선을 우선 고려
    • 스레드 수 최적화만으로도 성능 개선 효과를 얻을 수 있음
  5. 유연한 대응:
    • 웹 애플리케이션이라고 해도 상황에 따라 CPU-바운드 작업이 많을 수 있으므로, 작업 특성에 맞는 최적화 전략을 유연하게 적용해야 함

📝 정리

✔️ 멀티태스킹과 스케줄링

멀티태스킹이란 동시에 여러 작업을 수행하는 것을 말한다. 이를 위해 운영체제는 스케줄링이라는 기법을 사용한다. 스케줄링은 CPU 시간을 여러 작업에 나누어 배분하는 방법이다.

✔️ 프로세스와 스레드

프로세스는 실행 중인 프로그램의 인스턴스이다. 각 프로세스는 독립적인 메모리 공간을 가지며, 운영체제에서 독립된 실행 단위로 취급된다.

스레드는 프로세스 내에서 실행되는 작은 단위이다. 여러 스레드는 하나의 프로세스 내에서 자원을 공유하며, 프로세스의 코드, 데이터, 시스템 자원 등을 공유한다. 실제로 CPU에 의해 실행되는 단위는 스레드이다.

프로세스의 역할
프로세스는 실행 환경을 제공한다. 여기에는 메모리 공간, 파일 핸들, 시스템 자원(네트워크 연결) 등이 포함된다. 이는 프로세스가 컨테이너 역할을 한다는 의미이다.

프로세스 자체는 운영체제의 스케줄러에 의해 직접 실행되지 않으며, 프로세스 내의 스레드가 실행된다. 참고로 1개의 프로세스 안에 하나의 스레드만 실행되는 경우도 있고, 1개의 프로세스 안에 여러 스레드가 실행되는 경우도 있다.

✔️ 컨텍스트 스위칭

컨텍스트 스위칭은 CPU가 한 작업에서 다른 작업으로 전환할 때 발생하는 과정이다. 이 과정에서 현재 작업의 상태(컨텍스트)를 저장하고 다음 작업의 상태를 불러온다. 컨텍스트 스위칭은 멀티태스킹을 가능하게 하지만, 시간 비용, 캐시 미스, TLB 플러시 등의 오버헤드를 발생시킬 수 있다. 효율적인 컨텍스트 스위칭은 시스템 성능에 중요한 영향을 미친다.


참고

Leave a comment