Skip to content

Blocking I/O & Non-Blocking I/O


일반적인 운영체제 환경에서 애플리케이션은 시스템 콜을 통해 커널에 I/O를 요청한다.


  1. Blocking I/O

    read, recv, accept 같은 호출이 완료될 때까지 호출한 스레드가 반환되지 않는 방식이다.

    • 진행 순서

      1. Process(Thread)가 커널에 I/O 시스템 콜을 요청한다.
      2. 데이터가 준비되거나 연결이 들어오는 등, 요청한 조건이 만족될 때까지 해당 스레드는 잠든다.
      3. 커널이 작업을 완료하면 스레드를 깨우고 결과를 반환한다.
    • 특징

      • 요청한 스레드는 I/O가 끝날 때까지 그 호출에서 빠져나오지 못한다.
      • CPU를 계속 태우는 것은 아니지만, 연결마다 스레드를 오래 붙잡는 구조는 고동시성에서 메모리 사용량과 컨텍스트 스위칭 비용이 커질 수 있다.

    여러 Client가 접속하는 서버를 단순 Blocking 방식으로 만들면, 연결마다 스레드 또는 프로세스를 할당하는 구조가 되기 쉽다. 이 방식은 구현은 단순하지만 연결 수가 많아질수록 확장성이 떨어진다.


  2. Non-Blocking I/O

    시스템 콜이 즉시 반환되는 방식이다. 아직 처리할 수 없는 상태라면 EAGAIN 또는 EWOULDBLOCK 같은 결과를 돌려준다.

    • 진행 순서

      1. User Process가 recvfrom 같은 함수를 호출한다.
      2. 아직 읽을 데이터가 없으면 커널은 즉시 EAGAIN/EWOULDBLOCK을 반환한다.
      3. 애플리케이션은 다른 작업을 계속 수행할 수 있다.
      4. 소켓이 읽기 가능한 시점이 오면 다시 recvfrom을 호출해 커널 버퍼의 데이터를 사용자 버퍼로 복사한다.
      5. 복사가 끝나면 읽은 바이트 수를 반환한다.
    • 특징

      • 호출 시 바로 반환되므로 한 스레드가 여러 연결을 다루기 쉽다.
      • 대신 준비되지 않은 소켓을 계속 확인하면 busy waiting이 되기 쉬우므로, 보통 select, poll, epoll, kqueue 같은 readiness notification과 함께 사용한다.

정리

  • Blocking I/O호출한 스레드가 잠드는 방식
  • Non-Blocking I/O시스템 콜이 즉시 반환되는 방식

참고로 Non-Blocking I/OAsynchronous I/O는 같은 말이 아니다.

  • Non-Blocking I/O는 보통 애플리케이션이 준비 상태를 확인한 뒤 다시 읽거나 쓴다.
  • Asynchronous I/O는 커널이나 런타임이 완료 시점을 나중에 통지해주는 모델에 가깝다.