본문 바로가기

Robotics/Software Tech.

IOCP 사용하기

로봇에서 사용하는 소프트웨어라고 뭐가 다를까..
로봇도 하나의 컴퓨터이다. 똑같다. 움직인다는 것 빼고는...

로봇에서 그냥 단순하게 소켓통신으로 하면되지 무슨 IOCP까지 써가면서 네트워크 프로그램을
만들어야 하나...에 의문을 가질수 있는 사람도 있다.
뭐.. 로봇과 조종자간의 단순한 1:1통신이라면 Asyncsocket으로 해도된다.. 충분하다..
하지만, 요즘 네트워크로 조종되는 로봇.. 그리고, 별도의 서버를 두고 여러대의 로봇이 이 서버로부터
정보를 받을경우.. 말은 달라진다..

로봇은.. 그냥 컴퓨터다.. 이렇다면, 일반 네트워크 서버를 단순하게 구성하는 방법으론 문제가 생길법하다..
그래서, 로봇에도 IOCP를 사용하자..

IOCP

IOCP(I/O Completion Port)는 어떤 I/O 핸들에 대해서, 블록 되지 않게 함으로 프로그램 대기시간을 줄이는 목적으로 사용된다.
우선 Overlapped IO(비동기 IO작업)의 개념이 기반이 된다. IOCP는 이런 Overlapped IO가 실행이되고 알려주는 방법에 대한 것이다.
그리고 IOCP는 커널객체이다. 뮤텍스나 스레드와 같이 커널에서 제공을 한다. (커널 객체는 일반적으로 전역적으로 사용되는 개념인거 같다.;;)

사용 이점

  • 사용자가 설정한 버퍼만을 사용하기 때문에 더 효율적으로 작동한다. (기존에는 OS버퍼, 사용자 버퍼로 따로 분리되는 개념이었다.)
  • IO요청에 대해서 효율적으로 접근한다. 디스크IO의 경우 디스크에 접근을 똑똑하게 한다-_-;(순서대로가 아닌 효율적인 순서에 따라 접근하기도 한다.는 뜻;;)
  • Raid나 여러개의 랜카드같은 Deviced-level Parallelism을 지원한다. 역시나 효율적으로 할 수 있다.

사용 순서

1. IOCP핸들을 생성한다.
2. 핸들의 키와 쌍으로 IOCP핸들에 등록한다.
3. 핸들에 I/O 작업을 한다. (I/O작업을 신청한다는 의미가 더 어울릴 것 같다.)
4. 어떤 핸들에 대한 I/O작업이 완료되면 IOCP가 프로그램에게 알려준다.
5. 완료된 작업에 대해 해야 할 일을 한다.

  • 윈속API는 버전 1.1에서는 OverlappedIO를 지원하지 않는다. 반드시 2.2를 사용할것.
      소켓 생성(WSASocket)을 할때 마지막에 반드시 WSA_FLAG_OVERLAPPED를 넣어줄것. 반드시란다. 버그가 있기때문이란다;;
      WSASend, WSARecv사용할때 Overlapped구조체를 넣어줄것. LPWSAOVERLAPPED나 LPOVERLAPPED는 그놈이 그놈임-_-;
      LPWSAOVERLAPPED_COMPLETION_ROUTINE는 콜백루틴을 넣는것인데 버그이기때문에 사용 금지다-_-;

사용법

1. IOCP핸들을 생성한다.


HANDLE CreateIoCompletionPort (
     HANDLE FileHandle,              // 목적하는 핸들.
     HANDLE ExistingCompletionPort,  // 연결할 IOCP의 핸들. 새로만들려면 NULL로 설정.
     ULONG_PTR CompletionKey,        // 핸들을 통한 작업이 끝났을때 IOCP는 이 컴플리션 키값을 넘겨준다.
     DWORD NumberOfConcurrentThreads // 입출력 작업시 얼마나 많은 스레드를 사용할건지 설정. 0으로하면 시스템에서 알아서 잡아줌.
);

성공시 IOCP핸들을 반환. 실패시 0.
처음 생성시 인자로 INVALID_HANDLE, NULL, NULL, NULL를 넣어줌. 2. 핸들의 키와 쌍으로 IOCP핸들에 등록한다.


HANDLE CreateIoCompletionPort (
     HANDLE FileHandle,              // 목적하는 핸들.
     HANDLE ExistingCompletionPort,  // 연결할 IOCP의 핸들. 새로만들려면 NULL로 설정.
     ULONG_PTR CompletionKey,        // 핸들을 통한 작업이 끝났을때 IOCP는 이 컴플리션 키값을 넘겨준다.
     DWORD NumberOfConcurrentThreads // 입출력 작업시 얼마나 많은 스레드를 사용할건지 설정. 0으로하면 시스템에서 알아서 잡아줌.
);

같은 함수를 사용해서 ExistingCompletionPort에 위의 생성한 IOCP핸들을 넣어준다.
연결된 IOCP핸들이 반환된다. 

3. 핸들에 I/O 작업을 한다. (I/O작업을 신청한다는 의미가 더 어울릴 것 같다.)

IOCP는 중첩된(Overlapped)된 I/O방식을 사용한다.
따라서, WSASend(), WSARecv(), ReadFile(), WriteFile()를 사용할 것을 권한다.
보통의 IO함수랑 다른 차이점은 Overlapped구조체를 요구한다. 대부분 필요에 따라 구조체를 변형시켜서(상속, 포함)사용한다.
그리고 여기서 설정된 Overlapped구조체나 데이터들은 작업이 완료될때까지 변경되어서는 안된다. 
비동기로 요청을 하였으므로 바로 반환된다. 그러므로 SOCKET_ERROR을 반환하지만 에러는 아니다.
반드시 GetLastError()로 조사를 하고 ERROR_IO_PENDING이 나오면 정상적인 비동기 작업이다.

4. 어떤 핸들에 대한 I/O작업이 완료되면 IOCP가 프로그램에게 알려준다.


BOOL GetQueuedCompletionStatus(
     HANDLE CompletionPort,       // IOCP 핸들
     LPDWORD lpNumberOfBytes,     // 읽거나 쓰여진 바이트의 수
     PULONG_PTR lpCompletionKey,  // IOCP를 생성시 등록된 CompletionKey값을 반환
     LPOVERLAPPED *lpOverlapped,  // Overlapped 구조체
     DWORD dwMilliseconds         // 기다리는 시간. INFINITE로 무한대로 기다릴수 있음.
);

반환값은 FALSE라고해서 실패는 아니고. 반환되는 lpOverlapped구조체가 NULL이고 GetLastError()로 조사시 64의 값이 아닐때 에러란다-_-;

BOOL PostQueuedCompletionStatus (
     HANDLE CompletionPort,            // IOCP 핸들
     DWORD dwNumberOfBytesTransferred, // 읽거나 쓰여질 바이트의 수
     ULONG_PTR dwCompletionKey,        // 등록할 CompletionKey값
     LPOVERLAPPED lpOverlapped         // Overlapped 구조체
);

IOCP가 반드시 파일이나, 네트웍의 전송으로만 사용되어야 할 필요는 없다.
스레드간의 통신에서도 이렇게 CompletionKey값을 주고받는것으로 대체 할 수도 있다.

5. 완료된 작업에 대해 해야 할 일을 한다.


수행된 내용의 다음 내용을 처리한다.

----------------------------------

IOCP와 이벤트를 연결시키면 우리는 입출력이 완료 되는 시점에서 이벤트를 발생시킬 수 있다.
IOCP와 이벤트 핸들을 연결시키려면 우선 소켓을 만들고 입출력 완료 포트를 생성해야 한다.

함수 :CreateloCompletionPort()
설명:이 함수는 IO Completion port를 생성하여 핸들 값을 반환한다.
원형:
HANDLE CreateloCompletionPort(
    HANDLE              FileHandle,
    HANDLE              ExistiongCompletionPort,
    DWORD               completionKey,
    DWORD               NumberOfConcurrentThreads
);

리턴값:성공하면 I/O Completion Port의 핸들 값을 리턴하고, 실패할 경우 NULL을  리턴한다.
에러의  상태를 확인하려면 GetLastError()함수를 호출하면 된다.

1.FileHandle
Overlapped I/O completion을 연결할 파일 핸들.CreateFile()함수를 이용할 경우에는 핸들을 얻기위해 반드시 FILE_FLAG_OVERLAPPED 플래그를 지정해야한다.

2.ExistinggCompletionPort의 핸들.
이 파라미터에 이미 존재하는 Completion Port를 지정하면 해당 핸들로 연결된다. 새로 생성하고 싶다면 이 인자에 NULL을 지정한다.

3.CompletionKey
완료키를 지정한다. 모든 I/O completio Prot는 CompletionKey를 포함한다.

4.NumberOfConcurrentThreads
동시에 발생을 허용하는 쓰레드의 숫자를 지정한다.

Completion Port 생성에 성공했다면 이제 GetQueuedCompletionStatus()함수를 호출해서 이벤트를 기다린다. 만일 이벤트가 발생하지 않는다면 이 함수는 블로킹 상태로 있을 것이며, 이벤트가 발생한다면 블로킹을 해제한다. 마치 WaitForMultipleObject()함수와 같은 역활을 수행하는것이다.


함수:GetQueuedCompletionStatus()
설명:완료 포트 (Completion port)의 상태를 얻어온다.
원형:
BOOL GetQueuedCompletionStatus(
    HANDLE              CompletionPort,
    LPDWORD           lpNumberOfBytesTransferred,    
    LPDWORD           lpCompletionKey,
    LPOVERLAPPED *lpOverlapped
    DWORD               dwMilliseconds
);

리턴 값: 성공할 경우 TRUE를 , 실패할 경우 FALSE를 리턴한다.
완료 포트의 상태는 네 번째 인자인 lpOverlapped에 리턴한다.


1.CompletionPort
이벤트 발생을 대기할 I/O Completion port의 핸들.

2.lpNumberOfBytesTransferred
입출력된 데이터의 크기를 리턴 받을 변수

3.lpCompletionKey
지정된 CompletionKey.  이 Key 값으로 누구에게 (어떤 파일로 )데이터가 입출력되었는지를 알수있다.

4.*lpOverlapped
입출력 결과를 리턴 받을 OVERLAPPED 구조체의 포인터.

5.dwMilliseconds
밀리세컨 단위의 숫자로 Completion port에 패킷 전달이 완료되기를 기다리는 시간이다. 즉 이 인자로 주어진 시간이 지나면 블로킹이 해제 된다.

'Robotics > Software Tech.' 카테고리의 다른 글

유니코드(unicode)와 아스키코드(asciicode)  (0) 2008.01.04
AJAX  (0) 2008.01.02
[MFC]TRACE 파일 저장  (0) 2008.01.01
PUMA Inverse kinematics C 프로그램 소스  (0) 2007.12.26
인터넷폰(internet phone) 논문  (0) 2007.12.20