IOCP 서버에서 쓰레드 풀의 관리를 위해 PostThreadMessage를 사용하고 있는데


PostThreadMessage에서 첫번째 인자인 ThreadID를 나는 현재 자신이 돌아가고 있는 Thread의 ID를 말하는 것인줄 알고


GetCurrentThreadID를 넣어주고 있었다..


그런데 그게아니라 내가 Message를 넘겨줄 스레드의 아이디를 적어줘야 하는 것이였다.

ZeroByteRecv 와 PageLocking 을 관련해서 이해가 안가서 인터넷을 뒤적뒤적 거렸다.

 

 

내가 이해한 바로는 

 

우리가 처음 관리하고 있는 소켓 풀들에 대하여 WSARecv 로 비동기 대기를 하게 되는데

 

이상태에서

 

WSABUF wsaBuf;

wsaBuf.buf = buf;

wsaBuf.len = 100;

 

WSARecv(sock, &wsabuf, len ,...);

 

해서 소켓 풀들에 대해서 메모리를 잡고 있는 상태가 된다.

 

하지만 많은 소켓들에 대해서 아직 데이터를 받은 상태가 아닌데 이렇게 버퍼를 잡고 있으면

 

Page에 대해 메모리를 잡고 있게 되며 그 영역에 대해 Lock을 건다. 

 

쓰지 않는 메모리를 잡고 있다가 나중에 운영체제 상에서 다른 작업들에 의해 page교환이 이루어지고

 

page교환이 이루어 진 뒤에 WSARecv 로 socket 들로 부터 작업을 받는 다면

 

page교환하는데 작업상 능률 저하 또는 오류가 발생 할 수 있다는 것이다.

 

 

때문에

 

WSABuf wsaBuf;

wsaBuf.buf = nullptr;

wsaBuf.len = 0;

 

으로 설정해서 WSARecv 를 하게 된다면 메모리를 잡지 않고서도 다른 소켓들에 대해 작업을 받아들이는 것을 할 수 있다는 것이다.

 

즉 zeroByte Recv 로 작업을 들어온 것을 알게되면 그 때 다시 적당한 크기의 버퍼로 WSARecv 를 호출해서 데이터를 읽어 들이는 것이다.

(0 Byte 로 설정해놨을때 데이터가 들어온다고해서 WSABUf와 연결된 버퍼의 데이터가 사라지는 것은 아니다.)

 

 

이렇게 ZeroByte 기법을 쓰고자 적용할때 문제점은 0바이트로 들어올떄의 처리이다.

 

다른 블로그글을 찾아봤는데 클라이언트에서 closesocket을 하거나 shutdown을 하면 GQCS에서는 오류가 안나고 0 바이트가 들어온 것으로 인식이 되기 때문에 이것을 잘 구분 해서 처리해 줘야 된다고 작성해놨었는데..

 

로즈서버로 돌려봤을때 0바이트로 받아지는 경우는 애초에 처음 AcceptEx로 받았을때 이외엔 나오지 않았다 (closesocket으로 했을때 0 바이트가 나오지 않음.)

 

AcceptEx할때 accept 와 동시에 바이트를 받을 수 있는데 그 바이트 설정을 0 으로 해놔서 그런것 같다.

 

CRITICAL_SECTION 은 구조체로 선언되어 있는데. 

CRITICLA_SECTION 을 동적 할당하고 그것을 다른 쓰레드에서 delete 했을때 


자동으로 LeaveCriticalSection 이 호출 될까..


사실 소멸자같은게 없으니까 당연한 거였을지도 모르겠다..


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <iostream>
#include <set>
#include <vector>
#include <map>
#include <Windows.h>
 
#include <thread>
using namespace std;
 
 
DWORD WINAPI Test(LPVOID pParam) {
 
    CRITICAL_SECTION* tempCritical = (CRITICAL_SECTION*)pParam;
 
    if (tempCritical) {
        delete tempCritical;
        tempCritical = NULL;
    }
 
    return 0;
}
 
DWORD WINAPI Test2(LPVOID pParam) {
    CRITICAL_SECTION* tempCritical = (CRITICAL_SECTION*)pParam;
    EnterCriticalSection(tempCritical);
    cout << "Critical Section Lock Count : " << tempCritical->LockCount << endl;
 
    return 0;
}
int main() {
 
    CRITICAL_SECTION* cs = new CRITICAL_SECTION;
    InitializeCriticalSection(cs);
    EnterCriticalSection(cs);
    thread test(Test, (LPVOID)cs);
    
    test.join();
 
    cout << "Critical Section Lock Count : " << cs->LockCount << endl;
 
    thread test2(Test2, (LPVOID)cs);
 
    test2.join();
 
 
 
    
    return 0;
}
cs


원래 EnterCriticalSection 을 한번 걸게 되면 초기 상태는 -1 인데 -1 -> -2 로 이루어진다.

LeaverCriticalSection 은 -2 -> -1 로 가게된다.

그렇다고 해서 EnterCri ~ 또는 LeaveCri ~ 를 많이 한다고해서 -2 , -3 , -4  or 0 , 1, 2 숫자가 계속 올라가는 것은 아니였음.

그런데 삭제하고 나니 쓰레기 값이 도출 됬고 EnterCritical을 호출하니 Lock 이 걸리지 않고 넘어갔고 쓰레기값에서 -1 을 한 값이 도출됬다.


CRITICAL_SECTION 의 구조체변수 LockCount 와 RecursiveCount 에 대한 실험 결과 ->


LockCount 는 Initialize 를 하면 -1

현재 스레드에서 EnterCriticalSection 을 하면 -2

이 상태에서 LeaveCriticalSection 을 하지 않고 다른 스레드에서 EnterCriticalSection 시 -6

한번 더 반복시 -10

한번 더 반복시 -14 


즉 다른 스레드에서 EnterCriticalSection을 호출시 -4 씩 증가 하게 된다.

하지만 CRITICAL_SECTION을 호출한 스레드에서 한번더 EnterCriticalSection을 호출한다고 해서 -4 가 증가하진 않았다.


하지만 RecursionCount 가 1 증가하게 됬다.



+ Recent posts