본문 바로가기
프로그래밍/CS

CS스터디 2주차 - 인터럽트 데드락

by 숙님 2023. 3. 17.
728x90

 CS스터디의 한 주 분량의 공부해야 할 양이 많고 다들 열심히여서 다음 주 스터디 내용을 미리 정리해 보았다

다음 주 수요일 스터디시간까지 계속 더 공부할 계획이다 


1. 임계구역이란 무엇이고 어떻게 해결하나요 

임계구역(Critical Section)은 여러 스레드나 프로세스가 동시에 접근하면 문제가 발생할 수 있는 공유 자원을 사용하는 코드 영역을 말합니다. 이러한 임계구역 문제를 해결하기 위해서는 상호배제(Mutual Exclusion) 기법을 사용해 스레드나 프로세스가 임계구역에 접근할 때 다른 스레드나 프로세스가 접근하지 못하도록 제어합니다. 대표적으로 세마포어(Semaphore)나 뮤텍스(Mutex) 등이 사용됩니다.


2. 세마포어와 뮤텍스란

세마포어는 공유 자원에 대한 접근 제어를 위한 동기화 기법 중 하나로, 카운터를 이용하여 스레드의 접근을 제어합니다.

뮤텍스는 (Lock) 언락(Unlock) 이용하여 공유 자원에 대한 접근 제어를 위한 동기화 기법 하나로, 번에 하나의 스레드만 접근할 있도록 합니다.

 

- Race Conditon란 무엇인가요 

Race Condition(경쟁 상태)이란, 멀티스레드 환경에서 여러 스레드가 동시에 공유 자원을 접근하려고 , 결과가 예상과 다르게 나타나는 현상을 말합니다. 이는 예측 불가능한 결과를 초래할 있으며, 보통 동기화 기법을 사용하여 이를 해결합니다.

 

- Pipe, Socket이란 

파이프(Pipe)는 운영체제에서 제공하는 프로세스 간 통신(IPC) 방법 중 하나로, 단방향 통신 방식입니다.

소켓(Socket) 네트워크 상에서 프로세스 통신을 위한 엔드포인트를 제공하는 소프트웨어 인터페이스입니다. TCP/IP 프로토콜에서 사용되며, 클라이언트와 서버 간의 양방향 통신이 가능합니다.

 

- IPC가 무엇이고, 어떤 종류가 있는지 설명해 주세요

IPC(Inter-Process Communication)는 프로세스 간의 통신을 위한 메커니즘입니다. 대표적인 IPC 방법으로는 파이프, 메시지 큐, 공유 메모리, 소켓 등이 있습니다. 이를 이용하여 프로세스는 데이터를 주고받고, 서로 동기화를 유지할 수 있습니다.

 

- Shared Memory 사용할 때 주의해야 할 점

동기화: 여러 프로세스가 공유 메모리에 접근할 때, 동기화를 위한 메커니즘이 필요합니다.
메모리 누수: 공유 메모리를 사용하는 프로세스가 비정상적으로 종료될 경우, 메모리 누수가 발생할 수 있습니다.
충돌 방지: 공유 메모리에 대한 충돌 방지를 위해, 키(Key) 값을 잘 관리해야 합니다.

 

- 메시지 큐는 단방향이라고 할 수 있나

네, 메시지 큐(Message Queue)는 일방향 통신 방식으로, 보내는 프로세스는 큐에 메시지를 넣고, 받는 프로세스는 큐에서 메시지를 가져옵니다. 따라서, 보내는 쪽과 받는 쪽이 명확히 구분되며, 양방향 통신이 필요한 경우, 2개의 메시지 큐를 사용해야 합니다.

 

- Thread Safe 하다는 것은 어떤 의미인가

Thread Safe란 멀티스레딩 환경에서 동시에 여러 스레드가 공유하는 자원에 대해 안전하게 접근할 수 있다는 것을 의미합니다. 즉, 어떤 스레드가 자원을 사용하고 있을 때, 다른 스레드가 동시에 그 자원에 접근해도 문제가 발생하지 않는 것을 말합니다. 이를 보장하기 위해서는 동기화 기법을 사용하거나, Thread Safe 한 자료구조를 사용해야 합니다.

 

- Thread Safe 를 보장하기 위해 어떤 방법을 사용할 수 있나

동기화 기법: 뮤텍스(Mutex), 세마포어(Semaphore), 모니터(Monitor) 등의 동기화 기법을 사용하여 여러 스레드가 공유하는 자원에 대한 접근을 제어합니다.
원자성(Atomicity) 보장: 원자성을 보장하는 연산을 사용하여 여러 스레드가 동시에 실행되더라도 결과가 일관성 있게 유지되도록 합니다.
Thread Safe한 자료구조 사용: 스레드 안전성을 보장하는 자료구조를 사용하여 여러 스레드가 동시에 접근해도 안전하게 데이터를 처리할 수 있도록 합니다. 예를 들어, ConcurrentHashMap, CopyOnWriteArrayList 등이 있습니다.

- Peterson's Algorithm 이 무엇이며, 한계점에 대해 설명해 주세요

Peterson's Algorithm은 상호 배제 문제를 해결하기 위한 알고리즘 중 하나로, 두 개의 프로세스가 공유 자원에 접근하는 경우 사용됩니다. 이 알고리즘은 각 프로세스가 자원에 접근하기 전에 상대방의 작업이 끝나길 기다린 후 자원에 접근합니다.
하지만 Peterson's Algorithm은 두 개의 프로세스만 다룰 수 있으며, 여러 프로세스가 동시에 자원에 접근하는 경우 불안전해질 수 있습니다. 또한 busy waiting 문제가 발생하여 CPU 사용률이 높아지는 단점도 있습니다. 따라서 더 많은 프로세스를 다루는 더욱 효율적인 상호 배제 알고리즘이 개발되었습니다.


3. 데드락이란 

데드락(Deadlock)은 여러 프로세스나 스레드가 서로 자원을 점유하고 있어, 더 이상 진행될 수 없는 상태를 말합니다. 이 상태에서는 모든 프로세스나 스레드가 기다리기만 하고, 아무 일도 일어나지 않게 됩니다. 데드락 상태를 해결하기 위해서는 프로세스나 스레드가 점유한 자원을 강제로 해제하는 방법 등의 대응책이 필요합니다.

 

- Deadlock이 일어나지 않게 만드는 방법

--상호배제를 사용해 임계구역에 대한 접근을 제어
--교착상태의 발생 가능성을 제거하거나 최소화
--자원 할당 순서를 일정하게 유지하여 교착상태가 발생하지 않도록 함
--자원 할당 시 자원 요청의 우선순위를 부여하여 교착상태를 회피
--교착상태가 발생한 경우, 하나 이상의 프로세스나 스레드가 점유한 자원을 강제로 해제하여 교착상태를 해제

 

- 코드적으로 예방하는 방법

데드락을 코드적으로 예방하는 방법 중 하나로는 상호배제, 점유대기, 비선점, 순환대기 4가지 조건 중 하나를 깨뜨리는 것입니다. 예를 들어, 자원 요청 시 다른 프로세스가 해당 자원을 이미 점유하고 있다면, 해당 자원을 요청한 프로세스는 대기하거나, 이미 점유한 자원을 강제로 해제하고 다른 프로세스에게 할당하는 방법을 사용할 수 있습니다. 이러한 방법으로 상호배제, 점유대기, 비선점, 순환대기 중 하나의 조건을 깨뜨려서 데드락을 예방할 수 있습니다.

 

- wait free / lock free

Wait-free와 lock-free는 락 기반 동기화와 달리, 공유 자원에 대한 접근을 대기하지 않고 즉시 수행하는 비차단 동기화 기법입니다. Wait-free는 모든 스레드가 어떤 작업을 완료하기 위해 대기하지 않고, 일련의 스레드 동작이 최대한 진행되도록 하는 것을 의미하고, Lock-free는 적어도 하나의 스레드가 작업을 완료하기 위해 대기하지 않는 것을 의미합니다. 이러한 방식으로 Wait-free와 Lock-free는 데드락의 위험이 없고, 다중 스레드 환경에서도 안정적인 동작을 보장합니다.

 

- Deadlock이 동작하기 위한 4가지 조건에 대해 설명해 주세요

상호 배제(Mutual Exclusion) : 하나의 자원은 한 번에 한 프로세스 또는 스레드만 사용할 수 있어야 합니다.
점유 대기(Hold and Wait) : 프로세스나 스레드가 자원을 가지고 있으면서 다른 자원을 요청할 수 있습니다.
비선점(Non-preemption) : 다른 프로세스나 스레드가 자원을 강제로 빼앗을 수 없습니다.
순환 대기(Circular Wait) : 프로세스나 스레드 간의 자원 요청이 사이클 형태로 구성되어 있습니다. 즉, A는 B가 가진 자원을, B는 C가 가진 자원을, C는 A가 가진 자원을 요청하는 상황입니다.

 

- 그렇다면 데드락 동작위한 4가지 조건 중 3가지만 충족하면 왜 Deadlock 이 발생하지 않을까요?

3가지 조건만 충족되면 교착 상태는 발생하지 않더라도, 다른 문제가 발생할 수 있으므로 4가지 조건을 모두 충족시키지 않는 것이 좋습니다.
예를 들어, 4가지 조건 중 하나인 자원 점유와 대기 조건이 없을 경우에는 교착 상태가 발생하지 않습니다. 이 경우, 프로세스는 필요한 자원을 얻지 못할 때 다른 자원을 사용하거나 대기하지 않고 바로 중단됩니다. 그러나 이 경우, 다른 문제가 발생할 수 있습니다. 예를 들어, 우선순위가 높은 프로세스가 계속해서 필요한 자원을 얻는 경우, 우선순위가 낮은 프로세스는 영원히 실행되지 않을 수 있습니다. 따라서, 4가지 조건 중 하나라도 충족되지 않도록 하는 것이 중요합니다.

 

- 어떤 방식으로 예방할 수 있을까요?

--교착 상태를 예방하기 위한 방법으로 자원 할당 순서를 일정하게 유지하는 방법이 있습니다.
--자원 요청에 대한 대기 시간제한을 두어 일정 시간 내에 필요한 자원을 할당받지 못하면 자원을 반납하도록 하는 방법이 있습니다.
--자원 요청 시 다른 프로세스의 자원을 선점하여 사용하는 방법이 있습니다.
--자원을 독점하지 않고 공유하는 방법이 있습니다.
이러한 방법으로 교착 상태를 방지할 수 있으나, 각각의 방법은 자원 활용도를 저하시키거나 오버헤드를 초래할 수 있기 때문에 적절한 상황에서 사용해야 합니다.

 

- 왜 현대 OS는 Deadlock을 처리하지 않을까요

Deadlock을 처리하기 위해서는 시스템 리소스의 사용 및 프로세스 간 상호작용을 강제로 조정하는 데에 많은 시스템 리소스와 처리 시간이 필요하며, 이는 성능에 부정적인 영향을 미치기 때문입니다. 대부분의 경우, 프로그래머들은 데드락을 예방하기 위해 코드를 작성하며, OS는 데드락이 발생할 때 다른 프로세스나 스레드에게 우선권을 부여하고 데드락을 예방하기 위한 일부 도구를 제공합니다.

 

- Wait Free와 Lock Free를 비교해 주세요

Wait-Free와 Lock-Free는 모두 멀티스레드 환경에서 동시성 문제를 해결하기 위한 방법입니다. 그러나 Wait-Free는 모든 스레드가 항상 수행을 보장하며, 블로킹 없이 동작합니다. 반면 Lock-Free는 모든 스레드가 항상 수행되지 않을 수 있지만, 블로킹을 최소화하고 경합 조건을 최소화하면서 동작합니다. Wait-Free가 보다 안전하지만 구현이 어려우며, Lock-Free는 구현이 상대적으로 쉬우나 경합 조건이 남아있을 수 있습니다.

 

- Deadlock의 발생 조건과 Decklock을 깨기 위해서 어떻게 해야 하나요? 

Deadlock 발생 조건은 상호배제, 점유대기, 비선점, 순환 대기입니다. 이를 깨기 위해서는 자원 할당에 대한 기아 상태(starvation)를 해결하거나, 자원 점유 순서를 일치시키는 등의 방법으로 상호배제 조건을 깨고, 자원 할당 시 자원을 미리 모두 할당하거나, 자원 할당 전에 모든 자원을 반납하는 등의 방법으로 점유대기와 순환 대기 조건을 깨야합니다.


4. 인터럽트는 언제 발생, 어떻게 처리하나요? 

인터럽트는 하드웨어나 소프트웨어에서 발생할 수 있으며, 실행 중인 작업을 중단하고 운영체제에게 제어권을 넘기는 신호입니다. 운영체제는 인터럽트를 처리하기 위해 인터럽트 벡터를 통해 적절한 핸들러 함수를 호출하며, 해당 작업을 수행한 뒤 인터럽트가 발생하기 전의 작업으로 복귀합니다. 인터럽트는 예기치 않은 상황에서 발생하므로, 이를 적절히 처리하지 않으면 시스템이 불안정해질 수 있습니다.

 

- Polling 방식에 대해 설명해 주세요

Polling 방식은 주기적으로 디바이스나 시스템 상태를 확인하는 방식으로, 프로그램이 무한 루프를 돌며 확인한다. 디바이스나 시스템 상태가 변경될 때까지 루프가 계속 실행되므로 리소스 낭비가 발생할 수 있다. 따라서 인터럽트 기반 방식으로 대체되었다.

 

- HW / SW 인터럽트에 대해 설명해 주세요

HW 인터럽트는 하드웨어 장치에서 발생하는 인터럽트로, 예를 들어 마우스나 키보드가 눌릴 때 발생합니다. 반면에 SW 인터럽트는 소프트웨어에서 발생하는 인터럽트로, 예를 들어 프로그램이나 서비스 요청에 의해 발생합니다. 둘 다 운영체제에서 처리됩니다.

 

- 한컴오피스 '한글'을 클릭 후 빈 화면의 커서가 깜빡이고 있다. 이때 hello world를 작성하면 컴퓨터 내부에서 어떤 일이 발생하는가?

--한컴오피스 '한글'을 클릭하면 운영체제는 해당 프로그램을 실행시키기 위해 프로세스를 생성

--이때 프로세스 내부에서 hello world를 작성하면, 운영체제는 해당 프로세스의 메모리 공간에 데이터를 쓰는 작업을 수행하고, 화면 출력 등의 작업은 디바이스 드라이버를 통해 실행

--디바이스 드라이버는 하드웨어와 상호작용하여 실제 출력을 담당

 

- DMA이란 

DMA (Direct Memory Access)는 CPU의 개입 없이 입출력 장치가 직접 메모리에 데이터를 읽고 쓰는 기술입니다. CPU가 인터럽트를 처리하거나 다른 작업을 수행하는 동안에도 입출력이 가능하므로 시스템의 전체 성능을 향상할 수 있습니다.

 

- 마우스로 한글 바로가기를 클릭했을 때 컴퓨터에서 일어나는 모든 일에 대해서 설명하세요

  1. Interrupt: 마우스 클릭은 입력 장치에서 인터럽트를 발생시킵니다. CPU 인터럽트를 인지하고 현재 실행중인 작업을 일시 중단하고 인터럽트 처리를 시작
  2. Process Scheduling: OS 마우스 클릭 이벤트를 인지하고 해당 이벤트에 대한 처리를 위해 적절한 프로세스를 스케줄링합니다. 이를 위해 OS CPU 사용 순서를 결정하고, 다른 프로세스들의 실행을 일시 중단하여 클릭 이벤트 처리에 필요한 리소스를 할당
  3. Disk Scheduling: 한글 바로가기 클릭 실행되는 프로그램이 저장된 디스크의 데이터를 읽기

5. 시스템콜에 대해 설명해 주세요 

시스템 콜(System Call)은 컴퓨터 운영체제의 서비스를 호출하는 인터페이스이다. 일반적으로 응용 프로그램에서 사용자 모드에서 실행되다가 커널 모드로 전환하여, 커널이 제공하는 하드웨어 리소스나 다른 시스템 서비스를 요청하거나 제어하기 위해 사용된다. 예를 들어, 파일 입출력, 네트워크 통신, 메모리 할당과 같은 작업을 수행하기 위해 사용된다.

 

- Syscall이 여러 개 있는데 어떻게 찾아가나?

각 운영체제는 시스템 콜에 대한 API를 제공하며, 이를 통해 프로그래머가 시스템 콜을 호출할 수 있습니다. 일반적으로 이러한 API는 라이브러리의 형태로 제공되며, 프로그래머는 이를 사용하여 필요한 시스템 콜을 호출합니다. 각 시스템 콜은 고유한 식별자를 가지고 있으며, 운영체제가 이를 식별하여 해당 동작을 수행합니다. 일부 운영체제에서는 시스템 콜에 대한 문서화가 제공되어 있으며, 이를 참조하여 필요한 시스템 콜을 식별할 수 있습니다.

 

- Syscall의 매개 변수

시스템 콜을 호출할 때 필요한 입력값으로, 함수 호출 시 전달되는 인자 값들을 의미합니다. 이 매개 변수들은 시스템 콜의 종류에 따라 다르며, 일반적으로 시스템 콜의 호출번호와 함께 전달됩니다. 이 매개 변수들은 커널 내부에서 해당 시스템 콜의 기능을 수행할 때 사용됩니다.

 

- 우리가 사용하는 시스템 콜의 예시를 들어주세요

파일 읽기/쓰기와 관련된 시스템 콜인 read(), write(), open(), close()가 대표적인 예시입니다. 또한 프로세스 관리를 위한 fork(), exec(), wait() 등도 사용됩니다. 이 외에도 네트워크, 시그널, 메모리 등 다양한 시스템 리소스를 조작하기 위한 다양한 시스템 콜이 있습니다.

 

- 시스템 콜이, 운영체제에서 어떤 과정으로 실행되는지 설명해 주세요

일반적으로 시스템 콜은 응용 프로그램에서 특정 기능이 필요할 때 호출됩니다. 이때 응용 프로그램은 사용하려는 시스템 콜에 해당하는 인자들을 설정하고, 시스템 콜 번호를 지정한 후에 시스템 콜을 실행하는 특별한 명령어를 호출합니다.
시스템 콜을 실행하기 위해서, 운영체제는 인터럽트를 발생시켜 커널 모드로 전환합니다. 이때 CPU는 사용자 모드에서 커널 모드로 전환되며, 현재 실행 중인 응용 프로그램의 상태를 보관하고 커널 모드에서의 실행을 위한 준비 작업을 수행합니다. 그리고 해당 시스템 콜의 코드가 실행됩니다.
시스템 콜 코드가 실행되면, 운영체제는 해당 시스템 콜의 목적에 맞게 작업을 수행하고 결과를 반환합니다. 이후에는 CPU는 다시 사용자 모드로 전환되며, 해당 시스템 콜을 호출한 응용 프로그램은 결과를 받아서 다음 작업을 수행하게 됩니다.

 

- 시스템 콜의 유형에 대해 설명해 주세요

시스템 콜은 크게 세 가지 유형이 있습니다.
-- 프로세스 관리: 프로세스의 생성, 종료, 일시 정지, 재개, 자원 할당 등을 담당하는 시스템 콜입니다.
-- 파일 관리: 파일 생성, 삭제, 읽기, 쓰기 등 파일과 관련된 작업을 처리하는 시스템 콜입니다.
-- 디바이스 관리: 입출력 장치, 네트워크 카드 등의 하드웨어를 제어하기 위한 시스템 콜입니다.

 

- 운영체제의 Dual Mode에 대해 설명해 주세요

운영체제는 사용자 프로세스와 시스템 자원을 관리하기 위해 실행되는데, 이때 운영체제는 보안상의 이유로 두 가지 모드로 나뉩니다.
-- 사용자 모드(User Mode)
사용자 프로세스가 실행되는 모드
제한된 자원만 접근 가능
CPU의 모든 명령어 실행 가능
-- 커널 모드(Kernel Mode)
시스템 자원(하드웨어, 메모리, 파일 등)을 제어하기 위해 실행되는 모드
모든 자원 접근 가능
CPU의 모든 명령어 실행 가능
-- 운영체제의 Dual Mode는 사용자 프로세스와 시스템 자원을 보호하고, 시스템의 안정성을 유지하기 위해 중요합니다. 즉, 운영체제는 커널 모드에서만 시스템 자원을 직접 접근하고, 사용자 모드에서는 시스템 자원에 제한된 접근만 가능하게끔 제한함으로써 보안과 안정성을 유지합니다.

 

- 서로 다른 시스템 콜을 어떻게 구분할 수 있을까요?

시스템 콜을 호출하는 프로세스는 각 시스템 콜마다 고유한 번호를 가지고 있습니다. 이 번호를 사용하여 운영체제는 어떤 시스템 콜을 호출했는지를 구분하게 됩니다. 이러한 번호를 시스템 콜 번호 또는 호출 번호라고 합니다. 일반적으로 운영체제는 시스템 콜 번호를 받아 해당하는 시스템 콜 함수를 호출하게 됩니다.

 

- 함수호출과 시스템 콜의 차이에 대해서 설명하세요

-- 함수호출 : 자신이 작성한 함수 혹은 라이브러리에 저장된 함수를 호출하는 것

-- 시스템 콜 : 운영체제에 정의된 함수를 호출하는 것

함수 호출과 시스템 콜은 모두 프로그래밍에서 사용되는 용어이지만, 그 차이점은 함수 호출은 프로그램 내부에서 실행되는 반면, 시스템 콜은 운영체제 커널에서 실행된다는 것입니다. 즉, 함수 호출은 사용자 모드에서 수행되는 반면, 시스템 콜은 커널 모드에서 수행됩니다. 함수 호출은 프로그램이 실행되는 동안 해당 함수가 필요한 경우 호출됩니다. 시스템 콜은 운영체제의 서비스에 액세스 하기 위해 프로그램이 명시적으로 호출해야 합니다. 또한, 함수 호출은 일반적으로 빠르고 경량적이며, 시스템 콜은 보안을 유지하고 커널의 자원에 액세스하기 위해 더 많은 오버헤드가 발생합니다.

 

- 인터럽트와 시스템 콜의 차이에 대해서 설명하세요

인터럽트와 시스템 콜은 모두 운영체제에서 실행되는 기능입니다. 하지만 인터럽트는 하드웨어에서 발생하고, 시스템 콜은 소프트웨어에서 발생합니다. 인터럽트는 하드웨어에서 발생한 이벤트에 대한 처리를 운영체제가 담당합니다. 반면에 시스템 콜은 애플리케이션에서 운영체제 기능을 실행하기 위해 호출하는 함수입니다. 인터럽트는 예기치 않은 상황에서 운영체제를 실행하기 위해 발생하고, 시스템 콜은 응용 프로그램이 운영체제의 기능을 사용하기 위해 명시적으로 호출합니다.


6. 동기와 비동기, 블로킹과 논블로킹의 차이점 

동기(Synchronous)와 비동기(Asynchronous)는 어떻게 프로세스 또는 스레드 간 작업을 처리하는지를 나타냅니다. 동기적 작업은 호출된 함수가 작업을 완료할 때까지 기다리는 것을 의미합니다. 반면에, 비동기 작업은 호출된 함수가 작업을 완료하기를 기다리지 않고 다음 코드를 계속 실행합니다.
블로킹(Blocking)과 논블로킹(Non-Blocking)은 I/O 작업과 관련이 있습니다. 블로킹 I/O는 I/O 작업이 완료될 때까지 호출하는 프로세스 또는 스레드가 대기하는 것을 의미합니다. 반면에, 논블로킹 I/O는 I/O 작업이 완료되기 전에 프로세스 또는 스레드가 다른 작업을 수행할 수 있습니다.

따라서, 블로킹 동기 작업은 호출된 함수가 작업을 완료하기를 기다리는 동안 호출자가 블로킹됩니다. 블로킹 비동기 작업은 I/O 작업이 완료될 때까지 호출자가 블로킹됩니다. 논블로킹 동기 작업은 호출된 함수가 작업을 완료하기를 기다리는 동안 호출자가 다른 작업을 수행할 수 있지만, 다시 호출할 때까지 계속해서 폴링을 해야합니다. 마지막으로, 논블로킹 비동기 작업은 I/O 작업이 완료되기를 기다리지 않고 다른 작업을 수행할 수 있으며, I/O 작업이 완료되면 콜백 함수를 호출하여 결과를 처리합니다.

 

- 그렇다면, 동기이면서 논블로킹이고, 비동기이면서 블로킹인 경우는 의미가 있다고 할 수 있나요?

네, 이러한 경우도 있을 수 있습니다. 예를 들어, 비동기이면서 블로킹인 경우는 파일 입출력에서 데이터를 읽어오는 동안에도 다른 작업을 처리할 수 있도록 하기 위해 사용됩니다. 이 경우, 데이터를 읽어오는 요청은 비동기적으로 처리하지만, 데이터가 읽어질 때까지 다른 작업을 처리하지 않고 대기하기 때문에 블로킹됩니다. 동기이면서 논블로킹인 경우도 있을 수 있습니다. 이 경우, 시스템 콜을 호출한 후 결과를 기다리지 않고 다른 작업을 처리할 수 있기 때문에 논블로킹이지만, 시스템 콜이 완료될 때까지 대기하지 않고 즉시 결과를 처리해야 하므로 동기적인 특성도 가지고 있습니다.

 

- I/O 멀티플렉싱에 대해 설명해 주세요

I/O 멀티플렉싱은 하나의 입출력 인터페이스를 사용하여 여러 개의 입출력 작업을 동시에 처리하는 기술입니다. 일반적으로 컴퓨터 시스템은 여러 개의 입출력 디바이스를 가지고 있으며, 이들을 제어하기 위해 별도의 입출력 인터페이스가 필요합니다.
하지만 I/O 멀티플렉싱을 사용하면 하나의 입출력 인터페이스만으로 여러 개의 입출력 디바이스를 제어할 수 있습니다. 이를 위해서는 운영체제가 제공하는 I/O 멀티플렉싱 함수를 사용하여 여러 개의 입출력 작업을 동시에 처리할 수 있습니다.
I/O 멀티플렉싱은 대규모 서버나 네트워크 프로그래밍에서 특히 유용합니다. 예를 들어, 여러 개의 클라이언트가 서버에 접속하여 요청을 보낼 때, 이들 요청을 하나의 입출력 인터페이스로 처리할 수 있습니다. 이를 통해 프로그램의 성능을 향상할 수 있으며, 동시에 처리 가능한 클라이언트 수도 증가시킬 수 있습니다.

- synchronized에 대해 아는 바를 전부 이야기하세요

Java에서 synchronized는 멀티스레딩 환경에서 공유자원에 대한 접근을 제어하는 데 사용되는 키워드입니다. synchronized 키워드를 사용하면 여러 스레드가 동시에 하나의 공유자원에 접근하는 것을 방지하여 동기화 문제를 해결할 수 있습니다.
synchronized는 다음과 같은 두 가지 방법으로 사용됩니다.
-- 인스턴스 메서드에 대한 synchronized
synchronized 키워드를 메서드의 선언부에 추가하여 해당 메서드를 인스턴스 레벨에서 동기화합니다. 이 방법을 사용하면 해당 객체의 인스턴스 레벨에서만 스레드 간에 동기화가 유지됩니다.
-- 블록에 대한 synchronized
synchronized 키워드를 사용하여 블록을 동기화할 수 있습니다. 이 경우, synchronized 키워드 뒤에 괄호 안에 객체를 전달하여 해당 객체 레벨에서 동기화됩니다.
synchronized를 사용하면 한 스레드가 공유자원에 대한 작업을 수행할 때, 다른 스레드가 해당 작업을 실행하지 않도록 블로킹하여 동기화 문제를 해결합니다. 이러한 방식으로 동기화를 구현하면 공유자원에 대한 안정적인 동시 액세스를 보장할 수 있습니다. 하지만 과도한 사용은 성능 저하를 유발할 수 있으므로 신중하게 사용해야 합니다.

 

- Thread Pool, Monitor, Fork-Join이란 

-- Thread Pool:
Thread Pool은 미리 정해진 개수의 스레드들을 유지하면서, 많은 작업들을 처리하는 방식입니다. 작업이 들어오면, 스레드 풀에서 유휴 상태의 스레드를 가져와 작업을 수행하고, 작업이 완료되면 스레드를 풀에 반환합니다. 이를 통해 스레드 생성과 삭제를 반복하면서 발생하는 오버헤드를 줄이고, 성능 향상을 도모할 수 있습니다.
-- Monitor:
Monitor는 공유 자원의 동시 액세스를 제어하기 위한 동기화 기법입니다. 각각의 객체마다 모니터가 존재하며, 공유 자원에 대한 접근을 하나의 스레드만 가능하게 만듭니다. 다른 스레드들은 모니터에 들어가기 위해 대기하게 됩니다. 이를 통해 스레드 간의 경쟁 조건을 방지하고, 동기화를 보장할 수 있습니다.

-- Fork-Join:
Fork-Join은 분할 정복 알고리즘을 활용하여 병렬 처리를 구현하는 방식입니다. 작업을 여러 개의 작은 작업으로 분할하고, 각각의 작은 작업을 병렬로 처리한 다음, 결과를 합쳐 전체 작업을 완료합니다. 이를 통해 대용량 데이터 처리 등에서 성능 향상을 도모할 수 있습니다. Java에서는 ForkJoinPool 클래스를 제공하여 Fork-Join 패턴을 지원합니다.

 

- Thread Pool, Monitor, Fork-Join과 synchronize와 차이점

Thread Pool, Monitor, Fork-Join은 모두 멀티스레드 환경에서 동기화 문제를 해결하기 위한 방법입니다.
synchronized는 Java에서 제공하는 키워드로, 개발자가 직접 동기화를 구현할 수 있도록 합니다. 반면에 Thread Pool, Monitor, Fork-Join은 Java에서 제공하는 동기화 관련 클래스와 인터페이스를 활용하여 동기화를 구현합니다.
Thread Pool은 스레드 생성과 삭제를 최소화하여 성능을 향상하기 위한 기법이고, Monitor는 객체 단위에서 동기화를 제어하여 공유 자원에 대한 안정적인 동시 액세스를 보장하는 기법입니다. Fork-Join은 분할 정복 알고리즘을 활용하여 병렬 처리를 구현하는 기법입니다.
따라서, 각각의 방법은 서로 다른 동기화 기법을 사용하며, 상황에 맞게 적절한 방법을 선택하여 사용해야 합니다.

 

- 동기화를 구현하기 위한 HW 적인 Solution (세마포어, 뮤텍스, 모니터… X) 

동기화를 구현하는 데에는 하드웨어적인 솔루션이 가능합니다.
-- 원자 연산(Atomic operation):
원자 연산은 하드웨어적으로 원자성을 보장하는 명령어입니다. 원자 연산은 하나의 명령어로 실행이 완료되며, 중간에 다른 스레드가 접근하여 변경할 수 없는 연산을 말합니다. 예를 들어, compare-and-swap(CAS) 연산은 메모리의 값을 읽어와 특정 값과 비교하여 일치하면 새로운 값을 저장합니다. 이때 다른 스레드가 메모리를 변경하면, CAS 연산이 실패하여 재시도하게 됩니다.
-- 메모리 모델(Memory model):
메모리 모델은 스레드 간에 공유되는 메모리 공간의 동작 방식을 정의합니다. 메모리 모델은 하드웨어에 의해 구현되며, 스레드 간의 동기화를 위한 다양한 메모리 배리어(barrier)를 제공합니다. 메모리 배리어는 스레드 간의 메모리 가시성(visibility)을 제어하며, 스레드 간의 데이터 동기화를 보장합니다.

-- 캐시 일관성(Cache coherence):
캐시 일관성은 공유 메모리 시스템에서 발생하는 캐시 간의 데이터 불일치를 해결하기 위한 기술입니다. 캐시 일관성은 하드웨어적으로 구현되며, 캐시 간의 동기화를 위한 프로토콜을 사용합니다. 캐시 일관성은 스레드 간의 동기화를 보장하며, 병렬 처리 시스템의 성능을 향상시킵니다.
-- 이러한 하드웨어적인 솔루션들은 동기화 문제를 해결하기 위한 다양한 방법 중 하나로 사용될 수 있습니다. 하지만, 일반적으로 소프트웨어적인 솔루션들이 보다 효율적이고, 유지보수가 용이하기 때문에 많이 사용됩니다.

 

- CAS, TAS이란

CAS(Compare-and-Swap)와 TAS(Test-and-Set)은 원자적 연산(Atomic operation) 중의 하나로, 멀티스레드 환경에서 공유 데이터에 대한 안전한 동시 액세스를 보장하는 데 사용됩니다.
CAS는 메모리의 값을 읽어와 특정 값과 비교하여 일치하면 새로운 값을 저장하는 원자적인 연산입니다. CAS 연산은 새로운 값이 저장될 때까지 반복 수행됩니다. 따라서, CAS 연산을 통해 동시 액세스에 대한 안전한 처리를 보장할 수 있습니다.
TAS는 메모리 공간의 값을 테스트하고, 특정 값으로 설정하는 원자적인 연산입니다. TAS 연산은 하나의 스레드만 접근하여 값을 변경할 수 있으며, 다른 스레드가 접근하여 값을 변경할 수 없습니다. TAS 연산은 뮤텍스 등 동기화를 구현할 때 사용됩니다. 

두 연산 모두 원자적인 연산으로써, 공유 데이터에 대한 안전한 동시 액세스를 보장하기 위해 사용됩니다. 하지만, CAS 연산은 조건을 만족하는 경우에만 값을 변경할 수 있으며, TAS 연산은 값을 변경할 때까지 무한히 반복됩니다. 따라서, 상황에 맞게 적절한 연산을 선택하여 사용해야 합니다.

 

- volatile란

volatile은 멀티스레드 환경에서 공유 데이터의 가시성(Visibility)을 보장하는 데 사용되는 키워드입니다. volatile로 선언된 변수의 값은 메인 메모리에 직접 접근하여 읽고 쓰기 때문에, 다른 스레드에서 변경된 값을 즉시 감지할 수 있습니다.
보통 변수는 스레드마다 캐시를 갖고 있기 때문에, 한 스레드에서 변경한 값이 다른 스레드에서 보이지 않는 경우가 발생할 수 있습니다. 하지만, volatile 변수는 메인 메모리에 바로 접근하므로, 변경된 값이 다른 스레드에서 즉시 감지됩니다.
즉, volatile 변수를 사용하면 스레드 간의 가시성을 보장하여 데이터 불일치 문제를 예방할 수 있습니다. 하지만, volatile 변수는 단순히 가시성만 보장할 뿐, 스레드 간의 동기화를 보장하지 않습니다. 따라서, volatile 변수는 원자적인 연산이 필요하지 않은 경우에만 사용하는 것이 좋습니다.

- Memory Barrier란

Memory Barrier(메모리 배리어)는 멀티스레드 환경에서 공유 데이터의 가시성과 연산 순서를 보장하는데 사용되는 기술입니다. Memory Barrier를 사용하면 CPU의 캐시와 메인 메모리 간의 데이터 일관성을 유지할 수 있습니다.
CPU는 연산을 수행하면서 캐시를 이용하여 데이터를 처리합니다. 하지만, 다른 CPU에서 같은 데이터를 처리하면서 캐시에 저장한 값과 메인 메모리의 값이 일치하지 않을 수 있습니다. 이러한 문제를 해결하기 위해, Memory Barrier를 사용하여 CPU의 캐시와 메인 메모리 간의 데이터 일관성을 보장합니다.
Memory Barrier는 두 가지 종류로 나눌 수 있습니다. 첫째는 Read Barrier로, 캐시에 저장된 값을 읽기 전에 메인 메모리의 값을 갱신합니다. 둘째는 Write Barrier로, 캐시에 저장된 값을 메인 메모리에 갱신하기 전에 모든 연산이 완료되도록 합니다.

Memory Barrier를 사용하면 멀티스레드 환경에서 공유 데이터의 가시성과 연산 순서를 보장할 수 있습니다. 하지만, Memory Barrier를 남발하면 성능 저하가 발생할 수 있으므로, 상황에 맞게 적절한 사용을 고려해야 합니다.

 

- volatile 키워드는 어떤 의미가 있나요?

volatile 키워드는 변수가 항상 메인 메모리에서 읽고 쓰이도록 보장하는 역할을 합니다. 멀티스레드 환경에서 동시에 변수에 접근하는 경우, 스레드가 캐시 된 값을 사용하다가 메인 메모리의 값과 다를 수 있습니다. 이때 volatile 키워드를 사용하면 변수가 메인 메모리에 저장되고, 다른 스레드에서 읽어올 수 있도록 보장합니다.
즉, volatile 변수를 사용하면 스레드 간의 가시성을 보장할 수 있으며, 변수의 값이 항상 최신값을 가지도록 보장할 수 있습니다. 하지만, volatile 키워드는 변수의 값을 갱신하면 항상 메인 메모리에 저장하기 때문에, 성능 저하를 일으킬 수 있습니다. 따라서, 변수의 값을 자주 갱신하지 않아도 되는 경우에만 volatile 키워드를 사용하는 것이 좋습니다.

 

- 싱글코어가 아니라 멀티코어라면, 어떻게 동기화가 이뤄질까요?

멀티코어 환경에서는 동기화 문제가 더 복잡해집니다. 각 코어는 자신만의 캐시 메모리를 가지고 있기 때문에, 다른 코어에서의 변수 값 변경을 바로 알아채지 못할 수 있습니다.
이러한 문제를 해결하기 위해 멀티코어 환경에서는 캐시 일관성 프로토콜을 사용합니다. 캐시 일관성 프로토콜은 캐시 메모리와 메인 메모리 사이의 일관성을 유지하기 위한 규약입니다. 즉, 어떤 코어가 메인 메모리의 값을 변경하면, 다른 코어들의 캐시 메모리에 있는 해당 값도 갱신되도록 합니다.
멀티코어 환경에서의 동기화는 이러한 캐시 일관성 프로토콜을 기반으로 이뤄집니다. 캐시 일관성 프로토콜은 멀티코어 프로세서의 하드웨어에 내장되어 있기 때문에, 프로그래머는 이를 인식하고 코드를 작성해야 합니다. 멀티스레드 프로그래밍에서는 이러한 캐시 일관성 프로토콜을 이용하여 스레드 간의 변수 값을 동기화하고, 동기화된 코드 영역에서 캐시 일관성을 유지하는 것이 중요합니다.



댓글