#sockets #resource-leak
#сокеты #утечка ресурсов
Вопрос:
Это действительно озадачивает меня, вот трассировка стека, которая показывает, что вызов shutdown в моем сокете фактически привел к созданию другого дескриптора для потока, что вызывает утечку дескриптора.
0x00007ffe0b560064: ntdll!NtCreateEvent 0x0000000000000014
0x00007ffe06cb5ab6: mswsock!MSAFD_SockThreadInitialize 0x000000000000002e
0x00007ffe06cb5a60: mswsock!SockEnterApiSlow 0x000000000000003c
0x00007ffe06cbc591: mswsock!WSPShutdown 0x0000000000000221
0x00007ffe0a3318f2: WS2_32!shutdown 0x0000000000000072
... some other function calls
0x00007ff7cdf7479c: X!thread_start<unsigned int (__cdecl*)(void * __ptr64)> 0x0000000000000050
0x00007ffe0b017974: KERNEL32!BaseThreadInitThunk 0x0000000000000014
0x00007ffe0b52a271: ntdll!RtlUserThreadStart 0x0000000000000021
Вызов — это простое завершение работы (sock, 1); но почему создается новый дескриптор?
Я не вызываю shutdown дважды в сокете, но определенно чего-то не хватает, не так ли?
Комментарии:
1. Вы уверены, что был создан новый поток? В трассировке стека я вижу, что ваш поток вызывает
shutdown()
, и в конечном итоге это вызываетMSAFD_SockThreadInitialize()
, но неясно, чтоMSAFD_SockThreadInitialize()
создает поток (несмотря на его имя)2. Ах да, это событие для потока, но какого потока? и почему он это делает? Это вывод !htrace, который показывает, что дескриптор на данный момент был создан и оставлен открытым. Я понимаю, что это не фактический исходный код, но чтобы получить приблизительное представление: github.com/pustladi/Windows-2000/blob/master/private/net /…
3. Я предполагаю, что вызывающий поток. Я надеюсь, что вы вызываете
closesocket()
сокет послеshutdown()
? Если нет, то вы пропускаете сокет, и утечка дескриптора может быть частью этого.4. Спасибо за вашу помощь @JeremyFriesner, это вызов, который я мог подтвердить, что выполняется закрытие сокета: iRC = shutdown(sock, 1) ; iRC = select(1, amp;readFds, 0, 0, amp;timeVal) ; iRC = closesocket(sock) ;
5. @AmirDashti согласно коду в этой ссылке
SockEnterApiSlow()
возвращает указатель на локальные данные потока, вызываяSockThreadInitialize()
только в том случае, если эти данные TLS еще не были инициализированы для вызывающего потока. И эта инициализация включает в себя создание нового объекта события, который используется для сигнализации завершения ввода-вывода сокета, выполняемого вызывающим потоком. Итак, само собой разумеется, чтоshutdown()
он пытается выполнить ввод-вывод, используя эти данные TLS, поэтому он и вызываетSockEnterApiSlow()
. Когда вызывающий поток завершает работу и очищает свои данные TLS, этот объект события также должен быть освобожден.