네트워크에서는 소켓으로 연결되어 있는 다른 프로세스에게 데이터를 보내는 출력(send(), write()) 작업과 다른 프로세스로부터 데이터를 받는 입력(recv(), read()) 작업이 수행된다.
I/O 작업은 유저 프로세스(쓰레드)가 커널에 요청을 하면, 커널 레벨에서 작업을 수행한 뒤 다시 유저 프로세스에게 결과를 반환하는 방식으로 진행된다.
봉쇄(Blocking) 모델과 비봉쇄(Non-Blocking) 모델
프로그램 유휴 상태를 묘사, 제어권을 묘사
봉쇄(Blocking) 방법은 유저 프로세스가 커널에게 입출력 작업 요청을 위한 함수를 호출하면 커널은 작업이 완료될 때까지 함수를 반환하지 않는다. 따라서 유저 프로세스는 커널에서 함수가 반환될 때까지 프로그램을 동작시키지 않고 대기한다. 프로세스 제어권은 커널에게 있다.
비봉쇄(Non-Blocking) 방법은 유저 프로세스가 함수를 호출하면 커널은 작업 완료의 여부와 상관 없이 함수를 바로 반환한다. 따라서 유저 프로세스는 대기하지 않고 프로그램을 동작시킬 수 있다. 프로세스 제어권은 유저 프로세스에게 있다.
동기(Synchronous)와 비동기(Asynchronous)
현재 작업에 대한 응답과 다음 작업에 대한 요청 타이밍을 묘사, 데이터 상태에 대한 인지를 묘사
현재 작업에 대한 응답을 받음과 동시에 다음 작업에 대한 요청을 할 수 있어 작업의 종료가 순차적인 방법을 동기(Synchronous), 현재 작업에 대한 응답을 받지 않아도 다음 작업을 요청할 수 있어 작업의 종료가 순차적이지 않은 방법을 비동기(Asynchronous)라고 한다.
동기 방법에서는 유저 프로세스와 커널이 모두 데이터 상태를 인지하고 있어야 유저 프로세스가 커널에게 다음 작업을 요청할 수 있다. 따라서 유저 프로세스는 커널 프로세스처럼 데이터 상태를 인지하고 있다. 유저 프로세스는 데이터 상태를 인지하기 위해 커널에게 입출력 작업을 요청한 뒤 지속적으로 커널에게 작업 완료 여부를 물어 본다.
하지만 비동기 방법에서는 유저 프로세스가 현재 작업 완료와 상관 없이 다음 작업을 요청할 수 있으므로 유저 프로세스는 데이터 상태를 인지하고 있지 않아도 된다. 따라서 커널 프로세스가 입출력 작업을 완료했다면 이벤트 핸들러(Callback 함수 호출)를 동작시켜 작업 완료 여부를 유저 프로세스에게 통지함으로써 유저 프로세스는 작업의 종료 여부를 알 수 있다.
봉쇄/비봉쇄, 동기/비동기 방식은 동일한 개념이 아니기때문에 혼합되어 사용될 수 있음에 주의하자!
Synchronous Blocking I/O
가장 기본적인 I/O 모델
유저 프로세스가 read()함수를 호출하면 커널은 버퍼에 데이터가 복사되기 전까지 read() 함수를 반환하지 않는다. 함수가 반환되지 않는 동안 프로세스는 봉쇄되어 대기하고 있으며 다른 프로세스를 진행할 수 없다. 데이터가 입력되면 커널은 유저 프로세스에게 read()함수를 반환하여 데이터가 복사된다.
Synchronous Non-Blocking I/O
유저 프로세스가 read() 함수를 호출하면 커널은 데이터 복사 여부와 무관하게 바로 read()함수를 반환한다. 만약 아직 데이터가 복사되지 않은 상태라면 에러 코드를 반환한다. 이 과정은 버퍼에 데이터가 복사될 때까지 반복된다. 유저 프로세스가 함수를 호출해도 바로 반환되므로 다른 프로세스를 동시에 작업할 수 있다. 또한 유저 프로세스는 지속적으로 시스템 콜을 호출하며 데이터 상태를 인지하고 있다. 비로소 데이터 입출력 작업이 완료되었다면 커널은 에러 코드를 반환하는 대신 데이터를 복사한 결과를 반환한다.
Synchronous Blocking I/O 모델과 달리 여러 프로세스를 동시에 작업할 수 있다는 장점이 있지만, 데이터가 준비되기 전에는 유저 프로세스와 커널이 바쁘게 순환해야한다(busy wait)는 단점이 있다.
Asynchronous Blocking I/O
Synchronous Non-Blocking I/O 모델의 busy wait 문제를 해결할 수 있는 모델
IBM 문서에 의한 설명)
입출력 함수(read())를 호출하기 전, 커널에 입출력 데이터가 들어왔는지 여부를 확인하는 함수인 select()를 호출한다. 만약 커널에 들어온 입출력 데이터가 없다면 유저 프로세스는 봉쇄된다. 입출력 데이터가 있다면 유저 프로세스는 입출력 함수를 호출할 수 있다.
하지만 위의 설명을 뜯어보면 유저 프로세스가 select()를 호출하여, 입출력 데이터가 있는지를 유저 프로세스가 확인하고 있다. 따라서 동기 방식으로 볼 수 있다는 논란의 여지가 있다.
비동기 Select)
WSAEventSelect 소켓 통지 모델을 사용한다면 유저 프로세스가 지속적으로 커널의 데이터 상태를 확인할 필요 없이, 기존 상태와 비교하여 데이터 상태가 달라지면 이벤트 핸들러를 통해 콜백 함수가 호출된다. 따라서 비동기 방식으로 select()기능을 사용할 수 있다.
Asynchronous Non-Blocking I/O
유저 프로세스가 입출력 함수를 호출하면 커널은 데이터 상태와 무관하게 바로 함수를 반환한다. 유저 프로세스는 커널의 데이터 상태에 제약 없이 다른 프로세스를 작업할 수 있다. 또한 유저 프로세스는 데이터 입출력 여부를 물어보기 위해 지속적으로 커널에 함수를 호출할 필요가 없다. 데이터가 준비되면 커널이 이벤트 핸들러를 통해 콜백 함수를 호출하여 결과를 통지한다.
정리
Blocking : 유저 프로세스가 입출력 작업을 위한 함수를 호출하면, 커널은 작업이 완료될 때까지 함수를 반환하지 않아 현재 프로세스가 봉쇄된다.
Non-Blocking : 유저 프로세스가 입출력 작업을 위한 함수를 호출하면, 커널은 작업 종료 여부와 무관하게 함수를 바로 반환한다. 따라서 현재 프로세스가 봉쇄되지 않아 여러 작업을 수행할 수 있다.
Blocking, Non-Blocking, Synchronous, Asynchronous I/O
I/O 작업
I/O란 Input/Output의 약자로 입출력 작업을 말한다.
네트워크에서는 소켓으로 연결되어 있는 다른 프로세스에게 데이터를 보내는 출력(
send()
,write()
) 작업과 다른 프로세스로부터 데이터를 받는 입력(recv()
,read()
) 작업이 수행된다.I/O 작업은 유저 프로세스(쓰레드)가 커널에 요청을 하면, 커널 레벨에서 작업을 수행한 뒤 다시 유저 프로세스에게 결과를 반환하는 방식으로 진행된다.
봉쇄(Blocking) 모델과 비봉쇄(Non-Blocking) 모델
프로그램 유휴 상태를 묘사, 제어권을 묘사
봉쇄(Blocking) 방법은 유저 프로세스가 커널에게 입출력 작업 요청을 위한 함수를 호출하면 커널은 작업이 완료될 때까지 함수를 반환하지 않는다. 따라서 유저 프로세스는 커널에서 함수가 반환될 때까지 프로그램을 동작시키지 않고 대기한다. 프로세스 제어권은 커널에게 있다.
비봉쇄(Non-Blocking) 방법은 유저 프로세스가 함수를 호출하면 커널은 작업 완료의 여부와 상관 없이 함수를 바로 반환한다. 따라서 유저 프로세스는 대기하지 않고 프로그램을 동작시킬 수 있다. 프로세스 제어권은 유저 프로세스에게 있다.
동기(Synchronous)와 비동기(Asynchronous)
현재 작업에 대한 응답과 다음 작업에 대한 요청 타이밍을 묘사, 데이터 상태에 대한 인지를 묘사
현재 작업에 대한 응답을 받음과 동시에 다음 작업에 대한 요청을 할 수 있어 작업의 종료가 순차적인 방법을 동기(Synchronous), 현재 작업에 대한 응답을 받지 않아도 다음 작업을 요청할 수 있어 작업의 종료가 순차적이지 않은 방법을 비동기(Asynchronous)라고 한다.
동기 방법에서는 유저 프로세스와 커널이 모두 데이터 상태를 인지하고 있어야 유저 프로세스가 커널에게 다음 작업을 요청할 수 있다. 따라서 유저 프로세스는 커널 프로세스처럼 데이터 상태를 인지하고 있다. 유저 프로세스는 데이터 상태를 인지하기 위해 커널에게 입출력 작업을 요청한 뒤 지속적으로 커널에게 작업 완료 여부를 물어 본다.
하지만 비동기 방법에서는 유저 프로세스가 현재 작업 완료와 상관 없이 다음 작업을 요청할 수 있으므로 유저 프로세스는 데이터 상태를 인지하고 있지 않아도 된다. 따라서 커널 프로세스가 입출력 작업을 완료했다면 이벤트 핸들러(Callback 함수 호출)를 동작시켜 작업 완료 여부를 유저 프로세스에게 통지함으로써 유저 프로세스는 작업의 종료 여부를 알 수 있다.
봉쇄/비봉쇄, 동기/비동기 방식은 동일한 개념이 아니기때문에 혼합되어 사용될 수 있음에 주의하자!
Synchronous Blocking I/O
가장 기본적인 I/O 모델
유저 프로세스가
read()
함수를 호출하면 커널은 버퍼에 데이터가 복사되기 전까지 read() 함수를 반환하지 않는다. 함수가 반환되지 않는 동안 프로세스는 봉쇄되어 대기하고 있으며 다른 프로세스를 진행할 수 없다. 데이터가 입력되면 커널은 유저 프로세스에게read()
함수를 반환하여 데이터가 복사된다.Synchronous Non-Blocking I/O
유저 프로세스가
read()
함수를 호출하면 커널은 데이터 복사 여부와 무관하게 바로read()
함수를 반환한다. 만약 아직 데이터가 복사되지 않은 상태라면 에러 코드를 반환한다. 이 과정은 버퍼에 데이터가 복사될 때까지 반복된다. 유저 프로세스가 함수를 호출해도 바로 반환되므로 다른 프로세스를 동시에 작업할 수 있다. 또한 유저 프로세스는 지속적으로 시스템 콜을 호출하며 데이터 상태를 인지하고 있다. 비로소 데이터 입출력 작업이 완료되었다면 커널은 에러 코드를 반환하는 대신 데이터를 복사한 결과를 반환한다.Synchronous Blocking I/O 모델
과 달리 여러 프로세스를 동시에 작업할 수 있다는 장점이 있지만, 데이터가 준비되기 전에는 유저 프로세스와 커널이 바쁘게 순환해야한다(busy wait)는 단점이 있다.Asynchronous Blocking I/O
Synchronous Non-Blocking I/O 모델
의 busy wait 문제를 해결할 수 있는 모델IBM 문서에 의한 설명)
입출력 함수(
read()
)를 호출하기 전, 커널에 입출력 데이터가 들어왔는지 여부를 확인하는 함수인select()
를 호출한다. 만약 커널에 들어온 입출력 데이터가 없다면 유저 프로세스는 봉쇄된다. 입출력 데이터가 있다면 유저 프로세스는 입출력 함수를 호출할 수 있다.하지만 위의 설명을 뜯어보면 유저 프로세스가
select()
를 호출하여, 입출력 데이터가 있는지를 유저 프로세스가 확인하고 있다. 따라서 동기 방식으로 볼 수 있다는 논란의 여지가 있다.비동기 Select)
WSAEventSelect
소켓 통지 모델을 사용한다면 유저 프로세스가 지속적으로 커널의 데이터 상태를 확인할 필요 없이, 기존 상태와 비교하여 데이터 상태가 달라지면 이벤트 핸들러를 통해 콜백 함수가 호출된다. 따라서 비동기 방식으로select()
기능을 사용할 수 있다.Asynchronous Non-Blocking I/O
유저 프로세스가 입출력 함수를 호출하면 커널은 데이터 상태와 무관하게 바로 함수를 반환한다. 유저 프로세스는 커널의 데이터 상태에 제약 없이 다른 프로세스를 작업할 수 있다. 또한 유저 프로세스는 데이터 입출력 여부를 물어보기 위해 지속적으로 커널에 함수를 호출할 필요가 없다. 데이터가 준비되면 커널이 이벤트 핸들러를 통해 콜백 함수를 호출하여 결과를 통지한다.
정리