[운영체제] 멀티프로세스와 멀티쓰레드

zunzero·2022년 9월 20일
0

CS

목록 보기
1/8

Process

프로세스란 실행파일(program)이 메모리에 적재되어 CPU를 할당받아 실행(연산)되는 것을 말한다.

실행파일로 존재하던 프로그램이 메모리에 적재되어 CPU에 의해 실행(연산)되는 것을 프로세스라고 한다.
프로세스가 메모리에 적재될 때는 4개의 구역으로 나뉜다.

stack, heap, data, code

stack영역부터 code영역까지 높은 메모리 주소부터 낮은 메모리 주소 순으로 메모리가 할당된다.

stack 영역은 함수 호출 시 생성되는 지역변수와 매개변수가 저장되는 메모리 영역으로, compile time에 메모리 영역의 크기가 결정된다.
heap 영역은 프로그래머가 직접 공간을 할당(malloc)/해제(free)하는 메모리 영역으로 runtime에 메모리 영역의 크기가 결정된다.
data 영역은 프로그램의 전역 변수와 static 변수가 저장되는 메모리 영역이다.
code 영역은 실행한 프로그램의 코드가 저장되는 메모리 영역이다.

PC register

어떤 코드를 읽어야 하는가를 정하는 것은 CPU 내부에 있는 PC (Program Counter) register에 저장되어 있다.
이는 다음에 실행될 코드(명령어)의 주소값을 저장한다.
즉, PC register가 가리키는 명령어를 CPU가 읽어와서 연산을 하게 되는 것이 Process가 실행이 되는 것이다.

멀티 프로세스

Multi Process란 2개 이상의 프로세스가 동시에 실행되는 것을 말한다.
CPU core의 개수에 따라 의미가 나뉘는데, CPU core의 개수가 1개라면 동시성(concurrency)을, CPU core의 개수가 여러개라면 병렬성(parallelism)을 의미한다.
요새는 CPU core가 죄다 여러개이긴 하다. ㅋㅋ

동시성

한 개의 CPU core는 사실 한 번에 하나의 연산 밖에 하지 못한다.
그런데 동시성이라고 불리는 이유는 매우 빠른 속도로 스위칭을 해가며 연산을 해나가기 때문에 동시에 여러 프로세스들이 처리되고 있는 것처럼 보이기 때문이다.

2개 이상의 프로세스가 동시에 실행되는 멀티 프로세스 환경에서, 각 프로세스들은 메모리 영역을 각자 차지하며 동시에 적재된다.
하나의 CPU는 매 순간 하나의 프로세스만 연산할 수 있는데, 앞서 말했듯 CPU의 처리 속도가 워낙 빨라서 수 ms 이내의 짧은 시간동안 여러 프로세스들이 CPU에서 번갈아 실행되기 때문에 사용자 입장에서는 여러 프로세스가 동시에 실행되는 것처럼 보인다.
이처럼 CPU의 작업시간을 여러 프로세스들이 조금씩 나누어 쓰는 시스템을 시분할 시스템(time sharing system)이라고 부른다.

여러 프로세스가 동시에 메모리에 적재된 경우, 서로 다른 프로세스 영역을 침범하지 않도록 운영체제가 관리해준다.

싱글 코어 환경에서 CPU는 하나의 명령어만 처리할 수 있기 때문에, 각 프로세스가 적재된 메모리 주소 중 PC register가 가리키는 주소의 명령어를 읽어와서 연산을 처리한다.

Context

시분할 시스템에서는 한 프로세스가 매우 짧은 시간동안 CPU를 점유하여 일정부분의 명령을 수행하고, 다른 프로세스에게 넘긴다.
그 후 차례가 되면 다시 CPU를 점유하여 명령을 수행한다.
따라서 이전에 어디까지 명령을 수행했고, register에는 어떤 값이 저장되어 있었는지에 대한 정보가 필요하다.

프로세스가 현재 어떤 상태로 수행되고 있는지에 대한 총체적인 정보가 바로 Context이다.
Context 정보들은 PCB(Process Control Block)에 저장한다.

PCB

PCB는 운영체제가 프로세스를 표현한 자료구조이다.
이는 프로세스의 중요한 정보를 포함하기 때문에 접근이 보호된 메모리 영역 안에 저장이 된다. (커널 스택 같은 곳, 이는 보호를 받으면서도 비교적 접근이 용이함.)

Context Switch

이전에 스프링부트 공부 중, 멀티 쓰레드를 공부하며 컨텍스트 스위칭 비용에 관련한 이야기를 언급한 적이 있다.
Context Switch란 한 프로세스에서 다른 프로세스로 CPU 제어권을 넘겨주는 것을 말한다.
이전의 프로세스의 상태를 PCB에 저장하여 보관하고 새로운 프로세스의 PCB를 읽어서 보관된 상태를 복구하는 작업이 이루어지는 것이다.

Process1에서 Process2로, 다시 Process2에서 Process1으로 컨텍스트 스위칭이 일어났다.

Process State

프로세스는 실행(running), 준비(ready), 봉쇄(wait, sleep, blocked) 3가지 상태로 구분된다.
실행 상태는 프로세스가 CPU를 점유하고 명령을 수행 중인 상태를 말한다.
준비 상태는 CPU만 할당 받으면 즉시 명령을 수행할 수 있도록 준비된 상태이다.
봉쇄 상태는 CPU를 할당 받아도 I/O 잘업을 기다리는 경우 등의 이유로 명령을 실행할 수 없는 상태를 말한다.

IPC

멀티 프로세스 환경에서, 프로세스는 각각 독립적인 메모리 주소 공간을 차지한다.
때문에 다른 프로세스의 주소 공간을 참조할 수 없다.
따라서 운영체제는 프로세스 간의 자원 접근을 위한 매커니즘인 프로세스 간 통신(IPC, Inter Process Communication)을 제공한다.

공유 메모리 (shared memory)

공유 메모리 방식에서는 프로세스들이 주소 공간의 일부를 공유한다.
공유한 메모리 영역에 읽기/쓰기를 통해서 통신을 수행한다.
프로세스가 공유 메모리 할당을 커널에 요청하면, 커널은 해당 프로세스에 메모리 공간을 할당해준다.
공유 메모리 영역이 구축된 이후에는 모든 접근이 일반적인 메모리 접근으로 취급되기 때문에 더 이상 커널의 도움 없이도 각 프로세스들이 해당 메모리 영역에 접근할 수 있다.
따라서 커널의 관여가 없이 데이터를 통신할 수 있기 때문에 IPC 속도가 빠르다.
하지만 동시에 같은 메모리 위치에 접근하게 되면 일관성 문제가 발생할 수 있어 동기화 문제가 있다는 단점이 있다.

메시지 전달 (message passing)

메시지 전달 방법은 통상 system call을 사용하여 구현된다.
커널을 통해 send(message)와 receive(message)라는 두 가지 연산을 제공받게 된다.
커널의 관여로 인해 메모리 공유보다는 속도가 느리지만, 충돌을 회피할 필요가 없기 때문에 적은 양의 데이터를 교환하는 데에는 유용하다는 장점이 있다.
구현 또한 쉽다.
대표적인 예시로 pipe, socket, message queue 등이 있다.

Thread

쓰레드란 한 프로세스 내에서 실행되는 동작(기능 func)의 단위이다.

스레드는 프로세스 내에서 독립적인 기능을 수행한다.
즉, 독립적인 함수를 호출함을 의미한다.
스레드는 프로세스의 stack 메모리를 제외한 나머지 메모리 영역을 공유할 수 있다.

Multi Thread

멀티 쓰레드란 하나의 프로세스가 동시에 여러 개의 일을 수행할 수 있도록 해주는 것이다.
하나의 프로세스(실행이 된 하나의 프로그램)에서 여러 작업을 병렬로 처리하기 위해 멀티 스레드를 사용한다.
멀티 스레드에서는 한 프로세스 내에 여러 개의 스레드가 있고 각 스레드들은 stack 메모리를 제외한 나머지 영역(code, data, heap)을 공유한다.

stack 메모리를 공유하지 않고 스레드 고유의 stack 메모리가 필요한 이유는, 스레드는 프로세스 내에서 독립적인 기능을 수행하기 때문에 독립적으로 함수를 호출한다.
그래서 각자 stack 메모리가 필요한 것이다.
PC register 또한 독립적으로 필요하다.

Stack Memory & PC register

스레드가 함수를 호출하기 위해서는 인자 전달, return address 저장, 함수 내 지역변수 저장 등을 위한 독립적인 stack 메모리 공간을 필요로 한다.
따라서 스레드는 프로세스로부터 stack 메모리 영역은 독립적으로 할당 받고, code/data/heap 영역은 공유한다.

또한 멀티 스레드에서는 각각의 스레드마다 PC register를 갖고 있어야 하는데, 그 이유는 한 프로세스 내에서도 스레드끼리 context switch가 일어나기 때문이다.
PC register에 명령어 주소가 저장되어 있어야 이어서 실행이 가능하기 때문이다.

Multi Process Vs. Multi Thread

  • 멀티 스레드는 멀티 프로세스보다 적은 메모리 공간을 차지하고 Context Switching이 빠르다.
  • 멀티 프로세스는 멀티 스레드보다 많은 메모리 공간과 CPU 시간을 차지한다.
  • 멀티 스레드는 동기화 문제와 하나의 스레드 장애로 전체 스레드가 종료될 위험이 있다.
  • 멀티 프로세스는 하나의 프로세스가 죽더라도 다른 프로세스에 영향을 주지 않아 안정성이 높다.

메모리 구분이 필요할 때는 멀티 프로세스가, Context Switching이 자주 일어나고 데이터 공유가 빈번한 경우에는 멀티 스레드가 유리하다.

멀티 스레드는 메모리 공간과 시스템 자원 소모를 줄일 수 있다.
스레드 간의 통신 비용은 프로세스 간의 통신(IPC)보다 적어 통신으로 인한 오버헤드가 적다.

멀티 스레드에서 Context Switching이 빠른 이유는 Context Switching 시 캐시 메모리를 초기화할 필요가 없기 때문이라고 한다.

하지만 스레드 간의 자원 공유 문제 때문에 동기화 문제가 발생할 수 있어, 이를 고려한 프로그램 설계가 필요하다.

동기화 문제 해결 - Semaphore

동기화 문제는 멀티 프로세스/스레드 환경에서 서로 다른 스레드가 메모리 영역을 공유하기 때문에 여러 스레드가 동일한 자원에 동시에 접근하여 엉뚱한 값을 읽거나 수정하게 되어서 발생하는 문제이다.

Semaphore란 S개의 스레드만이 공유 자원에 접근할 수 있도록 제어하는 동기화 기법이다.
Semaphore 기법에서는 정수형 변수 S(세마포)값을 가용한 자원의 수로 초기화 하고, 자원에 접근할 때는 'S--' 연산을, 자원을 방출할 때는 'S+++' 연산을 수행한다.
즉, S가 0일 때는 모든 자원이 사용 중임을 의미한다.
자원을 사용하려는 프로세스는 S가 0보다 큰 상태까지 block되고, S가 0보다 커지면 자원을 이용할 수 있다.
S값이 1인, 즉 가용한 자원의 수가 1개인 경우에는 이를 Mutex라고 부른다. (binary semaphore)

count++ 연산을 위해서는, 메모리에서 count값을 가져오는 연산과 count값을 1 증가시키는 연산과 증가된 1을 메모리에 다시 저장하는 연산이 필요하다.
총 3번의 연산이 필요한 것이다.
그런데 중간에 같은 count 변수를 이용하는 연산이 끼어들면서 문제가 발생한 것이다.

임계 영역 (critical section)

둘 이상의 프로세스/스레드가 동시에 동일한 자원에 접근하도록 하는 프로그램 코드 부분을 의미한다.
한 프로세스/스레드가 자신의 임계구역에서 수행하는 동안에는 다른 프로세스/스레드들은 그들의 임계구역에 들어갈 수 없어야 한다.
즉, 임계구역 내의 코드는 원자적으로(atomically) 실행이 되어야 한다.
따라서 각각의 프로세스/스레드는 자신의 임계구역에 진입하려면 진입 허가를 요청해야 한다.
진입 허가를 entry section, 임계영역이 끝나고 나면 exit section으로 퇴출하게 된다.
이로 인해 임계영역의 원자성이 보장되고 프로세스/스레드들이 동기화될 수 있도록 한다.
이를 위한 동기화 방법이 Semaphore이다.

Mutex

mutual exclusion의 축약어이다.
공유 자원에 접근할 수 있는 프로세스/스레드의 수가 1개로 제한된 semaphore 기법이다.
임계영역을 보호하고 경쟁상황을 방지하기 위해 mutex lock을 사용한다.
임계영역에 들어가기 전에는 반드시 lock을 획득해야 하고, 임계영역에서 나올 때는 lock을 반환해야 한다.

위 그림에서 스레드2는 임계영역 접근 권한을 얻을 때까지 busy waiting을 해야한다.
busy waiting은 다른 프로세스/스레드가 생산적으로 사용할 수 있는 CPU를 낭비한다는 단점이 있다.

Semaphore

mutex 기법에선 lock을 획득하고 반환하는 과정을 통해 acquire()와 release()가 구현됐지만, semaphore는 다르다.
semaphore는 S(세마포)값에 'S--'연산을 적용하고, 'S++' 연산을 적용하면서 임계영역으로 접근했다고 빠져나온다.

사실 S값을 1로 설정하고 semaphore 기법을 사용하면 그게 그냥 mutex 기법이다.

profile
나만 읽을 수 있는 블로그

0개의 댓글