용어에 대하여

(전략) 이번 장에서는 프로세스의 내부로 들어가서, 하나의 프로세스의 환경 안에서 여러 개의 제어 가닥(thread of control)들을 이용해서 여러 개의 과제들을 수행하는 방법을 살펴본다. 그러한 제어 가닥 또는 한 줄기의 실행 흐름을 스레드(thread)라고 부른다. 한 프로세스 안의 모든 스레드는 동일한 프로세스 구성요소들(파일 서술자, 메모리 등)을 공유한다. 1

간략한 역사

초기 시분할 시스템에서는 각 프로세스가 가상적인 폰 노이만 컴퓨터였다. 각각 명령어와 데이터를 저장하는 메모리 공간을 가지고 기계어로 된 명령어를 순차적으로 수행하며, 운영체제가 제공하는 I/O 수단을 통해 컴퓨터 외부와 교류했다. 각 명령어 마다 '다음 명령'이 명료하게 정의되어 있었고, 명령어 집합의 규칙에 따라 실행 흐름이 제어됐다. 현재 널리 사용되는 대부분의 프로그램 언어가 이러한 순차적 프로그래밍 방식을 따른다. 언어 명세를 보면 주어진 동작이 실행된 이후 "어떤 일이 일어나는가”가 명료하게 정의돼 있다.

순차적 프로그래밍 모델은 사람이 생각하는 방식과 같아서 직관적이고 자연스럽다. 대부분 경우 한 번에 한 가지씩 순서대로 처리한다. 침대에서 나와, 목욕 가운을 입고, 아래층으로 내려가 커피를 준비한다. 프로그래밍 언어에서처럼 이런 생활 속의 동작 각각도 좀 더 세분화된 순차적인 동작들을 추상화한 것이다. 즉 찬장을 열고, 마시려는 커피의 종류를 선택하고, 적정량의 커피를 주전자에 넣은 다음, 주전자 안에 물이 충분히 있나 본다. 물이 부족하면 물을 더 넣어 렌지 위에 얹고, 렌지를 켠 후 물이 끓을 때까지 기다리는 등등. 물이 끓을 때까지 기다리는 마지막 단계는 어느 정도 비동기적인 성격이 있으며, 다시 말해 물이 데워지는 동안은 몇 가지 선택이 있다. 단순히 기다리거나, 곧 주전자에 신경을 써야 한다는 걸 잊지 않으면서, 토스트를 준비할 수도 있고(역시 비동기적 작업이다), 현관에서 신문을 집어올 수도 있다. 주전자나 토스터기를 만드는 회사는 제품이 비동기적으로 사용되는 경우가 많다는 걸 알기 때문에 뭔가 작업이 끝나면 소리를 내도록 만드는 게 보통이다. 일 머리가 있는 사람은 순차적으로 할 일과 비동기적으로 할 일 간에 적절히 균형을 찾아낸다. 프로그램에서도 마찬가지다.

자원 활용, 공정성, 편의성 등 프로세스 개념을 만들어내게 된 것과 같은 동기를 갖고 스레드가 고안됐다. 스레드로 인해 한 프로세스 안에 여러 개의 프로그램 제어 흐름이 공존할 수 있다. 스레드는 메모리, 파일 핸들과 같이 프로세스에 할당된 자원을 공유한다. 하지만 각 스레드는 각기 별도의 프로그램 카운터, 스택, 지역 변수를 갖는다. 또한, 프로그램을 스레드로 분리하면 멀티프로세서 시스템에서 자연스럽게 하드웨어 병렬성을 이용할 수 있다. 즉 한 프로그램 내 여러 스레드를 동시에 여러 개의 CPU에 할당해 실행시킬 수 있다.

스레드를 가벼운 프로세스lightweight process라고 부르기도 하며, 현대 운영체제의 대부분은 프로세스가 아니라 스레드를 기본 단위로 CPU 자원의 스케줄을 정한다. 의도적으로 조율하지 않는 한 하나의 스레드는 다른 스레드와 상관 없이 비동기적으로 실행된다. 스레드는 자신이 포함된 프로세스의 메모리 주소 공간을 공유하기 때문에 한 프로세스 내 모든 스레드는 같은 변수에 접근하고 같은 힙heap에 객체를 할당한다. 이 때문에 프로세스 때보다 더 세밀한 단위로 데이터를 공유할 수 있다. 하지만 공유된 데이터에 접근하는 과정을 적절하게 동기화하지 않으면 다른 스레드가 사용 중인 변수를 순간적으로 수정해서 예상치 못한 결과를 얻을 수도 있다. 2

From: 멀티프로세서 프로그래밍

다중프로세서는 복수의 하드웨어 프로세서로 구성되며, 각 프로세서는 순차적인 프로그램을 실행한다. 다중프로세서 아키텍처에 관해 다룰 때, 시간의 기본 단위는 사이클이다. 사이클은 프로세서가 하나의 명령어를 가져와서 실행하는 데 걸리는 시간이다. 절대적인 용어로, 사이클 시간은 기술이 발전함에 따라 변화한다(1980년에는 초당 천만 사이클이었고, 2005년에는 30억 사이클이었다). 그리고 플랫폼에 따라 차이가 있다(토스터를 제어하는 프로세서는 웹 서버를 제어하는 프로세서보다 더 긴 사이클을 갖는다). 그러나 메모리 접근과 같은 명령어의 상대적인 비용은 사이클로 표현했을 때 천천히 변화한다.

스레드(thread)는 순차 프로그램이다. 프로세서가 하드웨어 장치이듯이, 스레드는 소프트웨어 구조다. 프로세서는 한 동안 스레드를 실행하고, 이를 비켜두고 다른 스레드를 실행하며, 이 이벤트를 문맥 교환(context switch)이라 부른다. 프로세서는 스레드를 옆에 비켜둘 수 있으며, 혹은 스케줄러에서 나오며(deschedule), 이에는 다양한 이유가 있을 수 있다. 스레드가 완료되려면 시간이 걸리는 메모리 요청을 했다거나, 혹은 스레드가 충분히 길게 실행되어 다른 스레드가 실행될 때라거나 할 수 있다. 스레드가 스케줄러에서 나오면, 다른 프로세서에서 다시 재개할 수도 있다. 3

From: UNIX 고급 프로그래밍

스레드와 스레드 ID

일반적으로 하나의 프로세스에는 제어의 줄기가 하나만 존재한다. 즉, 일단의 기계어 명령들이 한 번에 하나씩 실행된다. 그러나 문제의 서로 다른 부분을 여러 개의 제어 줄기로 처리할 때 더 풀기 쉬워지는 문제들도 있다. 또한 제어의 줄기가 여러 개이면 다중 프로세서 시스템에서 가능한 병렬성을 활용할 수 있게 된다. 그러한 제어의 줄기를 스레드(thread)라고 부른다.

한 [[/study/os/process]]{프로세스}의 모든 스레드는 동일한 주소 공간과 파일 서술자들, 스택들, 프로세스 관련 특성들을 공유한다. 각 스레드는 자신만의 스택에서 실행되나, 모든 스레드는 자신과 같은 프로세스에 속한 다른 스레드의 스택에 접근할 수 있다. 스레드들이 같은 메모리에 접근할 수 있기 때문에, 일관성이 깨지지 않게 하려면 공유 자료에 대한 여러 스레드들의 접근을 동기화할 필요가 있다.

프로세스처럼 스레드도 ID로 식별한다. 그러나 스레드 ID들은 한 프로세스 안에서만 유효하다. 즉, 한 프로세스의 스레드 ID는 다른 프로세스에서는 아무런 의미도 없다. 스레드 ID는 프로세스 안에서 스레드들을 다룰 때 특정 스레드를 지칭하는 용도로 쓰인다.

스레드를 제어하는 함수들은 프로세스를 제어하는 데 쓰이는 함수들과 비슷하다. 그러나 스레드가 프로세스 모형이 확립되고 한참 후에 UNIX 시스템에 추가되었기 때문에, 스레드 모형과 프로세스 모형에는 다소 까다로운 상호작용이 존재한다. 4

참고문헌

  • UNIX 고급 프로그래밍 [제3판] / 리처드 스티븐스, 스티븐 레이고 공저 / 류광 역 / 퍼스트북 / 인쇄일: 2014년 08월 28일 / 원제: Advanced Programming in the UNIX Environment
  • 멀티프로세서 프로그래밍 / 모리스 헐리히, 니르 샤비트 공저 / 김진욱, 하재승 공역 / 한빛미디어 / 초판발행 2009년 07월 16일 / 원제: The Art of Multiprocessor Programming
  • 자바 병렬 프로그래밍 / 브라이언 게츠, 더그 리, 조슈아 블로쉬 등저 / 에이콘출판사 / 초판 2쇄 발행: 2016년 03월 22일

주석

  1. UNIX 고급 프로그래밍. 11.1장. 471쪽. 

  2. 자바 병렬 프로그래밍. 1.1장. 30쪽. 

  3. 멀티프로세서 프로그래밍. B.2장. 616쪽 

  4. UNIX 고급 프로그래밍. 1.6장. 17쪽