본문 바로가기
■ Programming /Window Network

[윈도우 네트워크] Blocking 과 Non-Blocking

by Popbox 2018. 1. 8.
반응형

[Blocking / Non-Blocking]

 

 

 

  Blocking


Blocking IO Model



I/O 작업은 유저 레벨(User Level - Application)에서 직접 수행할 수 없다.

실제 I/O를 수행하는 것은 커널레벨(Kernel Level - OS) 이다.

따라서 유저 프로세스(또는 쓰레드)는 커널에게 I/O를 요청해야 한다.

I/O 작업을 처리하기 위해 유저 레벨(User Level)에 있던 Application이 시스템 함수를 호출한다. (system call)

이 때, 컨텍스트 스위칭(Context Switching)이 발생한다.

그리고 커널 레벨(Kernel Level)에서 해당 I/O 작업이 끝나고 데이터를 반환하게 되면,

그 때가 되어서야 어플리케이션 단의 스레드(thread)에 걸렸던 Block이 풀린다.

어플리케이션(Application - User Level) 관점에서 보면 아무런 동작도 안하는 것처럼 (렉이 걸린 듯한)

보이지만, 실제로는 커널에서 I/O작업을 수행하느라 Block이 되어 있는 것이다.

이 부분이 Blocking IO의 문제점이며 개선 포인트이다.





Blocking



I/O에서 블로킹 형태의 작업은 유저 프로세스가 커널에게 I/O를 요청하는 함수를 호출하고,

커널이 작업을 완료하면 함수가 작업 결과를 반환한다.


I/O 작업이 진행되는 동안 유저 프로세스는 자신의 작업을 중단한채 대기해야한다.

I/O 작업이 CPU자원을 거의 쓰지 않기 대문에 이런 형태의 I/O는 리소스 낭비가 심하다. 

(해야할 일을 못하고 기다리기 때문)







만약 여러대의 클라이언트가 접속하는 서버(server)를 블로킹(Blocking) 방식으로 구현한다면???????????????

-> A클라이언트에 대해 I/O작업을 진행하면 메인 쓰레드가 진행하던 작업이 중단된다.

-> 작업에 대한 영향을 미치지 않게 하기 위해서는 클라이언트당 하나의 쓰레드를 제공해야한다.

-> 이러한 방식의 문제는 접속자 수가 많아질수록 엄청나게 많은 쓰레드가 생성이 된다는 뜻이고, 

쓰레드가 많으면 CPU의 컨텍스트 스위칭(Context Switching) 횟수가 증가할 것이며, 이때 사용되는 컨텍스트 스위칭 비용 때문에, 실제 작업하는 양에 비하여 훨씬 비효율적으로 동작하게 될 것이다.



 

[요약]

Blocking은 I/O수행을 위한 함수가 호출이 되고, 그 결과가 나올때까지 기다린다.


 












  Non - Blocking 



Non-Blocking I/O Model



Blocking 방식의 비효율성을 극복하고자 만들어진 것이 Non-Blocking 방식이다.

Non-Blocking은 I/O 작업을 진행하는 동안 유저 프로세스의 작업을 중단시키지 않는다.

유저 프로세스가 커널에게 I/O 함수를 호출하면(System Call),

커널에서 함수의 진행 상황과 상관없이 바로 결과를 반환한다.

이 떄 반환되는 결과는 반환하는 순간에 가져올 수 있는 데이터에 해당한다.

호출 직전에는 가져올 수 있는 데이터가 없겠지만, 시간이 지나면 가져올 수 있는 데이터가 생겨날 것이다.


이렇게 되면 서버는 클라이언트가 요청한 사이즈에 맞는 데이터를 반환하기 위해 데이터를 축적해야 한다.

데이터의 축적이 끝났을 때, 반환되어 클라이언트에서 송신한 사이즈의 데이터를 받아올 수 있게 된다.

하지만구현 방식의 문제는 클라이언트가 송신한 데이터가 전부 도착했는지 계속 확인해줘야 한다는 것이다.

데이터가 준비되었는지(패킷 완성) 확인하는 과정에서 수많은 클라이언트들의 요청이 동시 다발적으로 일어날 경우,

CPU에게 적지 않은 부담이 될 수 있는 I/O 모델이다.








Non-Blocking



 위 그림은 Non-Blocking방식으로 구현된 I/O의 대표적인 사례를 잘 보여준다.


유저 프로세스는 recv() 함수를 호출하여 커널에게 해당 소켓으로부터 데이터를 받겠다고 요청하고 있다.

커널은 이 요청에 대해서 상대방의 데이터를 전송 받아서 recvBuffer에 저장하고, 유저에게 그 내용을 복사해 준다.

상대방으로 부터 데이터를 받는 중에 recvBuffer가 비어 있다면, 유저 프로세스가 커널에게 받아올 수 있는 정보는 없다.

따라서 recv() 함수는 아직 작업 진행중이란 의미로 [ EWOULDBLOCK ] 를 리턴한다.


이 결과를 받은 유저 프로세스는 다른 작업을 진행할 수 있다. 

만약 recvBuffer에 유저가 받을 수 있는 데이터가 있다면, 버퍼로 부터 데이터를 복사하여 받아온다.

recvBuffer는 커널이 가지고 있는 메모리에 적재되어 있으므로 메모리간 복사가 일어나 I/O보다 훨씬 빠른 속도로 데이터를 받아올 수 있다.

이 때, recv() 함수는 빠른 속도로 읽을 수 있는 데이터를 복사해주고, 복사한 데이터의 길이와 함께 반환한다.

위의 모든 반환이 I/O의 진행 시간과는 관계없이 빠르게 동작하기 때문에, 

유저 프로세스는 자신의 작업을 오랜시간 중지하지 않고도 I/O처리를 수행할 수 있다.

 


[요약]

Non-Blocking은 Blocking과 반대로 결과가 나올 때까지 기다리지 않고 바로 결과를 반환한다.


 




 감사합니다. 공감 한번 부탁드려요.


  




반응형

댓글