Appearance
Blocking I/O & Non-Blocking I/O
일반적인 운영체제 환경에서 애플리케이션은 시스템 콜을 통해 커널에 I/O를 요청한다.
Blocking I/O
read,recv,accept같은 호출이 완료될 때까지호출한 스레드가 반환되지 않는 방식이다.진행 순서
- Process(Thread)가 커널에 I/O 시스템 콜을 요청한다.
- 데이터가 준비되거나 연결이 들어오는 등, 요청한 조건이 만족될 때까지 해당 스레드는 잠든다.
- 커널이 작업을 완료하면 스레드를 깨우고 결과를 반환한다.
특징
- 요청한 스레드는 I/O가 끝날 때까지 그 호출에서 빠져나오지 못한다.
- CPU를 계속 태우는 것은 아니지만, 연결마다 스레드를 오래 붙잡는 구조는 고동시성에서 메모리 사용량과 컨텍스트 스위칭 비용이 커질 수 있다.
여러 Client가 접속하는 서버를 단순 Blocking 방식으로 만들면, 연결마다 스레드 또는 프로세스를 할당하는 구조가 되기 쉽다. 이 방식은 구현은 단순하지만 연결 수가 많아질수록 확장성이 떨어진다.Non-Blocking I/O
시스템 콜이 즉시 반환되는 방식이다. 아직 처리할 수 없는 상태라면
EAGAIN또는EWOULDBLOCK같은 결과를 돌려준다.진행 순서
- User Process가
recvfrom같은 함수를 호출한다. - 아직 읽을 데이터가 없으면 커널은 즉시
EAGAIN/EWOULDBLOCK을 반환한다. - 애플리케이션은 다른 작업을 계속 수행할 수 있다.
- 소켓이 읽기 가능한 시점이 오면 다시
recvfrom을 호출해 커널 버퍼의 데이터를 사용자 버퍼로 복사한다. - 복사가 끝나면 읽은 바이트 수를 반환한다.
- User Process가
특징
- 호출 시 바로 반환되므로 한 스레드가 여러 연결을 다루기 쉽다.
- 대신 준비되지 않은 소켓을 계속 확인하면 busy waiting이 되기 쉬우므로, 보통
select,poll,epoll,kqueue같은 readiness notification과 함께 사용한다.
정리
Blocking I/O는호출한 스레드가 잠드는 방식Non-Blocking I/O는시스템 콜이 즉시 반환되는 방식
참고로 Non-Blocking I/O와 Asynchronous I/O는 같은 말이 아니다.
Non-Blocking I/O는 보통 애플리케이션이 준비 상태를 확인한 뒤 다시 읽거나 쓴다.Asynchronous I/O는 커널이나 런타임이 완료 시점을 나중에 통지해주는 모델에 가깝다.