오늘의 나보다 성장한 내일의 나를 위해…
이전 포스트에서 동기, 비동기, 블로킹, 논블로킹에 대해서 충분히 다뤘는데도 불구하고 또 다루는 이유는 이렇다. 전의 포스트에서는 일반적인 개념에 대해서 다뤘다면 이번에는 I/O 관점에서 다루고 싶었다.
많은 면접을 거친 결과 이런 질문들이 있다.
Q: 동기와 비동기에 대해서 설명해 주세요
혹인
Q: 동기 비동기 I/O에 대해서 설명해 주세요
혹은
Q: 블로킹과 논블로킹에 대해서 설명해 주세요
혹은
Q: 블로킹 I/O와 논블로킹 I/O에 대해서 설명해 주세요
물론 I/O 하나 붙었다고 해서 큰 개념에 차이가 있는 것은 아니다. 일반적인 의미는 이전 포스트에서 다룬 내용과 같다. 다만 이번에는 운영체제 관점에서 들여다 본다는 차이가 있다.
하나씩 살펴보도록 하자.
동기식 입출력 (Synchronous I/O)
- 프로그램 I/O 요청을 했을 때 해당 I/O 작업이 완료되어야 다음 작업을 할 수 있는 방식이다.
- I/O가 진행되는 동안 다음 명령을 수행하지 않고 기다린다.
- I/O 상태의 프로세스는 blocked state로 전환된다.
- I/O가 완료되면 인터럽트를 통해 완료를 알린다. 이후 CPU의 제어권이 기존 프로그램에게 넘어간다.
- blocked state의 프로세스는 wait 상태로 돌아간다.
- 명령 수행 속도는 빠르지만 입출력 연산은 상대적으로 느리다. 기다리는 과정에서 자원 낭비를 초래한다.
- 보통 I/O가 진행되면 CPU는 다른 프로그램의 작업을 수행하게 된다.
- 입출력 요청의 동기화
- 여러 프로세스가 동시에 I/O 요청을 할 경우 각 요청을 큐에 넣어 순서대로 처리한다.
비동기식 입출력(Non-Synchronous I/O)
- CPU의 제어권을 입출력 연산을 호출한 프로그램에게 곧바로 다시 부여한다.
- I/O 결과와 관련 없는 연산이 있을 경우 주로 사용된다.
- CPU는 I/O 결과와 상관 없이 처리 가능한 작업부터 처리한다.
- I/O 연산이 완료되면 인터럽트를 통해 알린다.
Blocking I/O
- 직접 제어할 수 없는 대상의 작업(I/O)이 완료될 때까지 기다린다.
- I/O가 완료되어야 제어권이 프로세스로 넘어간다.
- 동기와 마찬가지로 자원이 낭비된다.
Non-Blocking I/O
입출력 처리는 시작만 해둔 채 완료되지 않은 상태에서 다른 처리 작업을 계속 진행할 수 있도록 멈추지 않고 입출력 처리를 기다리는 방법을 말한다.
I/O 처리를 하는 전통적인 방법은 I/O 작업을 시작하면 완료될 때까지 기다리는 방법이다. 기존에는 synchronous I/O 혹은 blocking I/O를 통해 I/O 작업을 진행하는 동안 프로그램이 아무 일도 하지 않고 시간을 소비하게 만든다.
반면, Non-blocking I/O 방식을 사용하면 외부에 I/O 작업을 하도록 요청한 후, 즉시 다음 작업을 처리함으로써 시스템 자원을 더 효율적으로 사용할 수 있게된다.
그러나 I/O 작업이 완료된 이후에 처리해야 하는 후속 작업이 있다면, I/O 작업이 완료될 때까지 기다려야 한다. 따라서 이 후속 작업이 프로세스를 멈추지 않도록 만들기 위해 I/O 작업이 완료된 이후 후속 작업이 이어서 진행할 수 있도록 별도의 약속(Polling, Callback function 등)을 한다.
- I/O 작업이 진행되는 동안에는 유저 프로세스의 작업을 중단시키지 않는다.
- 제어권을 바로 반납한다.
- I/O 완료와 상관없이 작업 결과가 반환된다.
- 이를 입력 데이터가 있을 때까지 반복하고, 입력 데이터가 있으면 결과가 전달된다.
- 대기하지 않아도 되지만 I/O 완료를 확인해야 하기 때문에 시스템 호출이 반복된다.
- 동기, 비동기는 개선된 I/O 이벤트 통지 모델이다.
정리
- 동기/비동기는 인터럽트 발생으로 인한 제어권 반한 시점에 중점을 두고 Blocking/Non-Blocking은 제어권 자체에 중점을 둔다는 점에서 차이가 있다.