Apa yang terjadi pada variabel global dan statis di pustaka bersama ketika ditautkan secara dinamis?

127

Saya mencoba memahami apa yang terjadi ketika modul dengan variabel global dan statis secara dinamis ditautkan ke aplikasi. Yang saya maksud dengan modul adalah setiap proyek dalam sebuah solusi (saya banyak bekerja dengan studio visual!). Modul-modul ini dibangun ke dalam * .lib atau * .dll atau * .exe itu sendiri.

Saya memahami bahwa biner aplikasi berisi data global dan statis dari semua unit terjemahan individual (file objek) di segmen data (dan hanya membaca segmen data jika const).

  • Apa yang terjadi jika aplikasi ini menggunakan modul A dengan penautan dinamis waktu muat? Saya berasumsi DLL memiliki bagian untuk global dan statika. Apakah sistem operasi memuatnya? Jika ya, ke mana mereka dimuat?

  • Dan apa yang terjadi jika aplikasi menggunakan modul B dengan tautan dinamis run-time?

  • Jika saya memiliki dua modul dalam aplikasi saya yang menggunakan A dan B, apakah salinan global A dan B dibuat seperti yang disebutkan di bawah (jika prosesnya berbeda)?

  • Apakah DLL A dan B mendapatkan akses ke aplikasi global?

(Harap sebutkan alasan Anda juga)

Mengutip dari MSDN :

Variabel yang dideklarasikan sebagai global dalam file kode sumber DLL diperlakukan sebagai variabel global oleh compiler dan linker, tetapi setiap proses yang memuat DLL tertentu mendapatkan salinannya sendiri dari variabel global DLL tersebut. Ruang lingkup variabel statis terbatas pada blok di mana variabel statis dideklarasikan. Akibatnya, setiap proses memiliki contoh sendiri dari variabel global dan statis DLL secara default.

dan dari sini :

Saat menautkan modul secara dinamis, tidak jelas apakah library yang berbeda memiliki instance globalnya sendiri atau apakah library tersebut dibagikan.

Terima kasih.

Raja
sumber
3
Dengan modul Anda mungkin berarti libs . Ada proposal untuk menambahkan modul ke standar C ++ dengan definisi yang lebih tepat tentang apa itu modul dan semantik yang berbeda dari pustaka biasa seperti yang sekarang.
David Rodríguez - dribeas
Ah, seharusnya menjelaskan itu. Saya menganggap proyek yang berbeda dalam sebuah solusi (saya banyak bekerja dengan studio visual) sebagai modul. Modul-modul ini dibangun ke dalam * .lib atau * .dll.
Raja
3
@ DavidRodríguez-dribeas Istilah "modul" adalah istilah teknis yang benar untuk file yang dapat dijalankan secara mandiri (terhubung sepenuhnya), termasuk: program yang dapat dieksekusi, pustaka tautan dinamis (.dll) atau objek bersama (.so). Ini sangat tepat di sini, dan artinya benar serta dipahami dengan baik. Sampai ada fitur standar bernama "modul", definisi itu tetap tradisional, seperti yang saya jelaskan.
Mikael Persson

Jawaban:

176

Ini adalah perbedaan yang cukup terkenal antara sistem Windows dan mirip Unix.

Apa pun yang terjadi:

  • Setiap proses memiliki ruang alamatnya sendiri, yang berarti bahwa tidak pernah ada memori yang dibagi antar proses (kecuali Anda menggunakan beberapa pustaka atau ekstensi komunikasi antar-proses).
  • Aturan Satu Definisi (ODR) tetap berlaku, artinya Anda hanya dapat memiliki satu definisi variabel global yang terlihat pada waktu penautan (penautan statis atau dinamis).

Jadi, masalah kuncinya di sini adalah visibilitas .

Dalam semua kasus, staticvariabel global (atau fungsi) tidak pernah terlihat dari luar modul (dll / so atau executable). Standar C ++ mensyaratkan bahwa ini memiliki hubungan internal, yang berarti bahwa mereka tidak terlihat di luar unit terjemahan (yang menjadi file objek) di mana mereka didefinisikan. Jadi, itu menyelesaikan masalah itu.

Yang menjadi rumit adalah ketika Anda memiliki externvariabel global. Di sini, sistem Windows dan mirip Unix sama sekali berbeda.

Dalam kasus Windows (.exe dan .dll), externvariabel global bukan bagian dari simbol yang diekspor. Dengan kata lain, modul yang berbeda sama sekali tidak mengetahui variabel global yang ditentukan dalam modul lain. Ini berarti Anda akan mendapatkan kesalahan penaut jika Anda mencoba, misalnya, untuk membuat file yang dapat dieksekusi yang seharusnya menggunakan externvariabel yang ditentukan dalam DLL, karena ini tidak diperbolehkan. Anda akan perlu untuk memberikan file objek (atau perpustakaan statis) dengan definisi variabel ekstern dan link statis dengan baik dieksekusi dan DLL, mengakibatkan dua variabel yang berbeda global (satu milik executable dan satu milik DLL ).

Untuk benar-benar mengekspor variabel global di Windows, Anda harus menggunakan sintaks yang mirip dengan sintaks fungsi ekspor / impor, yaitu:

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

Saat Anda melakukannya, variabel global ditambahkan ke daftar simbol yang diekspor dan dapat ditautkan seperti semua fungsi lainnya.

Dalam kasus lingkungan mirip Unix (seperti Linux), pustaka dinamis, yang disebut "objek bersama" dengan ekstensi .somengekspor semua externvariabel global (atau fungsi). Dalam kasus ini, jika Anda melakukan penautan waktu muat dari mana saja ke file objek bersama, maka variabel global akan dibagikan, yaitu, ditautkan bersama sebagai satu. Pada dasarnya, sistem mirip Unix dirancang untuk membuatnya sehingga hampir tidak ada perbedaan antara menghubungkan dengan pustaka statis atau dinamis. Sekali lagi, ODR berlaku di seluruh papan: externvariabel global akan dibagikan di seluruh modul, yang berarti hanya ada satu definisi di semua modul yang dimuat.

Terakhir, dalam kedua kasus tersebut, untuk sistem Windows atau mirip Unix, Anda dapat melakukan penautan run-time dari pustaka dinamis, misalnya, menggunakan LoadLibrary()/ GetProcAddress()/ FreeLibrary()atau dlopen()/ dlsym()/ dlclose(). Dalam hal ini, Anda harus secara manual mendapatkan pointer ke setiap simbol yang ingin Anda gunakan, dan itu termasuk variabel global yang ingin Anda gunakan. Untuk variabel global, Anda dapat menggunakan GetProcAddress()atau dlsym()sama seperti yang Anda lakukan untuk fungsi, asalkan variabel global adalah bagian dari daftar simbol yang diekspor (berdasarkan aturan paragraf sebelumnya).

Dan tentu saja, sebagai catatan akhir yang diperlukan: variabel global harus dihindari . Dan saya percaya bahwa teks yang Anda kutip (tentang hal-hal yang "tidak jelas") merujuk persis pada perbedaan khusus platform yang baru saja saya jelaskan (perpustakaan dinamis tidak benar-benar ditentukan oleh standar C ++, ini adalah wilayah khusus platform, artinya itu jauh kurang andal / portabel).

Mikael Persson
sumber
5
Jawaban yang bagus, terima kasih! Saya memiliki tindak lanjut: Karena DLL adalah bagian kode & data yang berdiri sendiri, apakah ia memiliki bagian segmen data yang mirip dengan file yang dapat dieksekusi? Saya mencoba memahami di mana dan bagaimana data ini dimuat (ke) saat pustaka bersama digunakan.
Raja
18
@Raja Ya, DLL memiliki segmen data. Faktanya, dalam hal file itu sendiri, file yang dapat dieksekusi dan DLL hampir identik, satu-satunya perbedaan nyata adalah tanda yang disetel di file yang dapat dieksekusi untuk mengatakan bahwa file itu berisi fungsi "utama". Ketika sebuah proses memuat DLL, segmen datanya disalin di suatu tempat ke ruang alamat proses, dan kode inisialisasi statis (yang akan menginisialisasi variabel global non-sepele) juga dijalankan dalam ruang alamat proses. Pemuatannya sama dengan yang dapat dieksekusi, kecuali bahwa ruang alamat proses diperluas alih-alih membuat yang baru.
Mikael Persson
4
Bagaimana dengan variabel statis yang didefinisikan di dalam fungsi sebaris kelas? misalnya mendefinisikan "kelas A {void foo () {static int st_var = 0;}}" di file header dan memasukkannya ke dalam modul A dan modul B, apakah A / B akan berbagi st_var yang sama atau masing-masing akan memiliki salinannya sendiri?
camino
2
@camino Jika kelas diekspor (yaitu didefinisikan dengan __attribute__((visibility("default")))), maka A / B akan berbagi st_var yang sama. Tetapi jika kelas ditentukan dengan __attribute__((visibility("hidden"))), maka modul A dan modul B akan memiliki salinannya sendiri, tidak dibagikan.
Wei Guo
1
@camino __declspec (dllexport)
ruipacheco