*출처 콥노트
*출처 Uno's Blog
I/O
컴퓨터의 기본 동작중에 I/O는 가장 느리다. RAM에 접근하는데에는 나노초가 걸리지만, 디스크와 네트워크에 접근하는 데에는 밀리초가 걸린다.
실제 CPU의 측면에서는 I/O가 많은 비용을 요구하지 않지만, 보내지는 요청과 작업이 완료되는 순간 사이의 지연이 발생하게 되는 것이다.
Blocking I/O
I/O 요청에 해당하는 함수 호출은 작업이 완료될 때까지 스레드를 차단했다. 당연히 블록킹 I/O가 구현된 웹 서버는 여러 연결을 처리할 수 없다는 것이다.
Non-Blocking I/O
Non-Blocking을 구현하는 여러 가지 패턴이 있는데, 가장 기본적인 패턴은 busy-waiting이다. busy-waiting은 실제 데이터가 반환될 때까지 루프 내에서 리소스를 폴링(완료되었니?? 라고 집착하는것)하는 것이다.
// NOTE: pseudocode
function busyWaiting() {
while(something === true){
// waiting ...
}
}
사용할 수 없는 리소스를 반복하는데만 cpu 사이클이 계속 사용되고 있는 것이다(완료되었냐고 집착집착). 이러한 문제에 대한 대안으로 필요한 것이 Event-Demultiplexing이다. 이벤트 디멀티플랙서(Event-Demultiplexer)는 I/O 이벤트를 수집하여 이벤트 큐(Queue)에 넣고 처리할 수 있는 새 이벤트가 있을 때까지 차단(Block)한다. 이 패턴을 사용하면 busy-waiting을 사용하지 않고 단일 스레드 내에서 여러 I/O 작업을 처리할 수 있다.
각각 동기/비동기 I/O
동기 블로킹 I/O (Synchronous blocking I/O)
- 가장 흔한 모델
- 사용자 공간(user-space)에 있던 어플리케이션이 블록을 일으키는 시스템 함수 호출(system call)
- 이로써 한 작업당 한 번의 사용자공간-커널공간의 문맥 교환(context switching) 발생
- 정지된 어플리케이션은 CPU를 사용하지 않고 커널(kernel) 응답을 기다림
- 응답이 되돌아오면 데이터도 사용자 공간 버퍼로 돌아오고, 어플리케이션은 블록이 풀림(unblocked)
동기 넌블로킹 I/O (Synchronous Non-blocking I/O)
- 동기 블로킹의 개선안
- But 동기 블로킹에 비해 잦은 시스템 호출과 컨텍스트 스위칭(context switch)으로 비효율적인 방식
- I/O를 즉시 완료하는 대신, IO가 완료될 때까지 어플리케이션은 계속 물어보고(system call), 커널은 아직 완료되지 않았다는 에러 코드를(EAGAIN or EWOULDBOLCK) 반환
비동기 블로킹 I/O (Asynchronous blocking I/O)
- I/O는 넌블로킹으로, 알림(notification)을 블로킹으로 하는 방식
- 따라서 넌블로킹 I/O(non-blocking I/O) 는 설정되어 있다.
- select() 라는 시스템 함수 호출이 어플리케이션을 정지(block)시킨다.
- select() 는 IO 설명자(descriptor)에서 반응(activity)이 있나 보는데, 한 개가 아닌 여러 개의 IO 설명자에 대한 알림 기능을 수행할 수 있는 것이 특징
- select()의 비효율, 고성능 IO로는 비추천한다고 한다.
비동기 넌블로킹 I/O (Asynchronous Non-blocking I/O)
- 시스템 콜 요청(request)이 즉시 IO 개시 여부를 반환
- 어플리케이션은 이제 하고 싶은 다른 일을 하며, I/O 는 백그라운드에서 실행
- I/O 응답(response)가 도착하면 신호(signal)나, 쓰레드 기반 콜백(callback)으로 I/O 전달(transaction)을 완료
- 단일 프로세스가 여러 개의 I/O 요청이 예상되는 환경 속에서 컴퓨터 연산이나 I/O 작업을 병렬 수행하는 기능은 처리 속도와 I/O 속도 사이에 틈을 유발한다.
- I/O 작업 한 개 이상이 처리 중 상태에 빠져있는 동안(pending), cpu는 다른 업무를 볼 수 있다.