비동기 I/O 와 APC _System Programming

개념 :


동기 I/O : I/O 작업의 수행을 위해 호출된 함수가 블로킹 상태에 놓이기 때문에 CPU는 블로킹 상태에서
              반환될 때까지 일을 하지못한다.

비동기 I/O : I/O 작업의 수행을 위해 함수를 호출하자마자 반환한다. 따라서 CPU는 그다음 작업을 진행한다.



중첩(Overlapped) I/O :


넌블로킹(Non-Blocking) 함수를 이용하여 여러작업을 동시에 진행한다.
함수가 바로 반환되므로 계속 중첩해서 I/O 요청을 할수가 있다.
(참고 : 동기 방식(블로킹) I/O 에서는 전송을 위해 할당된 내부 메모리 버퍼에
          데이터가 복사가 되면 함수가 반환된다. (데이터 전송이 완료될 때 반환되는 것이 아니다)


OVERAPPED 구조체

typedef struct _OVERLAPPED {
        ULONG_PTR Internal;                // WIndows 시스템 내부 변수 ( 0으로 초기화 )
        ULONG_PTR InternalHigh;          // WIndows 시스템 내부 변수 ( 0으로 초기화 )
        union{
              struct {
                       DWORD Offset;            // 파일포인터 위치 지정
                       DWORD OffsetHigh;      // 파일포인터 위치 지정
              };
              PVOID Pointer;
        };
        HANDLE hEvent;           // I/O 연산이 완료되었음을 확인하기 위한 이벤트 커널 오브젝트
}; OVERLAPPED, *LPOVERLAPPED;


GetOverlappedResult() 함수 :

I/O 연산이 완료된 이후에 전송된 데이터 크기 확인을 위해 호출한다.

BOOL GetOverlappedResult(        //  
          HANDLE hFile,                                          // 입력 및 출력결과를 확인할 오브젝트의 핸들을 지정
          LPOVERLAPPED lpOverlapped,                   // OVERLAPPED 구조체 변수의 주소값을 전달
          LPDWORD lpNumberOfBytesTransferred,      // 실제 전송이 완료된 바이트 크기를 얻기 위한 변수주소
          BOOL bWait                                               // I/O 연산이 끝나지 않은 상황에서의 처리 결정
);                                                                       // TRUE : I/O 연산이 끝날때까지 기다린다.
                                                                          // FALSE : I/O 연산이 끝나지 않아도 반환한다.


중첩 I/O 예제 : 파이프 통신 기반.

(참고 : http://dakuo.tistory.com/entry/파이프Pipe-IPC-통신-소스 에서 클라이언트는 그대로고
          서버만 중첩 I/O 기반으로 수정했습니다. 변경된 부분은 진하게 썻습니다)

중첩 I/O PipeServer 예제

(참고 :

overlappedInst.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL); 에서 생성된 이벤트 오브젝트는

Signaled 상태로 생성되며, 사용자 리셋 모드의 이벤트 오브젝트이다.

OVERLAPPED 구조체 변수의 멤버로 등록된 이벤트 커널 오브젝트는 ReadFile, WriteFile 함수 호출시

자동으로 Non-Signaled 상태가 된다.(초기 상태와 자동 리셋모드가 의미가 없다)) 

중첩 I/O 에서는 WriteFile, ReadFile 함수가 NULL을 반환해도
(과도한 중첩된 I/O 연산으로에 의해 발생한 병목현상 일수도 있기 때문에 (NULL 반환)
블로킹 함수를 사용한 단일 I/O 연산에서는 하나의 I/O 연산만 수행하므로 병목현상이 발생하지 않는다)

GetLastError 함수 호출을 통해

과도한 I/O 요청에 의한 문제인지 (반환값이 ERROR_IO_PENDING) 아닌지를 검사해야 한다)



완료루틴(Completion Routine) 기반 확장 I/O


완료루틴(Completion Routine) : I/O 연산이 완료되었을 때 실행되는 루틴
(ex. I/O A 연산이 완료되면 Routine D 가 실행된다.
       I/O B 연산이 완료되면 Routine E 가 실행된다)


확장 I/O 제공 기능 = 중첩 I/O 기능 + α(알파)

확장 I/O 기반 함수 :

BOOL WriteFileEx(
        HANDLE hFile,                            // 데이터 전송의 대상 핸들 (FILE_FLAG_OVERLAPPED 속성)
        LPCVOID lpBuffer,                       // 전송할 데이터를 지니는 버퍼주소
        DWORD nNumberOfBytesToWrite,  // 전송할 데이터의 크기
        LPOVERLAPPED lpOverlapped,     // OVERLAPPED 구조체 변수의 포인터
        LPOVERLAPPED_COMPLETION_ROUTINE lpCOmpletionRoutine  // 연산 완료시 호출되는 완료루틴
);


LPOVERLAPPED_COMPLETION_ROUTINE 선언

typedef VOID (WINAPI *LPOVERLAPPED_COMPLETION_ROUTINE)(
          DWORD dwErrorCode,                               // 에러코드
          DWORD dwNumberOfBytesTransfered,        // 완료된 I/O 연산 데이터의 크기
          LPOVERLAPPED lpOverlapped                   // OVERLAPPED 구조체 변수의 포인터
);


BOOL ReadFileEx(
         HANDLE hFIle,                                // 데이터 수신의 대상 핸들 ( FILE_FLAG_OVERLAPPED 속성 )
         LPVOID lpBuffer,                              // 데이터 수신을 위한 버퍼주소
         DWORD nNumberOfBytesToRead,      // 읽어 들일 데이터의 최대 크기를 바이트 단위로 지정
         LPOVERLAPPED lpOverlapped,          // OVERLAPPED 구조체 변수 포인터
         LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine  // 연산 완료시 호출되는 완료루틴
);



알림가능한 상태(Alertable State) :
 
Windows 는 I/O 연산이 완료된 후 완료루틴 실행 타이밍을 프로그래머가 결정할수 있도록 하고 있다.

DWORD SleepEx(
          DWORD dwMilliseconds,     // 전달되는 시간만큼 블로킹에 들어간다.
          BOOL bAlertable                 // FALSE : Sleep 함수와 동일
);                                                // TURE : 이 함수를 호출한 쓰레드를 알림 가능항 상태로 변경

WaitForSingleObjectEx(HANDLE hHandle, dwMilliseconds, BOOL bAlertable)

WaitForMultipleObjcetsEx(DWORD nCount, COSET HANDLE *lpHandles, BOOL fWaitAll,
                                     DWORD dwMilliseconds, BOOL bAlertable)



파일 포인터의 위치 정보 :

비동기 I/O 에서는 함수를 호출 하자마자 반환하므로 파일 포인터의 위치가 옮겨지지 않아

커널 오브젝트의 존재하는 파일의 포인터 위치 정보는 의미가 없다.

따라서 OVERLAPPED 구조체의 멤버 (Offset, OffsetHigh)에 직접 데이터 입·출력 위치를 계산해서 대입한다.

예제보기



타이머 완료루틴 :

CreateWaitableTimer(
           LPSECURITY_ATTRIBUTES lpTimerAttributes,             // 보안속성
           BOOL bManualRest,                                                 // TRUE : 타이머를 수동 리셋 모드로 생성
                                                                                         // FALSE : 타이머를 자동 리셋 모드로 생성
           LPCTSTR lpTimerName                                            // 타이머 커널 오브젝트에 붙일 이름
);


BOOL SetWaitableTimer(
           HANDLE hTimer, // 알람을 설정할 타이머 오브젝트의 핸들 (정해진 시간이되면 커널 오브젝트는 Signaled)
           const LARGE_INTEGER *pDueTime,               // 알람이 울리는 시간 ( + : 절대시간,   - : 상대 시간) 
           LONG lPeriod,          // 주기적으로 알람을 울리게 할 때 사용 (주기가 되는 시간 전달,  0일 경우 사용안함)
           PTIMERAPCROUTINE pfnCompletionRoutine,  // 완료루틴을 지정 
           LPVOID lpArgToCompletionRoutine,                // 타이머 완료루틴의 첫 번째 전달인자가 그대로 전달된다
           BOOL fResume                                            // 전원관리. 기본적으로 FALSE 전달
);


타이머의 완료 루틴 :

VOID CALLBACK TimerAPCProc(
          LPVOID lpArgToCompletionRoutine,
          DWORD dwTimerLowValue,
          DWORD dwTimerHighValue
);

전달인자 dwTimerLowValue 와 dwTimerHighValue 를 통해서 타이머가 Signaled 상태가 된 시간 정보가 전달되며

첫번째 전달인자 lpArgToCompletionRoutine 은 SetWaitableTimer 함수의 다섯 번째 인자로 그대로 전달된다.



APC(Asynchronous Procedure Call) : 비동기 함수 호출 매커니즘

User-mode APC :

모든 쓰레드는 자신만의 APC Queue (쓰레드별로 독립)를 가지고 있다.


APC Queue 의 접근 :

DWORD QueueUserAPC(
           PAPCFUNC pfnAPC,  // 비동기로 호출될 함수 ( VOID CALLBACK APCProc(ULONG_PTR dwParam)
           HANDLE hThread,     // 비동기 함수 정보를 추가할 APC Queue (APC Queue 를 소유하는 쓰레드의 핸들)
           ULONG_PTR dwData // APC Queue 에 등록된 함수 호출시 전달할 인자
);

덧글

댓글 입력 영역


통계 위젯 (블랙)

85
32
54502

블로그 랭킹 위젯

마우스오른쪽금지