Post

프로세스, 스레드


컴퓨터 구조

컴퓨터 구조를 공부하면서 알게된 내용을 요약해서 작성해보자.

CPU

  • CPU는 스레드, 프로세스, 운영체제 같은 개념을 모른다.
  • CPU는 두 가지 사항만 알고 있다.
    • 메모리에서 명령어(instruction)을 하나 가져온다.(dispatch)
    • 이 명령어를 실행(execute)한 후 다시 1.로 돌아간다.
  • 소스파일 -> 컴파일러 -> 실행 파일 -> 디스크 -> 메모리 -> CPU
  • 프로그램이 시작되면 먼저 main 함수에 대응하는 첫 번째 기계 명령어를 찾는다.
  • 찾은 명령어의 메모리 주소를 PC 레지스터에 기록한다.
  • CPU가 하나뿐인 단일 코어 컴퓨터에서 웹 서핑과 코드 작성을 동시에 하려면 어떻게 할까 ?
  • CPU는 한 번에 한 가지 일만 할 수 있다. A의 기계 명령어를 실행 하거나 B의 기계명령어를 실행한다.
  • A를 실행했다가 이를 잠시 중단하고 프로그램 B의 실행으로 넘어가고, B를 중지 했다가 A로 넘어간다.
  • CPU의 전환 빈도가 빠르면 위 사항은 동시에 실행 되는 것 처럼 보인다.

Process

  • CPU가 어떤 기계 명령어를 실행했는지와 CPU 내부의 기타 레지스터 값 등 상태 값이 있다.
  • 이 정보를 저장할 수 있다면 프로그램을 일시 중지했다가도 프로그램을 재개할 수 있다.
  • 다음과 같이 프로그램 실행 상태를 저장하고 복구할 때 사용할 구조체를 정의하는 코드를 작성했다.
    1
    2
    3
    4
    5
    
    struct ***
    {
      context ctx; // cpu의 상황 정보 저장
      ...
    };
    
  • 실행 중인 모든 프로그램은 필요한 정보를 기록할 수 있는 이런 형태의 구조체를 가지고 있어야 한다.
  • 이 구조체가 프로세스(process)다.
  • 프로세스를 사용하면 모든 프로세스를 원하는 대로 일시 중지하거나 다시 시작할 수 있다.
  • CPU가 하나여도 프로세스를 동시에 실행하거나 적어도 동시에 실행 중인 것처럼 보이게 할 수 있다.
  • 모든 사람이 프로그램을 자동으로 적재해 주는 적재 도구와 멀티태스킹을 실현해 주는
    관리 도구 처럼 이 기능들을 사용하기 원한다.
  • 이 여러 가지 기반 기능의 프로그램을 모아 둔 도구가 운영체제(operating system)이다.
  • 프로세스 주소 공간은 매우 중요하다.
  • 아래에서 위로 올라가는 방향으로 각각 다음과 같다.
    • 코드 영역(code segment): 코드를 컴파일하여 생성된 기계 명령어가 저장된다.
    • 데이터 영역(data segment): 전역 변수 등이 저장된다.
    • 힙 영역(heap segment): malloc 함수가 요청을 반환한 메모리가 여기에 할당된다.
    • 스택 영역(stack segment): 함수의 실행 시간 스택이다.

Thread

  • 하나의 프로세스에 속한 기계 명령어를 CPU 여러 개에서 동시에 실행할 수 있다.
  • 프로세스를 생성할 때는 메모리에서 실행 파일을 적재할 적절한 영역을 찾은 후 CPU의
    PC 레지스터를 main 함수의 주소로 지정해야 한다.
  • 이런 이유로 하나의 프로세스에는 단 하나의 실행 흐름만 존재할 수 있다고 했던 것이다.
  • CPU 여러 개가 한 지붕 아래에 있는 것과 마찬가지로 공유 프로세스 주소 공간에서
    동일한 프로세스에 속한 명령어를 동시에 실행할 수 있다.
  • 다시 말해 하나의 프로세스 안에 여러 실행 흐름이 존재할 수 있다.
  • 이것을 스레드(Thread)라고 한다.
  • 스레드를 경량 프로세스 라고도 한다.
  • 프로세스를 시작하고 스레드 여러 개를 생성하기만 하면 다중 코어를 충분히 이용하여
    모든 CPU를 최대한 활용할 수 있다. 이것이 바로 고성능과 높은 동시성의 기초다.
  • 실행 흐름이 하나뿐인 프로세스는 실행 시 정보를 저장하는 스택 영역이 하나만 있으면 된다.

Thread Pool

  • 스레드의 생성과 종료에 많은 시간을 허비한다.
  • 스레드를 많이 생성하면 메모리와 기타 시스템 리소스를 너무 많이 소비한다.
  • 스레드 수가 많으면 스레드 간 전환에 따른 부담이 증가한다.
  • 위 이유 때문에 스레드 풀이 탄생하게 되었다.
  • 스레드 풀의 개념
    • 스레드 여러 개를 미리 생성한다.
    • 스레드가 처리할 작업이 생기면 해당 스레드에 처리 요청을 한다.
  • 스레드 여러 개가 미리 생성되어 있기 때문에 스레드 생성, 종료 작업이 빈번하게 발생하지 않는다.
  • 스레드 풀 내에 있는 스레드 수도 일정하게 관리되기 때문에 불필요한 메모리 소비를 하지 않는다.
  • 이 개념에서 중요한 점은 스레드를 재사용 한다는 것이다.
  • 스레드 풀의 스레드는 작업 대기열에서 블로킹 상태로 대기한다.
  • 생산자가 작업 대기열에 데이터를 기록하면 스레드 풀의 스레드가 깨어나고, 깨어난 스레드는 작업 대기열에서 앞서 정의한 구조체를 가져온 후 구조체의 handle이 가리키는 처리 함수를 실행한다.
  • 작업 대기열은 여러 스레드 간에 공유되는 리소스이므로 동기화를 할 때 상호 배제 문제를 반드시 처리해야 한다.
  • 스레드 풀의 스레드는 너무 많아도 안 되고 적어도 안된다.
  • 작업을 처리할 때 필요한 리소스 관점에서 바라보고 작업을 구분하면 CPU 집약적인 작업과 입출력 집약적인 작업으로 구분할 수 있다.
  • CPU 집약적인 작업이란 과학 연산, 행렬 연산 등 작업을 처리할 때 외부 입출력에 의존할 필요 없이 처리할 수 있는 작업을 의미한다.
  • 이 경우 스레드 수와 CPU의 코어 수가 기본적으로 동일하다면 CPU의 리소스를 충분히 활용할 수 있다.
  • 입출력 집약적인 작업이란 연산 부분이 차지하는 시간은 많지 않은 대신 대부분의 시간을 디스크 입출력이나 네트워크 입출력 등에 소비하는 작업을 의미한다.

프로세스 주소 공간

  • 코드 영역에는 컴파일한 후 생성된 실행 가능한 기계 명령어가 저장된다.
  • 코드 영역은 스레드 간에 공유되므로 어떤 함수든지 모두 스레드에 적재하여 실행할 수 있다.
  • 특정 함수를 특정 스레드에서만 실행되도록 하는 것은 불가능하다.
  • 코드 영역은 읽기 전용이기 때문에 프로그램이 실행되는 동안 코드 영역 내역을 변경할 수 없다.