동적으로 링크 된 공유 라이브러리의 전역 및 정적 변수는 어떻게됩니까?


127

전역 및 정적 변수가있는 모듈이 응용 프로그램에 동적으로 연결될 때 어떤 일이 발생하는지 이해하려고합니다. 모듈이란 솔루션의 각 프로젝트를 의미합니다 (저는 Visual Studio에서 많이 작업합니다!). 이러한 모듈은 * .lib 또는 * .dll 또는 * .exe 자체에 내장되어 있습니다.

나는 응용 프로그램의 바이너리가 데이터 세그먼트에있는 모든 개별 번역 단위 (객체 파일)의 전역 및 정적 데이터를 포함하고 있음을 이해합니다 (그리고 const 인 경우 데이터 세그먼트 만 읽음).

  • 이 응용 프로그램이로드 시간 동적 링크와 함께 모듈 A를 사용하면 어떻게됩니까? DLL에 전역 및 통계에 대한 섹션이 있다고 가정합니다. 운영 체제에서로드합니까? 그렇다면 어디로로드됩니까?

  • 그리고 응용 프로그램이 런타임 동적 연결이있는 모듈 B를 사용하면 어떻게됩니까?

  • 응용 프로그램에 A와 B를 모두 사용하는 두 개의 모듈이있는 경우 A와 B의 전역 복사본이 아래와 같이 생성됩니까 (다른 프로세스 인 경우)?

  • DLL A와 B가 응용 프로그램 전역에 액세스 할 수 있습니까?

(이유도 기재 해주세요)

MSDN 에서 인용 :

DLL 소스 코드 파일에서 전역으로 선언 된 변수는 컴파일러와 링커에서 전역 변수로 처리되지만 지정된 DLL을로드하는 각 프로세스는 해당 DLL 전역 변수의 자체 복사본을 가져옵니다. 정적 변수의 범위는 정적 변수가 선언 된 블록으로 제한됩니다. 결과적으로 각 프로세스에는 기본적으로 DLL 전역 및 정적 변수의 자체 인스턴스가 있습니다.

그리고 여기에서 :

모듈을 동적으로 연결할 때 서로 다른 라이브러리에 자체 전역 인스턴스가 있는지 또는 전역이 공유되는지 여부가 명확하지 않을 수 있습니다.

감사.


3
으로 모듈 당신은 아마 평균 libs가 . 모듈이 무엇인지에 대한보다 정확한 정의와 현재의 일반 라이브러리보다 다른 의미를 가진 모듈 을 C ++ 표준 에 추가하는 제안이 있습니다 .
데이비드 로드리게스는 - dribeas

아, 분명히 했어야 했어. 솔루션의 여러 프로젝트 (비주얼 스튜디오에서 많이 작업)를 모듈로 간주합니다. 이러한 모듈은 * .lib 또는 * .dll에 내장되어 있습니다.
라자

3
@ DavidRodríguez-dribeas "모듈"이라는 용어는 실행 가능한 프로그램, 동적 연결 라이브러리 (.dll) 또는 공유 객체 (.so)를 포함하여 독립 실행 형 (완전히 연결된) 실행 파일에 대한 올바른 기술 용어입니다. 여기에서 완벽하게 적절하며 그 의미는 정확하고 잘 이해됩니다. "모듈"이라는 표준 기능이있을 때까지 그 정의는 내가 설명했듯이 기존 기능으로 남아 있습니다.
Mikael Persson 2013 년

답변:


176

이것은 Windows와 Unix 계열 시스템 간의 매우 유명한 차이점입니다.

무슨 일이 있어도:

  • 프로세스 에는 고유 한 주소 공간이 있습니다. 즉, 프로세스 간 통신 라이브러리 또는 확장을 사용하지 않는 한 프로세스간에 공유되는 메모리가 없습니다.
  • 하나의 정의 규칙 (ODR)는 여전히 만 링크시 (정적 또는 동적 링크)에서 볼 수 전역 변수 중 하나 정의를 가질 수 있다는 것을 의미 적용됩니다.

따라서 여기서 핵심 문제는 가시성입니다. 입니다.

모든 경우에, static 전역 변수 (또는 함수)는 모듈 (dll / so 또는 실행 파일) 외부에서 볼 수 없습니다. C ++ 표준에서는 이러한 파일에 내부 연결이 있어야합니다. 즉, 정의 된 변환 단위 (개체 파일이 됨) 외부에서는 볼 수 없습니다. 그래서 그 문제가 해결됩니다.

복잡 해지는 곳은 extern전역 변수 가있을 때 입니다. 여기서 Windows와 Unix 계열 시스템은 완전히 다릅니다.

Windows (.exe 및 .dll)의 경우 extern전역 변수는 내 보낸 기호의 일부가 아닙니다. 즉, 다른 모듈은 다른 모듈에 정의 된 전역 변수를 인식하지 못합니다. 예를 들어 externDLL에 정의 된 변수 를 사용하는 실행 파일을 만들려고하면 링커 오류가 발생합니다 . 이는 허용되지 않기 때문입니다. 해당 extern 변수의 정의와 함께 개체 파일 (또는 정적 라이브러리)을 제공하고이를 실행 파일과 DLL 모두에 정적으로 연결 하여 두 개의 개별 전역 변수 (하나는 실행 파일에 속하고 다른 하나는 DLL에 속함)를 생성해야합니다. ).

Windows에서 실제로 전역 변수를 내보내려면 함수 내보내기 / 가져 오기 구문과 유사한 구문을 사용해야합니다.

#ifdef COMPILING_THE_DLL
#define MY_DLL_EXPORT extern "C" __declspec(dllexport)
#else
#define MY_DLL_EXPORT extern "C" __declspec(dllimport)
#endif

MY_DLL_EXPORT int my_global;

그렇게하면 전역 변수가 내 보낸 심볼 목록에 추가되고 다른 모든 기능과 마찬가지로 링크 될 수 있습니다.

Unix와 유사한 환경 (Linux와 같은)의 경우 확장이있는 "공유 객체"라고하는 동적 라이브러리는 .so모든 extern전역 변수 (또는 함수)를 내 보냅니다 . 이 경우 어디에서나 공유 객체 파일로 로드 타임 링크 를 수행 하면 전역 변수가 공유됩니다. 즉, 하나로 함께 링크됩니다. 기본적으로 유닉스 계열 시스템은 정적 라이브러리와 동적 라이브러리와의 링크 사이에 사실상 차이가 없도록 설계되었습니다. 다시 말하지만, ODR은 보드 전체에 적용됩니다. extern전역 변수는 모듈간에 공유됩니다. 즉,로드 된 모든 모듈에 대해 하나의 정의 만 있어야합니다.

마지막으로 두 경우 모두 Windows 또는 Unix 계열 시스템의 경우 / / 또는 / / 중 하나를 사용하여 동적 라이브러리의 런타임 링크를 수행 할 수 있습니다 . 이 경우 사용하려는 각 기호에 대한 포인터를 수동으로 가져와야하며 여기에는 사용하려는 전역 변수가 포함됩니다. 전역 변수의 경우 전역 변수가 내 보낸 기호 목록의 일부인 경우 (이전 단락의 규칙에 따라) 함수에 대해 수행하는 것과 동일 하거나 사용할 수 있습니다 .LoadLibrary()GetProcAddress()FreeLibrary()dlopen()dlsym()dlclose()GetProcAddress()dlsym()

그리고 물론 필요한 마지막 참고 사항으로 전역 변수는 피해야 합니다. 그리고 당신이 인용 한 텍스트 ( "불분명 한 것"에 대해)는 방금 설명한 플랫폼 별 차이점을 정확히 언급한다고 믿습니다 (동적 라이브러리는 실제로 C ++ 표준에 의해 정의되지 않습니다. 이것은 플랫폼 별 영역입니다. 훨씬 덜 신뢰성 / 휴대 성).


5
좋은 대답, 감사합니다! 후속 조치가 있습니다. DLL은 자체 포함 된 코드 및 데이터이므로 실행 파일과 유사한 데이터 세그먼트 섹션이 있습니까? 공유 라이브러리를 사용할 때이 데이터가로드되는 위치와 방법을 이해하려고합니다.
Raja

18
@Raja 예, DLL에는 데이터 세그먼트가 있습니다. 사실, 파일 자체의 관점에서 실행 파일과 DLL은 사실상 동일합니다. 유일한 차이점은 실행 파일에 "주"기능이 포함되어 있다는 플래그가 설정되어 있다는 것입니다. 프로세스가 DLL을로드하면 데이터 세그먼트가 프로세스의 주소 공간 어딘가에 복사되고 정적 초기화 코드 (중요하지 않은 전역 변수를 초기화 함)도 프로세스의 주소 공간 내에서 실행됩니다. 로드는 프로세스 주소 공간이 새로 생성되는 대신 확장된다는 점을 제외하면 실행 파일과 동일합니다.
Mikael Persson

4
클래스의 인라인 함수 내에 정의 된 정적 변수는 어떻습니까? 예를 들어 헤더 파일에 "class A {void foo () {static int st_var = 0;}}"를 정의하고 모듈 A와 모듈 B에 포함시킵니다. A / B가 동일한 st_var를 공유합니까 아니면 각각 자체 복사본을 갖게됩니까?
camino

2
@camino 클래스를 내 보내면 (예 :으로 정의 됨 __attribute__((visibility("default")))) A / B는 동일한 st_var를 공유합니다. 그러나 클래스가로 정의 된 경우 __attribute__((visibility("hidden")))모듈 A와 모듈 B는 공유되지 않고 자체 복사본을 갖게됩니다.
웨이 구오

1
@camino __declspec (dllexport)
ruipacheco
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.