다음을 통해 공유


Atom 테이블 정보

원자 테이블은 문자열 및 해당 식별자를 저장하는 시스템 정의 테이블입니다. 애플리케이션은 문자열을 원자 테이블에 배치하고 문자열에 액세스하는 데 사용할 수 있는 atom이라는 16비트 정수( atom)를 받습니다. 원자 테이블에 배치된 문자열을 원 자 이름이라고 합니다.

시스템은 여러 원자 테이블을 제공합니다. 각 원자 테이블은 다른 용도로 사용됩니다. 예를 들어 DDE(동적 데이터 교환) 애플리케이션은 전역 atom 테이블을 사용하여 항목 이름 및 토픽 이름 문자열을 다른 애플리케이션과 공유합니다. DDE 애플리케이션은 실제 문자열을 전달하는 대신 파트너 애플리케이션에 전역 원자를 전달합니다. 파트너는 원자를 사용하여 원자 테이블에서 문자열을 가져옵니다.

애플리케이션은 로컬 원자 테이블을 사용하여 고유한 항목 이름 연결을 저장할 수 있습니다.

시스템은 애플리케이션에서 직접 액세스할 수 없는 원자 테이블을 사용합니다. 그러나 애플리케이션은 다양한 함수를 호출할 때 이러한 원자를 사용합니다. 예를 들어 등록된 클립보드 형식은 시스템에서 사용하는 내부 원자 테이블에 저장됩니다. 애플리케이션은 RegisterClipboardFormat 함수를 사용하여 이 원자 테이블에 원자를 추가합니다. 또한 등록된 클래스는 시스템에서 사용하는 내부 원자 테이블에 저장됩니다. 애플리케이션은 RegisterClass 또는 RegisterClassEx 함수를 사용하여 이 원자 테이블에 원 자를 추가합니다.

이 섹션에서 설명하는 항목은 다음과 같습니다.

전역 Atom 테이블

전역 원자 테이블은 모든 애플리케이션에서 사용할 수 있습니다. 애플리케이션이 전역 원자 테이블에 문자열을 배치하면 시스템은 시스템 전체에서 고유한 원자를 생성합니다. 원자가 있는 모든 애플리케이션은 전역 원자 테이블을 쿼리하여 식별되는 문자열을 가져올 수 있습니다.

다른 애플리케이션과 데이터를 공유하기 위한 프라이빗 DDE 데이터 형식을 정의하는 애플리케이션은 형식 이름을 전역 원자 테이블에 배치해야 합니다. 이 기술은 시스템 또는 다른 애플리케이션에서 정의한 형식의 이름과 충돌을 방지하고 다른 애플리케이션에서 사용할 수 있는 메시지 또는 형식에 대한 식별자(원자)를 만듭니다.

사용자 Atom 테이블

전역 원자 테이블 외에도 사용자 원자 테이블은 모든 프로세스에서 공유되는 또 다른 시스템 원자 테이블입니다. 사용자 원자 테이블은 win32k 내부 시나리오의 적은 수에 사용됩니다. 예를 들어 windows 모듈 이름, win32k의 잘 알려진 문자열, OLE 형식 등이 있습니다. 애플리케이션은 사용자 원자 테이블과 직접 상호 작용하지 않지만 RegisterClass, RegisterWindowMessageRegisterClipboardFormat과 같은 여러 API를 호출하여 사용자 원자 테이블에 항목을 추가합니다. 에 의해 추가된 RegisterClass 항목은 에서 삭제 UnregisterClass할 수 있습니다. 그러나 및 RegisterClipboardFormat 에 의해 추가된 RegisterWindowMessage 항목은 세션이 끝날 때까지 삭제되지 않습니다. 사용자 원자 테이블에 더 이상 공간이 없고 전달되는 문자열이 테이블에 없는 경우 호출이 실패합니다.

Atom 테이블 크기

CreateWindow를 비롯한 많은 중요한 API는 사용자 원자를 사용합니다. 따라서 사용자 원자 테이블의 공간 소모로 인해 심각한 문제가 발생합니다. 예를 들어 모든 애플리케이션이 시작되지 않을 수 있습니다. 다음은 애플리케이션이 원자 테이블을 효율적으로 활용하고 애플리케이션 및 시스템의 안정성과 성능을 유지하기 위한 몇 가지 권장 사항입니다.

  1. 앱의 사용자 원자 테이블 사용을 제한해야 합니다. , RegisterWindowMessage또는 RegisterClipboardFormat 와 같은 RegisterClassAPI를 사용하여 고유 문자열을 저장하면 다른 앱에서 문자열을 사용하여 창 클래스를 등록하는 데 전역적으로 사용되는 사용자 원자 테이블의 공간이 사용됩니다. 가능한 경우 AddAtomDeleteAtom/을 사용하여 로컬 원자 테이블에 문자열을 저장하거나, 원자가 교차 프로세스에 필요한 경우 GlobalAddAtom/GlobalDeleteAtom을 저장해야 합니다.

  2. 사용자 원자 테이블 문제를 일으키는 애플리케이션에 대한 우려가 있는 경우 커널 디버거를 연결하고 (bae1 win32kbase!UserAddAtomEx /p <eprocess> "kc10;g")에 대한 호출 UserAddAtomEx 시 프로세스에 침입하여 근본 원인을 조사할 수 있습니다. user32! 호출 스택에서 어떤 API가 호출되는지 확인합니다. 이 방법론은 전역 원자 테이블 누수 식별에 설명된 전역 원자 테이블 문제 검색과 유사합니다. 사용자 원자 테이블의 내용을 덤프하는 또 다른 방법은 가능한 원자 범위에서 getClipboardFormatName 을 호출하여 0xC000 0xFFFF. 애플리케이션이 실행되는 동안 총 원자 수가 꾸준히 올라가거나 앱이 닫혔을 때 기준선으로 돌아가지 않으면 문제가 있습니다.

로컬 Atom 테이블

애플리케이션은 로컬 원자 테이블을 사용하여 애플리케이션 내에서만 사용되는 많은 수의 문자열을 효율적으로 관리할 수 있습니다. 이러한 문자열 및 연결된 원자는 테이블을 만든 애플리케이션에서만 사용할 수 있습니다.

여러 구조체에서 동일한 문자열이 필요한 애플리케이션은 로컬 원자 테이블을 사용하여 메모리 사용량을 줄일 수 있습니다. 애플리케이션은 문자열을 각 구조체에 복사하는 대신 문자열을 원자 테이블에 배치하고 결과 원자를 구조체에 포함할 수 있습니다. 이러한 방식으로 문자열은 메모리에 한 번만 표시되지만 애플리케이션에서 여러 번 사용할 수 있습니다.

또한 애플리케이션은 로컬 원자 테이블을 사용하여 특정 문자열을 검색할 때 시간을 절약할 수 있습니다. 검색을 수행하려면 애플리케이션에서 검색 문자열만 원자 테이블에 배치하고 결과 원자를 관련 구조의 원자와 비교하면 됩니다. 원자를 비교하는 것은 일반적으로 문자열을 비교하는 것보다 빠릅니다.

Atom 테이블은 해시 테이블로 구현됩니다. 기본적으로 로컬 원자 테이블은 해시 테이블에 37개의 버킷을 사용합니다. 그러나 InitAtomTable 함수를 호출하여 사용하는 버킷 수를 변경할 수 있습니다. 그러나 애플리케이션이 InitAtomTable을 호출하는 경우 다른 atom-management 함수를 호출하기 전에 호출해야 합니다.

원자 형식

애플리케이션은 문자열 원자와 정수 원자라는 두 가지 유형의 원자를 만들 수 있습니다. 정수 원자와 문자열 원자의 값은 겹치지 않으므로 두 유형의 원자를 동일한 코드 블록에서 사용할 수 있습니다.

여러 함수는 문자열 또는 원자를 매개 변수로 허용합니다. 이러한 함수에 원자를 전달할 때 애플리케이션은 MAKEINTATOM 매크로를 사용하여 원자를 함수에서 사용할 수 있는 형식으로 변환할 수 있습니다.

다음 섹션에서는 원자 형식에 대해 설명합니다.

문자열 원자

애플리케이션이 Null로 종료된 문자열을 GlobalAddAtom, AddAtom, GlobalFindAtomFindAtom 함수에 전달하면 그 대가로 문자열 원자 (16비트 정수)를 받습니다. 문자열 원자에는 다음과 같은 속성이 있습니다.

  • 문자열 원자의 값은 0xFFFF 0xC000 범위(MAXINTATOM)에 있습니다.
  • 대/소문자는 원자 테이블의 원자 이름을 검색하는 데 중요하지 않습니다. 또한 전체 문자열은 검색 작업에서 일치해야 합니다. 부분 문자열 일치가 수행되지 않습니다.
  • 문자열 원자와 연결된 문자열의 크기는 255바이트를 초과할 수 없습니다. 이 제한은 모든 원자 함수에 적용됩니다.
  • 참조 수는 각 원자 이름과 연결됩니다. 원자 이름이 테이블에 추가될 때마다 개수가 증가하며 원자 이름이 삭제될 때마다 감소합니다. 이렇게 하면 동일한 문자열 원자의 다른 사용자가 서로의 원자 이름을 삭제할 수 없습니다. 원자 이름에 대한 참조 수가 0이면 시스템에서 원자와 원자 이름을 테이블에서 제거합니다.

정수 원자

정수 원자는 다음과 같은 방법으로 문자열 원자와 다릅니다.

  • 정수 원자의 값은 0xBFFF(MAXINTATOM – 1)를 통해 0x0001 범위에 있습니다.
  • 정수 원자의 문자열 표현은 #dddd입니다. 여기서 dddd 로 표시되는 값은 10진수입니다. 선행 0은 무시됩니다.
  • 정수 원자와 연결된 참조 수 또는 스토리지 오버헤드는 없습니다.

원자 만들기 및 사용량 수

애플리케이션은 AddAtom 함수를 호출하여 로컬 원자를 만듭니다. GlobalAddAtom 함수를 호출하여 전역 원자를 만듭니다. 두 함수 모두 문자열에 대한 포인터가 필요합니다. 시스템은 문자열에 대한 적절한 원자 테이블을 검색하고 해당 원자를 애플리케이션에 반환합니다. 문자열 원자의 경우 문자열이 이미 atom 테이블에 있는 경우 시스템은 이 프로세스 중에 문자열에 대한 참조 수를 증분합니다.

동일한 원자 이름을 추가하기 위한 반복 호출은 동일한 원자를 반환합니다. AddAtom을 호출할 때 테이블에 원자 이름이 없으면 원자 이름이 테이블에 추가되고 새 원자가 반환됩니다. 문자열 원자인 경우 해당 참조 개수도 1로 설정됩니다.

애플리케이션은 로컬 원자를 더 이상 사용할 필요가 없는 경우 DeleteAtom 함수를 호출해야 합니다. 전역 원자가 더 이상 필요하지 않은 경우 GlobalDeleteAtom 함수를 호출해야 합니다. 문자열 원자의 경우 이러한 함수 중 하나가 해당 원자의 참조 수를 1씩 줄입니다. 참조 수가 0에 도달하면 시스템에서 테이블에서 원자 이름을 삭제합니다.

문자열 원자의 원자 이름은 참조 수가 0보다 크면 테이블에 배치한 애플리케이션이 종료된 후에도 전역 원자 테이블에 남아 있습니다. 로컬 원자 테이블은 테이블의 원자의 참조 수에 관계없이 연결된 애플리케이션이 종료될 때 제거됩니다.

Atom-Table 쿼리

애플리케이션은 FindAtom 또는 GlobalFindAtom 함수를 사용하여 특정 문자열이 이미 원자 테이블에 있는지 여부를 확인할 수 있습니다. 이러한 함수는 지정된 문자열에 대한 원자 테이블을 검색하고 문자열이 있는 경우 해당 원자를 반환합니다.

애플리케이션에 검색되는 문자열에 해당하는 원자가 있는 경우 애플리케이션은 GetAtomName 또는 GlobalGetAtomName 함수를 사용하여 atom 테이블에서 atom-name 문자열을 검색할 수 있습니다. 두 함수 모두 지정된 원자의 atom-name 문자열을 버퍼에 복사하고 복사된 문자열의 길이를 반환합니다. GetAtomName 은 로컬 atom 테이블에서 atom-name 문자열을 검색하고 GlobalGetAtomName 은 전역 atom 테이블에서 atom-name 문자열을 검색합니다.

Atom 문자열 형식

AddAtom, GlobalAddAtom, FindAtomGlobalFindAtom 함수는 null로 끝나는 문자열에 대한 포인터를 사용합니다. 애플리케이션은 다음 방법 중 하나로 이 포인터를 지정할 수 있습니다.

문자열 형식 설명
# Dddd 10진수 문자열로 지정된 정수입니다. 정수 원자를 만들거나 찾는 데 사용됩니다.
문자열 원자 이름 문자열 원자 이름입니다. 문자열 원자 이름을 원자 테이블에 추가하고 그 대가로 원자를 받는 데 사용됩니다.