• Overlapped I/O

    2014. 9. 18.

    by. xxx123

     

     

    1. Non-blocking I/O와 Overlapped I/O의 차이?

     

    일단 기존의 non-blocking I/O에 대해 알아보자.

    기존의 소켓을 non-blocking 설정하고 recv()함수를 호출하게 되면, 해당 작업이 성공이든 실패이든 바로 함수를 리턴한다.

    즉, 함수가 작업을 완료할 때까지 기다리지 않는다는 것이다.

    성공을 하는 경우라면 소켓버퍼에 데이터가 차 있어서 바로 해당 데이터를 얻어 오는 경우이고,

    실패하는 경우는 소켓버퍼에 데이터가 하나도 없을 때이다.

    위에서 처럼 들어온 데이터가 하나도 없는데 recv()를 하려하면 WOULDBLOCK ERROR를 뱉어내는데, 이때 이벤트라던지 메세지를 통해서 데이터가 들어왔다는 것이 통보되면 그때 다시 한번 recv()를 호출해서 데이터를 소켓버퍼로 부터 얻어와야 한다.

    바로 위에 밑줄친 부분에서 처럼 소켓버퍼에서 유저버퍼로 직접 얻어와야 한다는 것이 가장 큰 차이인 것이다.

    저렇게 직접 recv() 함수를 호출할 경우 I/O를 처리하는 과정은 동기로 진행이 되게 될것이고 적어도 데이터를 유저버퍼로 복사하는 동안은 시간을 잡아 먹을 수 밖에 없다.

     

    하지만 Overlapped I/O의 경우는 I/O에 대한 처리를 Device Driver에게 맡김으로써 별도의 스레드 없이 이러한 문제를 해결한다. Device Driver는 작업이 끝나면 알아서 유저버퍼에 데이터를 채워 넣어 놨을것인데, 이러한 작업의 전가를 통해 I/O에 대한 처리를 중첩해서 멈춤없이 처리할 수 있게 되는 것이다.

    이에대한 자세한 설명은 밑에서 하도록 하겠다.

     

     

    2. 내부 동작 방식

     

    Overlapped I/O 지원 함수인 WSARecv() 함수를 요청하면 프로토콜 드라이버에 패킷을 받을 메모리(할당은 어플리케이션에서 할당한 유저 메모리)를 할당해 놓고 I/O가 Pending 되었음을 표시만 해둔다.

     

    네트워크 디바이스의 인터럽트에 의해 패킷이 프로토콜 드라이버까지 올라오면 여기서 Pending된 I/O가 있는지 확인하고 있으면 해당 I/O의 버퍼에 데이터를 채워 넣는다.

    이러한 하드웨어 인터럽트에 의해 동작하므로 CPU 자원을 소모하지 않고 복수의 I/O의 처리가 가능해진다.

    어느샌가 보면 버퍼가 채워져있는 것이다.

     

     

    3. Zero - Copy

     

     

     

     

    소켓 버퍼에 복사하지 않고 사용자가 만든 유저버퍼에 직접 복사를 하기 때문에 한번의 복사 비용이 줄어들게 된다.

     

    이런 속도적 이점도 있지만 단점 또한 발생하게 된다.

    일단 사용자가 지정한 유저 버퍼는 page lock에 걸리게 된다. 그 이유는 만약 이렇게 지정한 메모리가 page돼버리게 되면 패킷이 도착하여 복사하려 할때 다시 메모리로 가져오는 수고를 해야하기 때문이다.

    그렇기에 빠른 복사를 위해 해당 버퍼는 page lock이 걸리지만 시스템은 단일 프로세스가 잠글 수 있는 페이지의 크기를 제한하고 있다. 이 제한된 양을 초과할 경우 수행 함수는 실패 할 것이며 에러를 발생 시킬 것이다.

     

    또한, 각각의 디바이스 드라이버는 I/O 요청을 삽입하기 위한 고정 크기의 리스트를 가지고 있는다. 이것은 Non-paged pool에 존재하며 이 리스트가 꽉차게되면 시스템은 더이상 요청을 디바이스에 전달할 수 없게되고 에러를 발생시킨다.

     

    마지막으로 해당 유저버퍼는 완료되기 전에 사용자에 의해서 삭제되어서도 안된다.

    결론. 너무 많은 양의 I/O가 발생할 경우 위와 같은 우려상황이 발생할 수 있다는 것을 주의해야한다.

     

     

    4. 정리

    ① 가장 중요한 부분은 "non-blocking + 비동기 I/O처리" 라고 할 수 있다. 즉, 일반 논블럭 함수와의 차이는 I/O에 대한 처리마저도 비동기로 하여 복사하는 작업 동안 블로킹 되지 않는다는 것이다.

    ② I/O의 요청을 순서대로 처리하지 않는다. 디스크의 현재 위치로 부터 가장 가까운 위치의 I/O를 먼저 처리한다.

    ③ 하나의 핸들 디바이스에 중첩하여 I/O 요청을 수행한다. 즉 , 하나의 소켓에 대하여 중첩 수행이 가능하다는 뜻이다.

    ④ Zero-Copy를 통해 소켓 버퍼로의 복사를 건너 뛰어 바로 유저 버퍼로 데이터를 복사한다. 그러므로 복사가 완료 되기 전까지는 유저 버퍼를 삭제해서는 안되며, 너무 많은 I/O 요청이 있을 경우 I/O 요청 리스트의 관리를 위해 Non-paged pool의 메모리를 많이 소모하여 문제가 생길 수 있으며, 유저 버퍼가 swap 되지 않도록 page lock을 걸어 메모리 부족 현상이 일어 날수

     

     

     

    [참고]

    http://linkmemo.tistory.com/entry/Overlapped-IO-Note-1

    http://yuchi.iptime.org/xe/Programming_QA/5799

    http://the24hours.tistory.com/entry/NDIS-Driver%EC%9D%98-%EC%9D%B4%ED%95%B4 // 드라이버 내용

     

    'Programming > Network' 카테고리의 다른 글

    SO_SNDBUF 0  (0) 2016.12.04
    TCP Header  (0) 2015.06.19
    page-locking & non-paged pool  (0) 2015.06.19
    shutdown()과 closesocket()  (1) 2015.06.15
    IOCP - 1 (I/O CompletionPort)  (0) 2014.12.14

    댓글