Windows IOCP 서버를 Linux epoll로 이식할 때 완료 통지와 준비 통지의 차이를 연결 상태 머신에 어떻게 반영하나요?
잠깐, 답변을 보기 전에
먼저 내 말로 설명해 볼까요?
답변과 점검 포인트 펼치기답변 접기
구두 답변
정의를 외우기보다, 원리와 조건을 연결해 설명해 보세요.
IOCP와 epoll은 모두 비동기 네트워크에 쓰이지만 통지 의미가 다릅니다. IOCP는 제출한 OVERLAPPED I/O가 완료됐다는 결과와 바이트 수를 주므로 완료 컨텍스트가 버퍼를 처리하고 다음 작업을 제출합니다. epoll은 논블로킹 파일 디스크립터가 지금 읽기·쓰기를 시도할 수 있다는 준비 상태를 알리므로, 이벤트 뒤 애플리케이션이 read·write를 직접 호출하고 부분 결과·EAGAIN을 처리해야 합니다. API 호출 이름만 바꾸면 이식이 끝났다고 볼 수 없습니다.
완료와 준비의 상태 모델
IOCP 수신에서는 커널에 버퍼와 OVERLAPPED를 제출하고 완료 바이트 수를 받은 뒤 그 범위를 프레이밍 파서에 넘깁니다. 해당 버퍼의 수명은 완료까지 유지하고, 완료 후 필요하면 다음 수신을 제출합니다. epoll에서는 읽기 이벤트가 와도 한 번의 read가 전체 메시지를 주지 않으며, level-triggered라면 아직 읽을 데이터가 남아 다음 이벤트가 올 수 있고, edge-triggered라면 EAGAIN이 나올 때까지 읽어야 합니다. 처리 예산 때문에 중간에 멈추면 ‘아직 읽을 작업이 있다’는 상태를 별도로 남기고 다시 스케줄해야 합니다. 이를 준비 기반 진행 상태(readiness-driven progress state)라고 부르겠습니다.
쓰기에서도 IOCP의 WSASend 완료와 epoll의 writable 준비는 다릅니다. epoll write는 일부 바이트만 소비할 수 있으므로 남은 오프셋을 연결 송신 큐에 기록하고, 큐가 비어 있을 때 writable 감시를 제거합니다. 항상 writable인 소켓을 계속 감시하면 이벤트 루프를 바쁘게 만들 수 있습니다. IOCP에서는 각 송신 작업의 완료와 버퍼 수명을 추적하며, 여러 in-flight 작업의 논리 메시지 순서를 별도로 보장합니다.
취소·EOF·공통 계약
IOCP의 취소는 이미 제출된 작업의 완료·취소 결과와 컨텍스트 수명을 drain하는 문제입니다. epoll에서 epoll_ctl(DEL)이나 FD close는 감시 목록에서 이벤트를 제거하는 것일 뿐 이미 실행 중인 콜백·다른 스레드의 read·write가 끝났다는 뜻이 아닙니다. 양쪽 모두 연결의 세대 번호나 생존 상태를 두고 늦은 이벤트가 새 연결에 적용되지 않게 합니다. EOF, EAGAIN, protocol error, 소켓 오류를 상위 상태 머신의 의미 있는 이벤트로 통일하되, 하위 어댑터의 준비·완료 차이는 숨기지 않고 버퍼 소유권과 재진입 계약은 명시하겠습니다.
공통 계약 테스트는 부분 읽기·부분 쓰기·여러 프레임 한 번에 읽기·EOF·EAGAIN·취소 경합·연결 재사용·콜백 재진입을 같은 입력으로 실행합니다. IOCP는 completion key와 OVERLAPPED의 수명을, epoll은 FD 재사용과 이벤트 stale 방지를 특히 점검합니다. 성능 비교 전에 두 구현이 동일한 상위 불변식, 즉 프레임 순서·버퍼 유효 기간·한 번의 종료·송신 잔량 처리를 보장하는지 확인하겠습니다. 필요하면 io_uring도 완료 모델과 제공 버퍼 수명 계약을 따로 검토하겠습니다.
epoll의 edge-triggered 모드에서 이벤트를 한 번 처리하고 돌아가는 것은 버퍼를 다 비웠다는 의미가 아닙니다. read가 EAGAIN이 될 때까지 읽을 예산과, 예산을 넘겨 다음 이벤트로 미룰 때 재등록·내부 ready 상태를 어떻게 둘지 정해야 합니다. IOCP에서는 완료 바이트가 이미 발생한 작업의 결과이므로 같은 버퍼에 대한 다음 제출 시점을 완료 처리와 연결합니다. 또한 Linux FD 번호가 닫힌 연결의 번호를 새 연결에 재사용할 수 있으므로 stale 이벤트가 새 객체에 적용되지 않도록 세대를 확인하겠습니다.
FD와 OVERLAPPED는 재사용 시 위험의 모양이 다릅니다. IOCP는 미완료 작업이 가리키는 구조체 수명을 유지해야 하고, epoll은 닫힌 FD 번호의 새 객체에 오래된 이벤트가 적용되지 않게 세대·등록 토큰을 확인해야 합니다. 공통 어댑터의 성공 반환을 ‘메시지 완성’으로 정의하지 않고 부분 진행 결과로 정의하겠습니다.
득점 포인트
이해가 드러나는 지점을 확인해 보세요.
- IOCP 완료 통지와 epoll 준비 통지에서 실제 read·write가 실행되는 위치를 구분한다.
- 부분 처리·EAGAIN·재스케줄·송신 잔량을 OS별 상태 머신에 반영한다.
- 취소·FD 제거·콜백 실행과 버퍼·연결 수명을 별도로 다룬다.
- 상위 의미 계약과 OS 하위 구현을 동일한 계약 테스트로 검증한다.
감점 포인트
놓치기 쉬운 조건과 오해를 짚어 보세요.
- IOCP API와 epoll API를 이름만 일대일 치환한다.
- epoll 이벤트 하나가 전체 메시지나 전체 쓰기를 완료한다고 가정한다.
- 감시 제거·FD close가 이미 실행 중인 콜백과 메모리 접근까지 중단한다고 말한다.
- 공통 인터페이스에서 버퍼 소유·재진입·취소 계약을 숨긴다.
더 파고들 거리
하나 더 질문하며 이해의 범위를 넓혀 보세요.
- IOCP와 epoll을 같은 인터페이스로 감쌉니다. 버퍼 소유·준비·완료·취소를 어느 계층에서 표현하나요?
- 콜백 재진입과 한 연결의 동시 read·write를 어떤 계약과 테스트로 제한할까요?
- io_uring을 IOCP와 비교할 때 완료 모델과 제공 버퍼 수명에서 무엇을 별도 검증할까요?
득점·감점 포인트는 학습용 자기 점검 기준이며, 특정 회사의 실제 채점표가 아닙니다.
원리부터 공부하고 싶다면
답변을 보충하는 개념·예제·슈도코드를 읽어 보세요.