Process와 Thread 이야기

프로세스(Process)

다음과 같이 위키에서 프로세스의 정의를 발췌했다.

In computing, a process is the instance of a computer program that is being executed by one or many threads.

 

컴퓨터 분야에서 프로세스는 컴퓨터 프로그램의 인스턴스로, 하나 이상의 스레드를 통해 실행된다.

 

프로세스라는 명칭은 낯설수 있는데, 프로그램은 친숙하리라 생각된다. 우리가 흔히 말하는 프로그램이 실행되면 프로세스 인스턴스가 생성된다. 인스턴스가 생성된다는 의미는 프로그램 실행에 필요한 내용이 컴퓨터 메모리(Ram)에 적재된다는 뜻이다. 일반적으로 프로세스와 프로그램을 같은 개념으로 이야기할 때가 많다. 하지만 엄밀히 따지면 이 둘은 다른 개념이다.

  • 프로그램: 어떤 작업을 하기 위해 실행할 수 있는 파일 또는 프로그램
  • 프로세스: 메모리에 적재되고 CPU 자원을 할당받아 프로그램이 실행되고 있는 상태

그림1. 윈도우즈 운영체제에서 실행되고 있는 프로세스 목록

그림2. macOS 운영체제에서 실행되고 있는 프로세스 목록

그림3. 안드로이드에서 실행되고 있는 프로세스 목록

앞의 그림1,2,3과 같이 운영체제(OS)를 통해 여러 프로세스(프로그램)를 실행하고 관리할 수 있다. 이를 멀티태스킹이라고 한다.

멀티 태스킹

멀티태스킹이란 OS를 통해 CPU가 작업하는데 필요한 자원(시간)을 프로세스 또는 스레드간에 나누는 행위를 말한다. 이를 통해 여러 응용 프로그램을 동시에 열고 작업 할 수 있다는 장점이 있다. 

음악을 들으면서, 웹서핑을 하고, 메신저의 메시지를 확인할 수 있는 이유는 모두 멀티태스킹 덕분이다.

이를 다이어그램으로 표현하면 다음과 같다.

그림4. 여러 Process를 관리하는 운영체제의 모습을 도식화한 모델

그림에서는 여러 프로세스가 동시에 실행되고 관리 되는 것처럼 보이지만, 사실 CPU는 한번에 한가지 명령어밖에 처리하지 못한다. 즉, 동시가 아닌 재빠르게 프로세스들을 번갈아가며 실행하고, 관리하는 것이다. CPU는 상상이상으로 빠르기 때문에, 프로세스들을 번갈아가며 실행하고 관리하는 것이 마치 동시에 하는 것처럼 보일 뿐이다. 이를 Context Switching(문맥교환)이라고 한다. 

Context Switching: Context Switching이란 하나의 프로세스가 CPU를 사용 중인 상태에서 다른 프로세스가 CPU를 사용하도록 하기 위해, 이전의 프로세스의 상태를 보관하고 새로운 프로세스의 상태를 적재하는 작업을 말한다.

Context Switching은 운영체제의 CPU자원을 할당하는 스케줄러(Scheuler)에 의해 발생한다. CPU를 적절하고 효율적으로 사용할 수 있도록 하는 작업을 스케줄링이라 한다.

CPU Scheduler

스케줄러(Scheduler)는 레디 큐에 존재하는 프로세스들을 특정한 우선순위를 기반으로 CPU를 할당받게 해주는 역할을 한다. 스케줄링의 목적은 다음과 같이 3가지로 표현할 수 있다.

  • CPU를 최대한 활용하기
  • 대기 시간을 최소화 하기
  • 처리량을 최대화 하기

스케줄링은 멀티 태스킹 작업을 만들어내는 데에 있어서 핵심적인 개념이다.

스케쥴링을 구현하는데는 반드시 자료구조의 구현이 따르는데, 어떤 자료구조를 이용하는지가 스케쥴링에 중요한 요소이다. 대표적으로 Linked List, Hash List, Bitmap, Red-Black Tree 등이 있다.

이를 구현하는 스케쥴링 알고리즘 따라 스케쥴링 종류가 또 나뉜다.

  • FCFS: First Come, First Serve 의 약자로, 말그대로 선착순 알고리즘이다. 먼저 도착한 프로세스를 처리하는 스케쥴링
  • SJF: Shorted Job First의 약자로, 최단 작업을 우선하는 스케쥴링이다.
  • Priority Scheduling: 미리 주어진 프로세스의 우선 순위에 따라서 스케쥴링하는 방식이다.
  • RR: Round Robin의 약자로, 정해진 시간에 주어진 만큼 프로세스를 할당한 뒤 작업이 끝난 프로세스는 레디큐의 가장 마지막에 가서 재할당을 기다린다.
  • Multilevel-Queue: 레디큐를 여러개의 큐로 분류하여 각 큐가 각각 다른 스케쥴링 알고리즘을 가지는 방식
  • Multilevel-Feedback-Queue: Multilevel-Queue는 특정 프로세스가 큐에 고정되어 있지만, Multilevel-Feedback-Queue는 큐와 큐 사이에 프로세스가 이동하는 것을 허용한다.

PCB와 프로세스의 상태

프로세스 제어 블록(Process Control Block, 줄여서 PCB)은 특정한 프로세스를 관리할 필요가 있는 정보를 포함하는 운영체제 커널의 자료 구조이다. 

PCB가 필요한 이유는 바로 Context Switching 때문이다. CPU가 여러 프로세스를 빠르게 번갈아가면서 작업하기 위해서는 프로세스에 대한 정보 및 상태를 저장/복원 할 필요가 있다. 

운영체제에 따라 PCB에 포함되는 항목이 다를 수 있지만, 일반적으로 다음과 같은 정보가 포함된다.

  • 프로세스 식별자(PID;Process ID)
  • 프로세스 상태(Process State)
  • 프로그램 계수기(Program Counter)
  • CPU 레지스터 및 일반 레지스터
  • CPU 스케줄링 정보
  • 메모리 관리 정보
  • 프로세스 계정 정보
  • 입출력 상태 정보

프로세스 상태

그림5. 프로세스 상태

프로세스는 다음과 같은 생명주기를 갖는다.

  • new : 프로세스가 생성되는 상태
  • ready : 프로세스가 CPU에 할당되어, 처리되기를 기다리는 상태
  • running: 프로세스가 CPU에 할당되어, 명령어들이 실행 되는 상태
  • waiting: 어떤 이벤트의 발생으로 인해 프로세스가 기다리는 상태
  • terminated: 프로세스가 종료되는 상태

그림6. 프로세스 간 Context Switching

두 프로세스 P0과 P1이 있다고 가정하자. P0은 Running(실행중)인 상태, P1은 Ready(대기) 상태다. 이 상태에서 interrupt 또는 시스템 콜이 발생하면 P0은 Running이 아닌 Ready 상태로 전환된다. 추후 복원될 P0을 위해 해당 프로세스 정보를 PCB에 담아 저장한다. P1은 이전에 저장한 PCB로부터 상태를 복원하여 작업을 계속 수행한다. P1도 어느 시점이 되면 다시 Ready 상태가 되어 PCB에 상태를 담아 저장하고, P0이 PCB로부터 복원되어 작업을 계속 수행할 것이다.

이 작업들이 계속 해서 반복되는데, 이러한 일련과정들을 Context Switching이라고 한다. Context Switching도 메모리에 I/O를 하는 작업이기 때문에 실행되는 프로세스의 수가 많거나, Context Switching의 빈번한 발생은 Overhead를 발생시켜 성능 저하를 결과를 가져온다.

메모리 영역

그림4와 같이 하나의 프로세스는 각각 독립된 메모리 영역(Code, Data, Stack, Heap)을 할당받는다. 각 메모리 영역이 하는 일을 살펴보자.

코드(Code) 영역:

코드 영역은 실행할 프로그램의 코드 및 매크로 상수가 기계어 형태로 저장되는 영역이다. CPU는 코드영역에 저장된 명령어를 하나씩 처리한다. 

데이터(Data) 영역:

데이터 영역은 코드에서 선언한 전역 변수와 정적(static) 변수가 저장되는 영역이다. 데이터 영역은 프로그램의 시작과 함께 할당되어 종료될 때 소멸된다.

스택(Stack) 영역:

스택 영역은 함수 안에서 선언된 지역변수, 매개변수, 리턴값, 등이 저장되고 함수 호출시 기록하고 종료되면 제거한다. 스택이라는 자료구조 명칭에서도 알 수 있듯이 후위선출(LIFO) 메커니즘을 따른다.

흔히 재귀함수를 통해 너무 많은 함수를 호출하게 되는 경우 스택 영역이 초과하면서 Stack Overflow(스택오버플로우)에러가 발생한다. (여러분이 잘 아는 그 스택오버플로우가 여기서 따온것이다)

힙(Heap) 영역:

힙 영역은 관리가 가능한 데이터 이외의 다른 형태의 데이터를 관리하기 위한 공간(Free Space)이다. 이 공간은 동적 메모리 할당 공간이므로 사용이 끝나면 운영체제가 쓸수 있도록 반납해야 한다. 프로그램이 실행하는 순간 프로그램이 사용할 메모리 크기를 고려하여 메모리의 할당이 이루어지는 데이터 또는 스택과 같은 정적 메모리 할당과는 대조적이다. 동적 메모리 할당은 어느 시점에 어느 정도의 공간을 할당할 수 있을지 정확히게 예측할 수 없으므로, 런타임에 확인가능하다.

IPC(Inter-Process Commnuication)

그림7. 안드로이드의 Parcelable을 통해 IPC하는 다이어그램

각 프로세스는 별도의 공간에서 실행되기 때문에, 한 프로세스에서 다른 프로세스의 메모리영역에 접근할 수 없다. 만약 프로세스가 다른 프로세스 자원에 접근하려면 IPC(Inter-Process Commnuication)를 사용해야 한다.

IPC란 운영체제 상에서 실행 중인 프로세스 간에 정보를 주고받는 것을 말한다. 프로세스는 자신에게 할당된 메모리 내의 정보만 접근할 수 있는데, 이는 안전성을 위해 운영체제에서 자기 프로세스의 메모리만 접근하도록 강제하고 있다는 것이다.

IPC의 종류로는 메일슬롯, 파이프, 소켓, 시그널, 공유메모리 등이 있다. 안드로이드에서는 대표적인 IPC의 예가 Parcelable이다. 이외에도 AIDL과 같은 서비스 바인딩 등이 있다.

스레드(Thread)

다음과 같이 위키에서 스레드의 정의를 발췌했다.

In computer science, a thread of execution is the smallest sequence of programmed instructions that can be managed independently by a scheduler, which is typically a part of the operating system

 

컴퓨터 분야에서 실행되는 스레드는 일반적으로 운영체제의 일부인 스케줄러에 의해 독립적으로 관리 될 수 있는 프로그래밍 된 명령어의 가장 작은 시퀀스다.

그림8. 시간에 따른 프로세스와 스레드의 흐름

스레드는 어떠한 프로그램 내에서, 특히 프로세스 내에서 실행되는 흐름의 단위를 말한다. 일반적으로 하나의 애플리케이션(프로그램)은 하나 이상의 프로세스를 가지고 있고, 하나의 프로세스는 반드시 하나 이상의 스레드를 갖는다. 즉 프로세스를 생성하면 기본적으로 하나의 (메인)스레드가 생성되는 셈이다.

프로세스와 스레드 비교

그림9. 메모리 관점에서 본 프로세스와 스레드

프로세스는 실행될 때 운영체제로부터 각각 독립된 메모리 영역을 할당 받는다. 스레드는 한 프로세스 내에서 동작되는 흐름으로 프로세스 내에서 Stack 영역만 별도로 할당 받고, 부모 프로세스의 Code, Data, Heap 영역은 공유 한다. 즉, 프로세스내에서 자식 스레드들은 서로 주소 공간이나 자원들을 공유하면서 실행될 수 있다.

멀티 프로세스와 멀티 스레드

멀티 프로세스

멀티 프로세스란 하나의 애플리케이션을 여러 개의 프로세스로 구성하여 각 프로세스가 하나의 작업을 처리하도록 하는 것이다.

그림10. 멀티 프로세스

멀티 프로세스는 다음과 같은 특징이 있다.

  • 안정성이 좋다. 여러개의 자식 프로세스 중 하나에 문제가 발생해도, 다른 자식 프로세스에 영향이 확산되지 않는다.
  • 구현이 비교적 간단하고, 각 프로세스들이 독립적으로 동작하며 자원의 서로 다르게 할당된다.
  • 프로세스 간 통신을 하기 위해서는 IPC를 통해야 한다.
  • 메모리 사용량이 많다.
  • 스케쥴링에 따른 Context Switch이 많아지고, 성능 저하의 우려가 있다.

멀티 스레드

멀티 스레드란 하나의 애플리케이션을 여러 개의 스레드로 구성하여 하나의 스레드가 하나의 작업을 처리하도록 하는 것이다. 일반적으로 멀티스레드르 사용하는 이유는 사용자와 상호작용하는 애플리케이션에서 단일 스레드로 Network 또는 DB 와 같은 긴 작업(Long-running task) 을 수행하는 경우 해당 작업을 처리하는 동안 사용자와 상호작용이 불능인 상태가 될 수 있기 때문이다. 그림 11을 살펴보면 두개의 스레드가 서로에게 방해주지 않고, 각자 할일을 하는 모습을 보여주고 있다.

그림11. 멀티스레드

멀티 스레드는 다음과 같은 특징이 있다.

  • 응답성이 좋다. 프로그램의 일부분(자식 스레드)이 오류 또는 긴 작업으로 인해 중단되어도 프로그램이 계속 적으로 수행된다.
  • 자원 공유가 쉽다. 스레드들은 부모 프로세스의 자원과 메모리를 공유 할 수 있다.
  • 프로세스를 할당하는 것보다 스레드를 할당하는 것이 비용이 적다.
  • 멀티프로세서 구조에서 각각의 스레드가 다른 프로세서에서 병렬로 수행될 수 있다.
  • 구현 및 테스트, 디버깅이 어렵다
  • 너무 많은 스레드 사용은 오버헤드를 발생시킨다.
  • 동기화 그리고 교착상태가 발생하지 않도록 주의해야 한다.
  • 자식 스레드 중 하나에 문제가 생긴경우 전체 프로세스에 영향을 줄 수 있다.

멀티 프로세스와 멀티 스레드의 비교

멀티 스레드는 멀티 프로세스에 비해 상당한 이점을 가지는 반면 위험 부담도 따른다. 그 이유를 알아보자.

자원의 효율성증대

멀티 프로세스로 실행되는 작업을 멀티 스레드로 실행할 경우 프로세스를 생성하여 자원을 할당하는 비용이 적고, 스레드는 프로세스 내의 메모리를 공유하기 때문에 독립적인 프로세스와 달리 스레드 간 데이터를 주고 받는 것이 간단해지고 시스템 자원 소모가 줄어든다.

응답 시간 단축 및 처리 비용 감소

프로세스간 IPC를 사용하여 통신하는 것은 상대적으로 비용이 크다. 하지만 스레드는 프로세스의 메모리 영역을 공유하여 스레드 간의 통신 비용이 적게 든다. 또한 프로세스간의 Context Switching은 느린 반면 쓰레드간의 Context Switching 은 빠른데, 그 이유는 Context Switching  시 스레드는 Stack 영역만 처리하면 되기 때문이다.

멀티 스레드의 안정성 문제

여러 개의 스레드가 동일한 데이터 공간(Critical Section)을 공유하면서 이들을 수정한다는 점에 필연적으로 생기는 문제이다. 멀티 프로세스의 프로그램은 문제가 생기면 해당 프로세스가 중단되거나 중단 시키고 다시 시작 하면된다. 하지만 멀티 스레드 방식의 프로그램에서는 하나의 스레드가 자신이 사용하던 데이터 공간을 망가뜨린다면, 해당 데이터 공간을 공유하는 모든 스레드를 망가뜨릴 수 있다.

Critical Section: 임계 구역(critical section) 또는 공유변수 영역은 둘 이상의 스레드가 동시에 접근해서는 안되는 공유 자원(자료 구조 또는 장치)을 접근하는 코드의 일부를 말한다.

마치며

프로세스와 스레드의 대한 전반적인 내용을 간략하게 훑어보았다. 아주 기초적인 내용이지만, 막상 누군가에게 설명하라하면 쉽지 않은 내용들인 것 같다. 기초를 튼튼히 다지면 개발할 때도 큰 도움이 될거라 믿어 의심치 않으며 포스팅을 마친다. 

Buy me a coffeeBuy me a coffee
카테고리: etc

0개의 댓글

답글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.