코딩하는 두식이
IOCP 정리 본문
IOCP
IOCP는 Overlapped I/O에 스레드 풀링(Thread pooling)과 큐(Queue)라는 매커니즘을 동시에 접목시킨 기술
Overlapped I/O와 차이는?
CreateCompletionPort() 함수를 통해 소캣과 컴플리션 키를 관리하는 디바이스 리스트 자료구조 사용, 디바이스 리스트는 컴플리션포트 객체에 등록된 디바이스를 관리한다.
IOCP 큐 자료구조 : Overlapped는 모델을 요청하면 작업이 완료되는 순간 이벤트나 콜백함수로 결과를 돌려줬다. IOCP는 커널이 프로그래머가 요청한 Overlapped 작업이 완료되었을 때 곧바로 사용자에게 알리는 것이 아니라 완료된 작업 결과를 IOCP 큐에 넣은 후 컴플리션 객체를 이용해 사용자에게 알린다. 커널 스레드 풀링을 통해 IOCP 큐에서 완료된 Overlapped IO 작업을 가져와 처리한다.
스레드 풀링
그림으로 배우는 IOCP

IOCP Queue
WatingThreadQueue(LIFO)
완료된 작업이 있을 때 까지 스레드가 대기 상태로 있는 곳
ReleaseThreadQueue
IOCP Queue에서 완료된 작업결과를 가져와 처리하기 위한 실행 상태 스레드가 있는 곳
PauseThread List
처리 도중 대기 작업에 의한 대기 상태로 바뀐 스레드가 있는 곳
짚고 넘어가야 할 세 가지
GetQueuecompletionStatus()
1. IOCP를 구성하는 5가지 자료구조
① Device List
hDevice는 네트워크에선 Socket이며, CompletionKey는 유일한 키값이다.
IOCP에 두 인자를 등록 시 DeviceList라고하는 자료구조에 두 값이 묶여서 저장된다.
이렇게 저장된 값들은 GQCS를 통해 해당 소켓에 데이터가 완료되면 Key와 함께 값을 얻을 수 있게된다.
② I/O Completion Queue
IOCP 큐라고도 불리며 I/O가 완료되면 그 관련 정보를 저장하는 자료구조이다.
착각하지 말아야 하는것은 큐에 패킷이 들어가는것이 아니라 그에 대한 정보(이벤트)가 들어간다는 것이다.
이 큐의 특징은 FIFO라서 앞에서 부터 순차적으로 자료를 빼서 처리한다.
③ Waiting Thread Queue
스레드 들이 대기하는 큐이며 LIFO로 동작한다. 즉, 가장 마지막에 사용된 녀석이 다음번에 다시 사용될 가능성이 높다.
실제로 스레드들이 들어가 있는것이 아니라 해당 스레드 ID가 들어가 있다.
IOCP에서 스레드풀의 개념을 이 큐에서 담당하고 있다고 볼 수 있다.
④ Released Thread List
스레드 풀. 바로 Waiting Thread Queue에서 꺼내온 쓰레드 정보라고 할 수 있다.
밑에서 설명하겠지만 이 큐에 들어 갈 수 있는 최대 스레드 갯수는 IOCP 설정 당시 셋팅을 한다.
⑤ Paused Thread List
정지된 스레드 리스트, 블로킹 함수 등에 의해 해당 Release 스레드가 Suspend 상태가 되면 이 리스트로 들어가게 된다.
어떤 이벤트에 의해 스레드가 Suspend 해제 상태가 되면 다시 Released Thread List로 옮겨간다.
전체적인 그림을 보자면 다음과 같다.
여기서 봐야할 점은 완료 큐를 통해 얻을 수 있는 정보는 Device(Socket)이나 Buffer 정보가 아닌
CompletionKey값과 Overlapped 정보라는 것이다.
이 두 정보를 통해 우리가 필요로 하는 정보를 얻어와야 한다.
2. IOCP에 대한 의문점 및 고찰
① 최초 생성에서 CreateIoCompletionPort의 인자값인 NumberOfConcurrentThreads
해당 함수의 인자 값은 생성할 스레드의 갯수가 아니다.
위에서 설명한 Released Thread List에 들어 갈 수 있는 최대 스레드 갯수이다.
즉, 동시에 작업을 수행할 스레드의 갯수라고 할 수 있다.
스레드 당 CPU 하나가 가장 이상적인 퍼포먼스를 내는 스레드 수 이기 때문에 CPU 갯수에 맞춰 이 값을 셋팅한다.
0 값을 넣으면 자동으로 해당 PC의 CPU 갯수로 세팅된다.
② 스레드 풀이 생성되는 시점
윈도우가 스레드를 가득담은 풀을 제공해주는 것은 아니다.
우리가 스레드를 충분히 만들어 두고 각 스레드에서 GQCS()를 호출하여 Blocking됨과 동시에 Waiting Thread Queue로 ID가 들어가게 되면 이것이 풀처럼 동작하는 것이다.
③ LIFO에서 오는 장점
이 부분은 FIFO일 때와 비교해보면 좋다.
LIFO 구조로 스레드를 가져다 쓴다고 하면 거의 항상 쓰던 스레드를 사용하게 될것이다.
이렇게 빈번하게 사용되는 스레드의 정보는 하드로 Swap되지 않을 것이고 캐시의 정보 또한 마찬가지 일것이다.
하지만 FIFO일 경우 모든 스레드가 동일하게 한번씩을 사용될 것인데. 이럴 경우 호출한지 좀 지난 스레드는
정보가 하드로 스왑될 것이고 다시 사용할 때 다시 하드에서 가져오는 작업을 해야 하는것이다.
④ 완료 통보에 대한 순서 문제
동일 소켓에 대해 I/O의 완료처리는 순서가 다를 수 있다. '동일' 이란 부분에 주의하자.
한 소켓에 100byte Recv와 200byte Recv를 중첩해서 걸어 놨다면 200byte의 완료 처리가 먼저 일어 날 수 있다는 뜻이다.
위에서 설명했듯 IOCP의 완료 통보는 확실히 순서적이지 않을 수 있다.
하지만 정확히 이해가지 않는 부분은 Overlapped I/O의 I/O 처리에 대한 순서 보장이다.
Noetwork Programming For Microsoft Windows라는 책을 보면 Overlapped I/O의 처리 동작은 순차적으로 나와있다.
하지만 이기탁씨의 문서나 Via를 보면 Overlapped I/O의 처리 동작은 비 순서적이라고 한다.
어찌됐듯 동일 소켓에 대해 비순서적인 처리가 일어날 여지가 있다면 해결 방법은 간단하다.
바로 한 소켓에 한번의 Recv만을 거는 것이다. 그렇게되면 순서가 뒤바뀌거나 할 일은 절대 없을 것이다.
⑤ WorkerThread의 갯수
WorkerThread로 사용할 스레드의 갯수를 결정할 때 MSDN은 CPU * 2 + 1이라는 알 수 없는 숫자를 추천한다.
WorkerThread의 갯수를 CPU 갯수보다 크게 잡으라고 하는 의미는 바로
스레드가 Suspend 될 경우가 있기 때문이다.
스레드를 CPU 수에 딱 맞춰 생성해 놓았는데 해당 스레드가 작업을 처리하던 도중 Blocking 함수를 만나 Paused Thread List로 들어가게 될 경우. 다른 작업이 들어왔을 때 작업을 처리할 스레드가 없어서 다른 스레드의 작업이 끝날 때까지 기다려야 하는 상황이 올 수 있다.
그렇기 때문에 저런 알 수 없는 숫자를 추천한 것이고, 실제로 가장 적절 스레드 수는 테스트를 통해 알아내야 하는 것이 맞다.
정리를 하자면,
IOCP란 "OverlappedI/O + ThreadPool + CompletionQueue"라고 할 수 있다.
I/O Completion Port 모델
- 윈도우즈에서 제공하는 I/O모델 중 최고의 성능
- Completion Port객체는 Overlapped I/O에서 쓰레드 풀링과 Queue라는 메커니즘을 동시에 접목.
쓰레드 풀링
- 풀(Pool)이란 집합소 또는 수영장에서 물을 모아놓은곳등의 의미. 즉 어떤 것을 모아 놓은 곳을 말한다.
- 쓰레드 풀(Pool)은 실제로 프로그램에서는 시작할 때 여러개의 쓰레드를 대기상태로 미리 생성해놓은 것을 쓰레드 풀 이라 한다.
- 그리고 이 쓰레드 풀에 대기중인 쓰레드들 중에 현재 필요한 만큼 꺼내어 실행 상태로 바꾸어서 사용하고
다 사용한 스레드는 파괴하지 않고 다시 대기상태로 바꿔서 쓰레드 풀에 넣어주는 일련의 과정을 쓰레드 풀링
쓰레드 풀링을 사용하는 이유
1. 쓰레드 생성과 파괴에 따른 CPU소모를 줄이기 위함.
-> 쓰레드를 미리 생성해놓고 상태만 바꿔서 사용함으로써 쓰레드의 잦은 생성과 파괴를 하지 않아 할당된 CPU의 시간(타임 슬라이스)을 낭비하지 않고 다른일을 할 수 있기 때문.
-> 이 말은 곧 게임서버의 성능을 더 향상 시킬 수 있다는 의미
2. 쓰레드에서 다른 쓰레드로 작업 전환을 할때에 컨텍스트 스위칭(Context-switching)이 일어나는데, 이 Context- switching또한 CPU소모가 많으므로 자주 일어나면 성능이 떨어진다.
-> 쓰레드풀링을 사용하면 실행 상태의 쓰레드를 상황에 맞춰 조절하므로 잦은 컨텍스트 스위칭을 피한다.
위의 과정은 I/O CompletionPort모델의 입출력 핸들의 등록과 비동기 I/O요청, I/O의 완료통지 방법에대한 그림이다.
1. 유저모드. 즉 우리의 프로그램에서 코딩하는 영역에서는 단순히 생성한 CP객체에 입출력 핸들 + 완료 키 등록.
2. 그리고 비동기 I/O(Overlapped I/O)를 요청
-> 서버에서 클라이언트와 연결된 소켓이 생성되었으면 무조건 WSARecv함수를 호출.
-> 클라이언트가 언제 패킷을 보낼지모르며 미리 WSARecv를 걸어둠으로서 수신이 완료되었을 때 처리.(이때 다시 IO 요청)
3. 결과 확인을 위해 WSAGetOverlappedResult함수 호출이 아닌 GeqQueuedCompletionStatus(통칭 GQCS)함수를 호출.
-> 커널 내부 처리과정은 전자와 많은 차이를 보인다. (자료구조 및 메커니즘)
위 과정을 좀 더 풀어서 보자.
1. CreateIoCompletionPort(CP객체를 생성할때도 이 함수 호출)함수를 통해 입출력 핸들 + 완료키를 CP객체에 등록
-> 커널 내부에서 등록한 입출력 핸들과 완료 키(Completion Key)를 관리해주는 Device List 자료구조에 추가된다.
--> Device List는 우리가 등록한 입출력 핸들을 관리한다.
2. IOCP Queue(IO 완료큐)라는 자료구조의 추가
-> 이전 Overlapped IO모델은 요청한 작업이 완료되면 사용자에게 이벤트(Event)나 콜백(CallBack)방식으로 알림.
-> IOCP모델은 커널에게 우리가 요청한 Overlapped I/O작업이 완료 되었을 때 사용자에게 바로 알리는게 아닌
완료된 작업 결과를 IOCP Queue에 넣은 후에 CP객체를 이용하여 사용자에게 알린다.
--> 커널은 쓰레드 풀링 메커니즘을 이용하여 IOCP Queue에서 완료된 Overlapped I/O작업을 가져와 뒤 처리.
IOCP모델에서 쓰레드 풀링 메커니즘이 어떻게 동작하는지 보자.
IOCP Queue에 완료된 작업결과가 들어오면 WaitingThread Queue(LIFO)에서 대기하고 있던 쓰레드들 중
가장 나중에 추가된 몇개를 실행 상태로 만든다음 WaitingThread Queue에서 삭제한 후 ReleaseThread List에 추가한다.
그리고 실행상태로 바뀐 쓰레드들은 IOCP Queue에서 완료된 작업 결과를 가져와 유저 모드의 GQCS함수로 반환하고
완료된 작업의 뒤 처리를 계속해서 진행한다.
실행상태가 된 쓰레드들은 완료된 작업의 뒤처리를 하는데 만약 뒤처리 도중 대기작업을 하는 함수를 호출했다면
해당 쓰레드는 대기 상태로 바뀌고 PausedThread List에 추가된다.
또한 뒤처리 도중 대기상태로 들어간 쓰레드가 후에 다시 실행상태가 되면 그 쓰레드는 PausedThread List에서 삭제된
후 ReleaseThread List에 다시 추가된다. 그리고 전에 하던 작업을 뒤이어 계속하게 된다.
그리고 모든 뒤처리 과정을 마친후 다시 GQCS함수를 호출한다. IOCP Queue에 완료된 작업이 있다면 위의 과정을 반복.
없다면 쓰레드는 대기 상태로 바뀌고 ReleaseThread List에서 삭제된 후 WaitingThread Queue로 들어가게 된다.
즉 간단하게 말하면 GQCS함수에 진입한 쓰레드들은 IOCP Queue에 완료된 작업이 있을때까지 대기하다가,
작업이 완료된게 있으면 빠져나와(Release상태) 일을 처리하고 대기 함수에 진입하면(Pause)상태에 빠지고 빠져나오면
이어서 처리. 일을 다 처리하면 다시 다음 일감을 처리하기위해 대기한다.
여기서 중요하게 볼 세가지가 있다.
1. WaitingThread Queue의 쓰레드 입출력 순서가 FIFO가 아니고 LIFO로 동작한다.
-> 컨텍스트 스위칭을 피하기 위함. 즉 방금 대기한 쓰레드가 일감이 들어오면 바로 일처리. CPU최대활용.
-> 컨텍스트 스위칭은 서로 다른 쓰레드간의 전환이 일어나는 것.
여기서 대기 상태였던 쓰레드를 다시 실행상태로 바꾼것이고 서로 다른 쓰레드간의 전환은 일어나지 않았다.
-> 만약 마지막에 들어온 쓰레드가 아닌 다른 쓰레드가 실행상태로 바뀐다면 컨텍스트 스위칭이 일어난단 의미.
그렇기에 LIFO구조를 사용하는 것.
즉 A(실행)->A(대기)->A(실행)의 과정은 컨텍스위칭x. A(실행)->A(대기)->B(실행)의 과정은 컨텍스위칭O.
2. 동시에 실행상태에 있는 쓰레드의 개수.
- 사용자는 커널이 실행 상태에 있는 쓰레드의 개수를 조절하고 있다는것을 알아야한다.
만약 쓰레드 풀에 총 5개의 쓰레드가 미리 생성되었고 이중 2개의 쓰레드만 동시에 실행상태로 될수있다고 가정해보자.
여기서 동시에 실행 상태에 있는 2개의 쓰레드들 중 하나가 대기 작업을 하는 함수를 호출하여 대기 상태로 들어간다면
현재 실행상태(Release List)에 있는 쓰레드는 1개밖에 없다.
-> 그렇다면 커널은 WaitingThread Queue에서 한개의 쓰레드를 실행상태로 바꾼다.
이어서 WaitingThread Queue에서 삭제한 후 ReleaseThread List에 추가할것임.
그러면 다시 동시에 실행가능한 쓰레드의 수는(실행상태의 쓰레드수) 현재 다시 2개가 된 것.
그런데 이번엔 실행도중 대기작업 함수 호출로 인해 대기 상태에 들어간 쓰레드가 대기작업을 마치고 다시 실행상태가
된다면?
다시 ReleaseThread List에 들어가게 될 것이고 현재 실행상태에 있는 쓰레드의 개수가 3개가 되버린다.
이렇게 되면 동시에 실행 상태가 될 수 있는 쓰레드의 최대 허용 개수가 2개를 초과하게 되지만,
커널은 실행 상태에 있는 쓰레드들 중 먼저 뒤처리 작업을 마치고 GQCS함수를 호출한 쓰레드를 다시 대기 상태로만들어 동시에 실행 상태가 되는 쓰레드의 개수가 2개가 될 수 있도록 조정한다.
이렇듯 커널은 동시에 실행 상태가 되는 쓰레드의 개수가 부족해지면 쓰레드 풀에서 대기중인 쓰레드들을 꺼내서
실행상태로 만들고, 초과하는 경우에는 대기 상태로 만들어 다시 쓰레드 풀에 넣어주는 역할을 한다.
-> 즉 동시에 실행 상태에 있는 쓰레드 개수를 사용자가 설정한 일정 개수로 유지될 수 있도록 조절하고 있다는 것을 기억해야 한다.
3. WatingThread Queue에 대기중인 쓰레드가 충분히 있어야 한다는 것이다.
- 실행상태에 있는 쓰레드가 완료된 작업의 뒤처리를 하고있는 도중 대기 작업 함수를 호출했다면
그 쓰레드는 ReleaseThread List에서 삭제된 후 대기 작업이 끝날 때까지 PausedThread List에서 대기 상태로 기다리고 WaitThreadQueue에서 대기 중인 다른 쓰레드가 실행 상태로 바뀐다고 말했다.
근데 만약 WaitThreadQueue에서 대기중인 쓰레드가 없다면 사용자가 설정한 동시에 실행중인 쓰레드 개수가
유지되지 못하며(동시에 실행가능한 쓰레드 수) 최악의 경우에는 실행중인 쓰레드가 모두 대기작업 함수를 호출한다면 실행 상태에 있는 쓰레드가 없을 수 있다.
이런 경우를 피하려면 우선은 충분한 개수의 쓰레드를 WaitThread Queue에 대기 시켜놔야 한다.
일반적으로 권장하는 쓰레드의 개수는 2n + 1개인데 여기서 n은 CPU의 개수(CPU의 코어). 만약 2개의 CPU가 있는
머신이라면 총 5개의 쓰레드를 미리 만들어 놓는것이 적당하다. 하지만 이 쓰레드의 개수는 작업의 종류에 따라 달라질
수 있으며 스트레스 테스트를 통해 적절히 조정하는 것이 필요하다.
-> 또한 완료된 작업의 뒤처리를 하는 도중 대기시간이 긴 작업이나 대기시간을 알 수 없는 작업은 피하는게 좋다.
-> 왜냐하면 그런작업이 많아지면 쓰레드 풀이 금방 바닥나서 더이상 작업을 처리할 수 없게 될지도 모른다.
IOCP의 장점
1. 위에서 서술한 것처럼 적은 수의 스레드만을 사용하여 구현할 수 있다.
2. 적은 수의 스레드 사용으로 CPU점유율도 낮으며, Context switching 비용도 적다.
-> 윈도우 OS가 직접 스레드 풀링을 관리하기 때문에
3. winsock2 API중 가장 확장성과 성능이 뛰어나다.
4. Overrapped I/O를 확장 시킨 개념이기때문에 커널영역과 유저영역의 버퍼를 공유한다.
IOCP의 단점
1. 프로그램 구현이 복잡해진다.
2. window 기반 플랫폼에서만 사용이 가능하다.
3. 하나의 I/O operation 마다 버퍼영역에 대한 page-lock/unlock이 필요하다.
-> 특정 메모리에 대한 pin/unpin은 많은 CPU cycle을 요구한다.
-> recv 를 posting 할 때, page-locking을 피해서 zero-byte recv를 사용한다.
-> page-locking 찾아볼 것.
4. 하나의 I/O operation 마다 시스템 콜을 호출한다.-> 이로 인해 유저모드-커널모드 전환이 발생한다.
tbb 라이브러리
C++로 되어 있는 일종의 라이브러리이며 스레드 처리에 대한 전문 지식 없이도 멀티코어 프로세서 시스템의 성능을 향상 시키도록 도와줍니다.
Overlapped I/O
논블록 소켓 단점을 보완한 네트워크 통신 방법이 Overlapped I/O
논블록 소켓의 단점
소켓 I/O 함수가 리턴한 코드 would block 인 경우 재시도 호출 낭비 발생.
소켓 I/O 함수를 호출할 때 입력하는 데이터 블록에 대한 복사 연산 발생.
CPU 안에 있는 캐시 메모리에 메모리 내용이 복사되어 있으면 데이터 액세스는 매우 빠르지만
캐시에 없는 데이터를 액세스할 때는 메인 메모리 RAM을 액세스하는데, 이 속도는 매우 느림.
물론 하드디스크나 네트워크 데이터보다는 빠르지만 고성능 서버 개발 시 이 복사 연산 무시할 수 없음.
특징
1. Overlapped I/O 객체가 서로 다르고 데이터 블록도 서로 다르면 중첩 가능.
2. 운영체제는 송신할 데이터나 수신할 데이터가 있으면 데이터 블록을 복사하지 않고 그 데이터블록 자체를 그대로 사용.
3. 윈도우에서만 사용 가능.
4. accept, connect 함수 계열의 초기화 복잡
5. 완료되기 전까지 Overlapped status 객체가 데이터 블록을 중간에 훼손하지 말아야 함.
6. send, receive, connect, accept 함수를 한 번 호출하면 이에 대한 완료 신호는 딱 한 번만 오기 때문에
프로그래밍이 간결해짐.
epoll,
Overlapped I/O 또한, 소켓 개수에 비례해서 루프를 돌기 때문에 성능 문제는 존재함.
예를 들어 소켓이 1만개 이고 각 소켓이 초당 100번씩 I/O 가능 이벤트 혹은 I/O 완료 이벤트가 발생하면,
초당 100 만번의 처리를 해야 함.
소켓 개수가 많을 때 이러한 루프 없이 한 번에 끝내는 방법이 IOCP, epoll 이다.
epoll은 소켓이 I/O 가능 상태가 되면 이를 감지해서 사용자에게 알림을 해 주는 역할.
epoll 장점
상태변화의 확인을 위한, 전체 파일 디스크립터를 대상으로 하는 반복문이 필요 없다.
select 함수에 대응하는 epoll_wait 함수호출 시 관찰대상의 정보를 매번 전달할 필요가 없다.
이벤트가 발생한 파일 디스크립터의 정보가 별도로 묶이기 때문에 select 방식에서 보인 전체 파일 디스크립터를 대상으로 하는 반복문의 삽입이 불필요하다.
epoll은 논블록 소켓을 대량으로 갖고 있을 때 효율적으로 처리해 주는 API 이다.
Overlapped I/O를 다루는 운영쳊에서 대응한 것이 바로 I/O Completion Port 혹은 IOCP라는 것입니다.
IOCP는 소켓의 Overlapped I/O가 완료되면 이를 감지해서 사용자에게 알려 주는 역할을 한다.
epoll 과 IOCP 비교
epoll 과 가장 차이점은 epoll은 I/O 가능인 것을 알려 주지만, IOCP는 I/O 완료인 것을 알려 준다는 것.
IOCP는 epoll에서 할 수 없는 유리한 기능 스레드 풀링 활용법이 있다 스레드 몇 개가 골고루 분담해서 하는 스레드 풀을 쉽게 구현 가능. 반면, epoll은 쉽지 않다.
epoll
: 소켓이 I/O 가능 상태가 되면 이를 감지해서 사용자에게 알림을 해주는 역할을 한다
어떤 소켓이 I/O 가능 상태인지 알려준다
논블록 소켓을 대량으로 갖고 있을 때 효율적으로 처리해 주는 API이다.
레벨 트리거
: 소켓이 I/O 가능하다를 의미한다
에지 트리거
: 소켓이 I/O 가능이 아니었는데, 이제 I/O 가능이 되었다를 의미한다
에지 트리거를 사용할 때
I/O 호출을 한 번만 하지 말고 would block이 발생할 때까지 반복해야 한다
소켓은 논블록으로 미리 설정되어 있어야 한다
IOCP(I/O Completion Port)
: 소켓의 Overlapped I/O가 완료되면 이를 감지해서 사용자에게 알려주는 역할을 한다
epoll과 IOCP의 차이점
epoll은 I/O 가능인 것을 알려 주지만, IOCP는 I/O 완료인 것을 알려 준다
어떤 소켓에 대해 Overlapped I/O를 하지 않는 이상 그 소켓에 대한 완료 신호는 전혀 발생하지 않는다
->소켓 하나에 대한 완료 신호를 스레드 하나만 처리할 수 있게 보장된다
IOCP 하나를 여러 스레드가 기다리도록 구현하기가 쉽다
많은 소켓에 대한 I/O 처리를 동시다발적으로 수행할 때, 여러 스레드가 완료 신호 처리를 골고루 나누어서 처리할 수 있다
IOCP epoll
블로킹을 없애는 수단 Overlapped I/O 논블록 소켓
블로킹 없는 처리 순서 1. Overlapped I/O를 건다 1. I/O 이벤트를 꺼낸다
2. 완료 신호를 꺼낸다 2. 꺼낸 이벤트에 대응하는 소켓에 대한
3. 완료 신호에 대한 나머지 처리를 논블록 I/O를 실행한다
한다
4. 끝나고 나서 다시 Overlapped I/O
를 건다
지원 플랫폼 윈도에서만 사용 가능 리눅스, 안드로이드에서 사용 가능
epoll을 쓰는 리눅스에서는 TCP 소켓으로 수신을 한 후에 데이터 수신을 하려면 소켓 수신 함수(recv)를 이어서 호출해 주어야 한다
IOCP를 쓰는 윈도 서버에서는 연결 받기와 수신을 소켓 함수 호출 한 번으로 끝낼 수 있다
'공부 > Game Server' 카테고리의 다른 글
Proactor / Reactor (0) | 2023.04.24 |
---|---|
Condition Variable (0) | 2023.04.22 |
TLS(Thread local storage) (0) | 2023.04.19 |
스핀락(Spin lock), 뮤텍스(Mutex), 세마포어(Semaphore) (1) | 2023.04.18 |
스레드의 동기화 기법 - 유저 모드 동기화/커널 모드 동기화 (0) | 2023.04.18 |