Paylaşılan bir kitaplık dinamik olarak bağlandığında global ve statik değişkenlere ne olur?


128

Küresel ve statik değişkenli modüller bir uygulamaya dinamik olarak bağlandığında ne olduğunu anlamaya çalışıyorum. Modüller derken, bir çözümdeki her projeyi kastediyorum (görsel stüdyo ile çok çalışıyorum!). Bu modüller ya * .lib ya da * .dll ya da * .exe'nin kendisinde yerleşiktir.

Bir uygulamanın ikili dosyasının veri segmentindeki tüm bağımsız çeviri birimlerinin (nesne dosyaları) genel ve statik verilerini içerdiğini (ve const ise yalnızca veri segmentini okuduğunu) anlıyorum.

  • Bu uygulama, yükleme süresi dinamik bağlantıya sahip bir modül A kullandığında ne olur? DLL'nin globalleri ve statikleri için bir bölümü olduğunu varsayıyorum. İşletim sistemi bunları yüklüyor mu? Eğer öyleyse, nereye yükleniyorlar?

  • Ve uygulama, çalışma zamanı dinamik bağlantılı bir modül B kullandığında ne olur?

  • Uygulamamda hem A hem de B'yi kullanan iki modülüm varsa, A ve B'nin globallerinin kopyaları aşağıda belirtildiği gibi oluşturulmuş mu (farklı süreçlerse)?

  • DLL A ve B uygulamaları globallerine erişebilir mi?

(Lütfen nedenlerinizi de belirtin)

MSDN'den alıntı :

Bir DLL kaynak kodu dosyasında genel olarak bildirilen değişkenler, derleyici ve bağlayıcı tarafından genel değişkenler olarak ele alınır, ancak belirli bir DLL'yi yükleyen her işlem, o DLL'nin genel değişkenlerinin kendi kopyasını alır. Statik değişkenlerin kapsamı, statik değişkenlerin bildirildiği blokla sınırlıdır. Sonuç olarak, her işlemin varsayılan olarak kendi DLL genel ve statik değişkenleri örneği vardır.

ve buradan :

Modülleri dinamik olarak bağlarken, farklı kitaplıkların kendi global örneklerine sahip olup olmadığı veya globallerin paylaşılıp paylaşılmadığı net olmayabilir.

Teşekkürler.


3
By modüllerin size muhtemelen ortalama kütüphanelerini . C ++ standardına, bir modülün ne olacağına dair daha kesin bir tanım ve şu an itibariyle normal kitaplıklardan farklı anlambilimle modüller eklemek için bir teklif var .
David Rodríguez - dribeas

Ah, bunu açıklamalıydım. Bir çözümdeki farklı projeleri (görsel stüdyo ile çok çalışıyorum) modül olarak görüyorum. Bu modüller * .lib veya * .dll'lerin içine yerleştirilmiştir.
Raja

3
@ DavidRod Rodríguez-dribeas "Modül" terimi, çalıştırılabilir programlar, dinamik bağlantı kitaplıkları (.dll) veya paylaşılan nesneler (.so) dahil olmak üzere bağımsız (tam bağlantılı) yürütülebilir dosyalar için doğru teknik terimdir. Burada tamamen uygundur ve anlamı doğru ve iyi anlaşılmıştır. "Modüller" adında standart bir özellik olana kadar, açıkladığım gibi, tanımı geleneksel olarak kalır.
Mikael Persson

Yanıtlar:


177

Bu, Windows ve Unix benzeri sistemler arasında oldukça ünlü bir farktır.

Ne olursa olsun:

  • Her işlemin kendi adres alanı vardır, yani işlemler arasında hiçbir zaman paylaşılan bellek yoktur (bazı işlemler arası iletişim kitaplığı veya uzantıları kullanmadığınız sürece).
  • Bir Tanım Kuralı (ODR) hala sadece bağlantı süresi (statik veya dinamik bağlama) görünür küresel değişkenin bir tanımını sahip olabileceği anlamına geçerlidir.

Yani, buradaki temel sorun gerçekten görünürlük .

Her durumda, staticglobal değişkenler (veya işlevler) bir modülün dışından (dll / so veya çalıştırılabilir) asla görünmez. C ++ standardı, bunların dahili bağlantıya sahip olmasını gerektirir, yani tanımlandıkları çeviri biriminin dışında (bir nesne dosyası haline gelir) görünmezler. Yani, bu sorunu çözer.

externKüresel değişkenlere sahip olduğunuz zaman karmaşıklaşır . Burada Windows ve Unix benzeri sistemler tamamen farklıdır.

Windows (.exe ve .dll) durumunda, externgenel değişkenler dışa aktarılan sembollerin parçası değildir. Başka bir deyişle, farklı modüller, diğer modüllerde tanımlanan global değişkenlerin hiçbir şekilde farkında değildir. Bu, örneğin, bir externDLL'de tanımlanmış bir değişkeni kullanması gereken bir yürütülebilir dosya oluşturmaya çalışırsanız, buna izin verilmediğinden, bağlayıcı hataları alacağınız anlamına gelir . Bu harici değişkenin tanımını içeren bir nesne dosyası (veya statik kitaplık) sağlamanız ve onu hem yürütülebilir dosya hem de DLL ile statik olarak bağlamanız gerekir ; bu, iki farklı genel değişkenle sonuçlanır (biri yürütülebilir dosyaya, diğeri de DLL'ye aittir. ).

Windows'ta global bir değişkeni gerçekten dışa aktarmak için, verme / alma sözdizimine benzer bir sözdizimi kullanmanız gerekir, yani:

#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;

Bunu yaptığınızda, global değişken dışa aktarılan semboller listesine eklenir ve diğer tüm işlevler gibi bağlanabilir.

Unix benzeri ortamlarda (Linux gibi), uzantıya sahip "paylaşılan nesneler" olarak adlandırılan dinamik kitaplıklar .sotüm externglobal değişkenleri (veya işlevleri) dışa aktarır. Bu durumda, herhangi bir yerden paylaşılan bir nesne dosyasına yükleme süresi bağlantısı yaparsanız , o zaman global değişkenler paylaşılır, yani tek olarak birbirine bağlanır. Temel olarak, Unix benzeri sistemler, statik veya dinamik bir kitaplıkla bağlanma arasında neredeyse hiç fark olmaması için bunu yapmak için tasarlanmıştır. Yine, ODR pano boyunca geçerlidir: externglobal bir değişken modüller arasında paylaşılacaktır, bu da yüklenen tüm modüller arasında yalnızca bir tanıma sahip olması gerektiği anlamına gelir.

Son olarak, her iki durumda da, Windows veya Unix benzeri sistemler için, dinamik kitaplığın çalışma anında bağlanmasını, yani LoadLibrary()/ GetProcAddress()/ FreeLibrary()veya dlopen()/ dlsym()/ kullanarak yapabilirsiniz dlclose(). Bu durumda, kullanmak istediğiniz sembollerin her birine manuel olarak bir işaretçi almanız gerekir ve bu, kullanmak istediğiniz global değişkenleri içerir. Global değişkenler için, global değişkenlerin dışa aktarılan sembol listesinin parçası olması koşuluyla (önceki paragrafların kurallarına göre), işlevler için kullandığınız gibi kullanabilirsiniz GetProcAddress()veya dlsym()aynısını kullanabilirsiniz .

Ve tabii ki, gerekli bir son not olarak: küresel değişkenlerden kaçınılmalıdır . Ve alıntı yaptığınız metnin ("net olmayan" şeyler hakkında) tam olarak az önce açıkladığım platforma özgü farklılıklara atıfta bulunduğuna inanıyorum (dinamik kitaplıklar gerçekten C ++ standardı tarafından tanımlanmamaktadır, bu platforma özgü bölgedir, yani çok daha az güvenilir / taşınabilir).


5
Harika cevap, teşekkürler! Bir takipim var: DLL kendi kendine yeten bir kod ve veri parçası olduğu için, yürütülebilir dosyalara benzer bir veri segmenti bölümü var mı? Paylaşılan kitaplık kullanıldığında bu verilerin nereye ve nasıl yüklendiğini anlamaya çalışıyorum.
Raja

18
@Raja Evet, DLL'nin bir veri segmenti var. Aslında, dosyaların kendileri açısından, yürütülebilir dosyalar ve DLL'ler hemen hemen aynıdır, tek gerçek fark, yürütülebilir dosyada bir "ana" işlev içerdiğini söyleyen bir bayraktır. Bir işlem bir DLL yüklediğinde, veri bölümü işlemin adres alanına bir yere kopyalanır ve statik başlatma kodu (önemsiz olmayan genel değişkenleri başlatır) da işlemin adres alanı içinde çalıştırılır. Yükleme, yeni bir tane oluşturmak yerine işlem adres alanının genişletilmesi dışında yürütülebilir dosya ile aynıdır.
Mikael Persson

4
Bir sınıfın satır içi işlevi içinde tanımlanan statik değişkenlere ne dersiniz? Örneğin, başlık dosyasında "sınıf A {void foo () {statik int st_var = 0;}}" tanımlayın ve bunu modül A ve modül B'ye dahil edin, A / B aynı st_var'ı paylaşacak mı yoksa her birinin kendi kopyası olacak mı?
camino

2
@camino Sınıf dışa aktarılırsa (yani ile tanımlanırsa __attribute__((visibility("default")))), A / B aynı st_var'ı paylaşacaktır. Ancak sınıf ile tanımlanmışsa __attribute__((visibility("hidden"))), modül A ve modül B'nin paylaşılmayan kendi kopyası olacaktır.
Wei Guo

1
@camino __declspec (dllexport)
ruip Pacheco
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.