[JSCODE] - OS 면접 스터디 4주차

@kdkdhoho · November 30, 2023 · 15 min read

📝 질문 리스트

병행성(동시성)과 병렬성에 대해 설명해주세요.

[병행성]
병행성은 코어 1개를 가진 CPU가 2개 이상의 작업을 처리하는 것을 생각하면 됩니다.

이는 마치 사용자 입장에서 동시에 실행되는 것처럼 보이는데요.
실제로 두 작업을 동시에 실행하는 것이 아닌, 두 작업을 매우 빠른 속도로 번갈아가면서 처리를 합니다. 이 속도가 매우 빠르기에 사용자 입장에서는 동시에 처리되는 것처럼 보입니다.

[병렬성]
코어 2개를 가진 CPU가 2개 이상의 작업을 병렬적으로 처리한다고 생각하면 됩니다.

두 방식 모두 여러 작업이 공유 자원에 접근할 때 생길 수 있는 여러 문제가 존재합니다.

프로세스 동기화에 대해 설명해 주세요.

여러 작업이 공유 자원을 사용할 때 생길 수 있는 동시성 문제를 방지하기 위한 개념입니다.
이를 적용하는 방법으로는 스핀락, 뮤텍스, 세마포어가 있습니다.

Critical Section(임계 영역)에 대해 설명해주세요.

여러 프로세스가 공유 자원을 사용하려고 할 때, 그 공유 자원에 접근해서 사용하는 과정까지의 영역입니다. 프로세스 동기화를 위해 사용되는 개념입니다.

Race Condition(경쟁 조건)이 무엇인가요?

여러 프로세스/쓰레드가 동시에 같은 자원에 접근하여 조작할 때, 작업들의 타이밍이나 작업 순서에 따라 결과가 기대했던 것과 달라지는 상황을 의미합니다.

<자세한 예시>
두 개의 쓰레드가 하나의 전역 변수를 수정하는 상황을 가정해보겠습니다.
전역 변수를 +1 하려는 작업은 CPU 레벨에서 값 조회, 연산, 갱신의 순으로 동작합니다.
만약 쓰레드 1번이 값 조회를 하고, 값을 더한 상태에서 쓰레드 2번으로 컨텍스트 스위칭이 일어난다면, 쓰레드 2번은 아직 변경되지 않은 전역 변수 값을 읽고, 더하게 됩니다. 이 상태에서 두 쓰레드 모두 메모리에 값을 갱신하다면 어떤 순서든 간에 1로 갱신됩니다.
이러한 상황을 경쟁 조건이라고 합니다.

이 경쟁 조건은, 자원에 접근하여 사용하는 영역을 임계 영역으로 설정하고 이 임계 영역에 오직 하나의 쓰레드만이 접근할 수 있도록 구현함으로써 해결할 수 있습니다.
대표적인 예로는 스핀락, 뮤텍스, 세마포어 방식이 있습니다.

Mutual Exclusion(상호 배제)에 대해 설명해주세요.

한국말로 '상호 배제' 라는 말입니다.
즉, 하나의 쓰레드가 공유 자원을 사용할 때 다른 쓰레드가 접근하지 못하도록 한다는 개념입니다.

뮤텍스(Mutex)에 대해 설명해주세요.

Mutual Exlcusion의 약자로, 상호 배제를 위한 동기화 기법 중 하나입니다.
**하나의 임계 영역**에 **하나의 프로세스나 쓰레드**만 들어가 작업을 할 수 있다는 개념입니다.

이 기법은 뮤텍스 락이라는 개념을 사용합니다.

보통 임계 영역에 하나의 공유 자원이 존재합니다.

**상호 배제를 할 수 있는 방법**으로는 **스핀락 방식과 뮤텍스 방식**이 존재합니다.
두 방법 모두 **Mutex Lock이라는 개념을 사용**합니다. 이 락은 1개만 존재합니다.
임계 영역에 접근할 때 이 락을 획득할 수 있다면 락을 획득하고 임계 영역에 접근합니다.
만약 이때 다른 프로세스가 임계 영역에 접근하려고 할 때 이 락을 획득할 때까지 임계 영역에 접근할 수 없습니다. 락을 획득하고 나서야 비로소 임계 영역에 접근 가능합니다.

여기서 락을 획득하는 방식의 차이가 스핀락 방식과 뮤텍스 방식의 차이입니다.

스핀락 방식의 경우 락을 획득할 때까지 계속해서 확인합니다.
이와 다르게 뮤텍스 방식의 경우, 락을 획득하지 못하면 본인을 대기 큐에 넣어두고 임계 영역에서 처리 중인 프로세스가 락을 반환할 때, 대기 큐에 있는 프로세스를 깨우는 방식입니다.

스핀락 방식의 경우 락을 획득할 때까지 CPU를 무한정 사용할 수 있는 문제점이 있지만, 멀티 코어 환경이며 임계 영역에서의 작업 시간이 컨텍스트 스위칭보다 더 빨리 끝난다면 뮤텍스에 비해 더 이점이 있다.
뮤텍스는 대기 큐로 갔다가, 깨움을 당해 다시 CPU를 할당받는 과정에서 컨텍스트 스위칭 작업이 필요하다. 하지만, 임계 영역에서의 작업이 빨리 끝나면 굳이 대기 큐로 갔다오는 작업과 컨텍스트 스위칭 작업이 불필요해지기 때문이다.
게다가 싱글 코어에서 스핀락 방식은 컨텍스트 스위칭이 필연적으로 발생하기 때문이다. 멀티 코어에서 스핀락 방식을 적용한다면 컨텍스트 스위칭없이 Core1은 Lock을 점유하고 Core2는 Lock에 대해 계속해서 대기하는 상황이라면 컨텍스트 스위칭없이 더 빠르게 Lock을 획득할 수 있는 것이다.

세마포어에 대해 설명해주세요.

여러 개의 쓰레드가 공유 자원에 동시에 접근하는 것을 **제한하기 위한 정수**의 개념입니다.

뮤텍스와 달리 하나의 임계 영역에 여러 개의 공유 자원이 존재할 수 있습니다.
따라서 임계 영역에 존재하는 공유 자원의 수만큼 정수가 초기화되고, 쓰레드가 세마포어에 접근 요청을 하면 정수를 1 감소시킵니다.
만약 이 값이 0이라면 현재 임계 영역 내의 모든 공유 자원을 각각의 쓰레드가 접근하고 있는 것이므로 더 이상 접근할 수 없습니다.
이때, 다른 쓰레드가 접근 요청을 하게 되면 이 정수값은 -1로 감소하고 대기 큐에 들어갑니다.
이후에 공유 자원을 모두 사용한 쓰레드가 임계 영역을 나올 때, 세마포어 값을 1 증가하고 대기 큐에 있는 쓰레드를 깨웁니다. 그리고 해당 쓰레드가 다시 한번 세마포어에 접근하게 됩니다.

뮤텍스(Mutex)와 이진 세마포어의 차이에 대해 설명해주세요.

세마포어는 하나의 임계 영역안에 접근할 수 있는 공유 자원의 수를 조절할 수 있는 점에서, 이진 세마포어는 뮤텍스를 대신할 수 있습니다.

하지만 뮤텍스는 이진 세마포어를 대신할 수 없습니다.

추가로 세마포어는 작업 간 실행 순서를 동기화할 수 있지만, 뮤텍스는 불가능합니다. <### 추가 학습 필요

모니터에 대해 설명해주세요.

세마포어를 이용하다보면 개발자의 실수로 인해 경쟁 조건이나, 데드락 또는 기아상태가 발생한다.

하지만 이는 특정 상황에만 발생하는 문제들이므로 디버깅하기 매우 어렵다는 문제가 있다.

따라서 프로세스 동기화를 더 쉽고 확실하게 사용하기 위한 방법으로 모니터 방식이 탄생했다.

모니터 방식은 기본적으로 뮤텍스 방식을 사용해서 구현한다.

모니터 방식을 설명하기 위해선 생산자-소비자 문제가 대표적이다.

> 자세한 내용은 [테코톡](https://youtu.be/4t8BennljZA?si=lEfu-OTT2cMYlgrB)을 참고하자.

간단히 설명하자면, 생산자와 소비자 사이에는 함께 사용할 수 있는 공간이 있고 생산자는 이 공간에 데이터를 저장하고, 소비자는 데이터를 소비한다.
이때 공간이 꽉 차 있으면 생산자는 추가할 수 없고, 반대로 공간이 비어있으면 소비자는 소비할 수 없다.

이를 모니터 방식을 통해 구현할 수 있다.

각 메서드(생산자 or 소비자)는 락을 획득하여 임계 영역에 들어가면, 본인이 수행할 수 있는 조건인지를 먼저 확인한다. (공간이 비어있는지? 꽉찼는지?) 수행할 수 없는 조건이라면 본인을 대기 큐에 넣는다.

수행할 수 있는 조건이라면 정해진 동작을 하고 모두 끝이 나면 대기 큐에 있는 무작위의 쓰레드를 깨우고 락을 반환하는 형식이다.

데드락이 무엇인가요?

여러 프로세스가 필요한 자원을 요청하는 과정에서 그 어떤 프로세스도 더 이상 진행할 수 없는 상태입니다.
자원을 대기하는 blocked 상태에서 영구히 대기한다.

데드락 발생 조건 4가지를 설명해 주세요.

1. 상호 배제: 리소스는 한번에 하나의 쓰레드만이 접근할 수 있다.
2. Hold and wait: 쓰레드가 이미 하나 이상의 리소스를 획득한 상태에서 다른 리소스를 획득하기 위해 대기하는 상태
3. No preemption: 리소스 반환은 해당 리소스를 취득한 리소스만이 할 수 있다.
4. Circular wait: 쓰레드들이 순환 형태로 

데드락 회피 방법은 무엇이 있나요?

발생 조건 4가지 중 1가지만이라도 해결하면 된다.

  1. 리소스를 공유 가능하게 한다. -> 이는 경쟁 조건 문제 발생
  2. 사용할 리소스를 모두 획득한 뒤에 시작하도록 하거나, 리소스를 획득하지 않은 상태에서만 리소스를 요청하게끔 한다. -> 리소스 사용 효율이 떨어질 수 있다. 또는 인기 많은 리소스를 계속해서 기다려서 기아 상태에 발생할 수 있다.
  3. 추가 리소스를 획득하려면, 가지고 있는 리소스를 다른 리소스가 선점할 수 있도록한다.
  4. 모든 리소스에 순서 체계를 부여하고 오름차순으로 리소스를 요청한다.

하지만 모두 약간의 문제들이 존재한다. 따라서 데드락 회피(뱅커 알고리즘) 또는 데드락 감지 및 복구

🤔 개인적인 궁금증

세마포어의 정수값은 어떻게 동기화를 할 수 있을까?

세마포어의 wait(), signal()에서 lock을 획득하기 위한 메서드는 CPU 레벨에서 Atomic한 연산을 지원한다. 다시 말해, 하나의 쓰레드가 lock을 획득하기 위한 과정 도중에 다른 쓰레드가 연산 중간에 끼지 못한다.

이를 통해

🎯 피드백

임계 영역을 설명할 때 다소 설명이 장황해졌다. 그 외에는 두괄식으로 답변하려고 노력하는 것이 잘 보였다.

@kdkdhoho
기본에 충실하게