중요한 것은, 스레드는 계속 생성된다는 거...

@codemaru · March 16, 2009 · 3 min read

얼마 전에 버그를 수정하다 신기한 현상을 하나 발견했습니다. 물론 지금까지 저만 멍청하게 생각하고 있었던 건지도 모르겠습니다. ㅋㅋ~ NtQuerySystemInformation이란 함수와 관련된 건데요. 아마 다른 함수들에도 적용이 될 것 같습니다. NtQuerySystemInformation 함수는 시스템 정보를 구해오는 함수입니다. 보통 프로세스 목록이라든지, 스레드 목록, 드라이버 목록 등과 같은 정보를 얻을 때 사용하는 함수죠. 이러한 종류의 윈도우 API를 사용할 때에는 한 가지 원칙이 있습니다. 바로 버퍼 크기를 먼저 구하고 할당한 다음 사용하라는 것이죠. 프로세스나 스레드가 몇 개가 떠 있는지 알 수 없기 때문입니다. 통상적으로 아래와 같은 패턴으로 만들게 되죠.

DWORD required, size = 0;  
PVOID buffer = NULL;  
NtQuerySystemInformation(buffer, size, &required);  
  
size = required;  
buffer = new BYTE[size];  
NtQuerySystemInformation(buffer, size, &required);

물론 NtQuerySystemInformation 함수의 인자가 저렇게 단순하진 않습니다. 예시는 핵심만 보여주기 위해서 버퍼, 버퍼 크기, 필요한 크기로 인자를 가정하고 표현해 본 것이죠. 거의 대부분의 경우에 위 코드는 정상적으로 동작합니다. 하지만 특정 경우에 두 번째 호출이 실패하는 경우가 생깁니다. 물론 다른 이유도 아니 버퍼 크기가 부족하다는 이유로 말이죠.

왜 그럴까요? 이유는 간단합니다. 처음 NTQSI를 호출한 시점과 그 다음 NTQSI를 호출한 시점의 시스템 정보가 변경되었기 때문입니다. 예를들어 스레드 목록을 구한다고 한다면, 그 사이에 스레드가 추가적으로 생성된 경우입니다. 어쨌든 저런 식의 코드는 아주 정상적인 환경에서도 실패할 가능성을 가지고 있는 셈이죠.

이런 결함을 없애기 위해서는 결국은 루프를 돌아야 합니다. 정상적일 때까지 말이죠. 물론 가져온 버퍼 크기보다 조금 더 할당하는 꼼수를 사용한다면 루프 회수를 줄일 수는 있겠죠. 같은 원리로 미리 할당된 버퍼를 사용한다면 함수 호출 회수를 더 많이 줄일 수 있습니다.

@codemaru
돌아보니 좋은 날도 있었고, 나쁜 날도 있었다. 그런 나의 모든 소소한 일상과 배움을 기록한다. 여기에 기록된 모든 내용은 한 개인의 관점이고 의견이다. 내가 속한 조직과는 1도 상관이 없다.
(C) 2001 YoungJin Shin, 0일째 운영 중