2013-10-15 2 views
74

전역 변수와 정적 변수가있는 모듈이 응용 프로그램에 동적으로 연결될 때 어떤 일이 발생하는지 이해하려고합니다. 모듈이란 솔루션의 각 프로젝트를 의미합니다 (저는 Visual Studio와 함께 많은 일을합니다!). 이러한 모듈은 * .lib 또는 * .dll 또는 * .exe 자체에 내장되어 있습니다.동적 라이브러리가 링크 될 때 공유 라이브러리의 전역 변수와 정적 변수는 어떻게됩니까?

응용 프로그램의 바이너리는 데이터 세그먼트의 모든 개별 번역 단위 (개체 파일)의 전역 및 정적 데이터를 포함한다는 것을 알고 있습니다 (const 인 경우 읽기 전용 데이터 세그먼트).

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

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

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

  • DLL A와 B는 응용 프로그램 전역에 액세스합니까?

MSDN에서 인용 (당신의 이유뿐만 아니라하시오) : 컴파일러에 의해 전역 변수로 취급되는 DLL 소스 코드 파일에서 글로벌로 선언 된

변수와 링커를 사용하지만 주어진 DLL을로드하는 각 프로세스는 해당 DLL의 전역 변수 사본을 가져옵니다. 정적 변수의 범위는 정적 변수가 선언 된 블록으로 제한됩니다. 결과적으로 각 프로세스는 기본적으로 DLL 전역 변수와 정적 변수의 자체 인스턴스를 갖습니다.

here에서

:

모듈을 동적으로 링크 할 때, 그것은 다른 라이브러리 전역의 자신의 인스턴스를 가지고 있는지 또는 전역가 공유 불분명 할 수 있습니다.

감사합니다.

+0

* 모듈 별 * 아마도 * libs *를 의미합니다.C++ 표준에 * modules *을 추가하는 제안이 있습니다. 모듈은 모듈의 정의와 현재의 정규 라이브러리와 다른 의미를 정의합니다. –

+0

아, 그건 분명히 했어야합니다. 나는 다른 프로젝트를 솔루션으로 생각한다. (나는 Visual Studio와 함께 많은 일을한다.) 모듈로 생각한다. 이러한 모듈은 * .lib 또는 * .dll에 내장되어 있습니다. – Raja

+0

@ DavidRodríguez-dribeas "모듈"이라는 용어는 실행 가능 프로그램, 동적 링크 라이브러리 (.dll) 또는 공유 개체 (.so)를 포함하여 독립 실행 형 (완전히 링크 된) 실행 파일에 대한 올바른 전문 용어입니다. 여기서는 완벽하게 적합하며 그 의미는 정확하고 잘 이해됩니다. 앞서 설명한대로 "모듈"이라는 표준 기능이있을 때까지는 모듈의 정의가 그대로 유지됩니다. –

답변

120

이것은 Windows와 유닉스 계열 시스템 사이의 꽤 유명한 차이점입니다.

상관없이 :

  • (일부 프로세스 간 통신 라이브러리 또는 확장을 사용하지 않는 한) 모든 메모리를 프로세스간에 공유되고이 결코 의미, 자신의 주소 공간이 각 과정.
  • 하나의 정의 규칙 (ODR)이 계속 적용됩니다. 즉, 링크 타임 (정적 또는 동적 링크)에서 볼 수있는 전역 변수의 정의를 하나만 가질 수 있습니다.

여기 핵심 문제는 실제로 가시성입니다.

모든 경우에 전역 변수 (또는 함수)는 모듈 (dll/so 또는 실행 파일) 외부에서 결코 볼 수 없습니다. C++ 표준은 내부 링크를 필요로합니다. 즉, 정의 된 변환 단위 (오브젝트 파일이됩니다) 외부에서 볼 수 없습니다. 그래서, 그 문제를 해결합니다.

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

Windows의 경우 (.exe 및 .dll) extern 전역 변수는 내 보낸 기호의 일부가 아닙니다. 즉, 다른 모듈은 다른 모듈에 정의 된 전역 변수를 전혀 알지 못합니다. 예를 들어 DLL에 정의 된 extern 변수를 사용해야하는 실행 파일을 만들려고하면 링커 오류가 발생합니다. 이는 허용되지 않기 때문입니다. 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; 

을 당신은 글로벌 변수가 목록에 추가 할 때 내 보낸 기호를 다른 모든 기능처럼 연결할 수 있습니다.

리눅스와 같은 유닉스 환경의 경우, 확장 라이브러리 .so과 함께 "공유 객체"라고하는 동적 라이브러리는 모두 extern 전역 변수 (또는 함수)를 내 보냅니다. 이 경우 로딩 시간을 공유 객체 파일에 연결하면 전역 변수가 공유됩니다 (즉, 하나로 연결됨). 기본적으로 유닉스 계열 시스템은 정적 라이브러리 나 동적 라이브러리와의 연결에 차이가 없도록 설계되었습니다. 다시 ODR이 보드 전체에 적용됩니다. extern 전역 변수가 모듈간에 공유됩니다. 즉,로드 된 모든 모듈에서 하나의 정의 만 있어야합니다.

마지막으로, 두 경우 모두, Windows 또는 유닉스 계열 시스템을 위해, 당신은 즉, 사용, 런타임에게 동적 라이브러리의 연결을 할 수 있습니다 LoadLibrary()/GetProcAddress()/FreeLibrary() 또는 dlopen()/dlsym()/dlclose(). 이 경우 사용하려는 심볼 각각에 대한 포인터를 수동으로 가져와야하며 사용하려는 전역 변수가 포함되어 있어야합니다. 전역 변수의 경우 전역 변수가 내 보낸 기호 목록의 일부인 경우 (앞 단락의 규칙에 따라) GetProcAddress() 또는 dlsym()을 함수와 동일하게 사용할 수 있습니다.

물론 최종 참고로 : 전역 변수는 피해야합니다.. 그리고 당신이 인용 한 텍스트 ("명확하지 않은"것에 대한 것)가 방금 설명한 플랫폼 별 차이점을 정확히 참조하고 있다고 생각합니다 (동적 라이브러리는 실제로 C++ 표준에 의해 정의되지 않았으며 이는 플랫폼 별 영역입니다. 안정성/휴대 성이 훨씬 떨어짐).

+2

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

+8

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

+0

설명해 주셔서 감사합니다. – Raja