जब यह गतिशील रूप से जुड़ा होता है तो एक साझा पुस्तकालय में वैश्विक और स्थिर चर क्या होता है?


127

मैं यह समझने की कोशिश कर रहा हूं कि जब ग्लोबल्स और स्थिर चर वाले मॉड्यूल गतिशील रूप से एक एप्लिकेशन से जुड़े होते हैं। मॉड्यूल द्वारा, मेरा मतलब है कि प्रत्येक परियोजना एक समाधान में है (मैं दृश्य स्टूडियो के साथ बहुत काम करता हूं!)। ये मॉड्यूल या तो * .लिब या * .dll या * .exe में ही निर्मित होते हैं।

मैं समझता हूं कि किसी एप्लिकेशन के बाइनरी में डेटा सेगमेंट में सभी व्यक्तिगत अनुवाद इकाइयों (ऑब्जेक्ट फ़ाइलों) का वैश्विक और स्थिर डेटा होता है (और केवल डेटा सेगमेंट पढ़ें अगर कॉन्स्टेंस)।

  • जब यह एप्लिकेशन लोड-टाइम डायनेमिक लिंकिंग के साथ मॉड्यूल A का उपयोग करता है तो क्या होता है? मुझे लगता है कि DLL अपने ग्लोबल्स और स्टैटिक्स के लिए एक सेक्शन है। क्या ऑपरेटिंग सिस्टम उन्हें लोड करता है? यदि हां, तो वे कहां से लोड होते हैं?

  • और क्या होता है जब एप्लिकेशन रन-टाइम डायनेमिक लिंकिंग के साथ मॉड्यूल बी का उपयोग करता है?

  • यदि मेरे आवेदन में दो मॉड्यूल हैं जो दोनों ए और बी का उपयोग करते हैं, तो नीचे वर्णित के अनुसार ए और बी के ग्लोबल्स की प्रतियां बनाई गई हैं (यदि वे अलग-अलग प्रक्रियाएं हैं)?

  • क्या DLLs A और B एप्लिकेशन ग्लोबल्स तक पहुँच प्राप्त करते हैं?

(कृपया अपने कारण भी बताएं)

MSDN से उद्धरण :

DLL स्रोत कोड फ़ाइल में वैश्विक रूप से घोषित चर को कंपाइलर और लिंकर द्वारा वैश्विक चर के रूप में माना जाता है, लेकिन प्रत्येक प्रक्रिया जो किसी दिए गए DLL को लोड करती है, उसे उस DLL के वैश्विक चर की अपनी प्रतिलिपि मिलती है। स्थैतिक चर का दायरा उस खंड तक सीमित होता है जिसमें स्थिर चर घोषित किए जाते हैं। नतीजतन, प्रत्येक प्रक्रिया में डिफ़ॉल्ट रूप से DLL वैश्विक और स्थिर चर का अपना उदाहरण है।

और यहाँ से :

जब गतिशील रूप से मॉड्यूल को जोड़ने पर, यह स्पष्ट नहीं हो सकता है कि क्या विभिन्न पुस्तकालयों में ग्लोबल्स के अपने उदाहरण हैं या क्या ग्लोबल्स साझा किए गए हैं।

धन्यवाद।


3
द्वारा मॉड्यूल आप शायद मतलब libsमॉड्यूल की सी ++ मानक के साथ एक और अधिक सटीक परिभाषा के साथ मॉड्यूल जोड़ने का प्रस्ताव है जो अब तक के नियमित पुस्तकालयों की तुलना में एक मॉड्यूल और अलग शब्दार्थ होगा।
डेविड रॉड्रिग्ज - dribeas

आह, स्पष्ट करना चाहिए था कि। मैं एक समाधान में विभिन्न परियोजनाओं पर विचार करता हूं (मैं दृश्य स्टूडियो के साथ बहुत काम करता हूं) मॉड्यूल के रूप में। इन मॉड्यूल को * .लिब या * .dll के रूप में बनाया गया है।
राजा

3
@ DavidRodríguez-dribeas शब्द "मॉड्यूल" स्टैंडअलोन (पूरी तरह से जुड़ी) निष्पादन योग्य फ़ाइलों के लिए सही तकनीकी शब्द है, जिनमें शामिल हैं: निष्पादन योग्य प्रोग्राम, डायनेमिक-लिंक लाइब्रेरी (.dll) या साझा ऑब्जेक्ट (.so)। यह पूरी तरह से यहां उपयुक्त है, और अर्थ सही और अच्छी तरह से समझा गया है। जब तक "मॉड्यूल" नाम की एक मानक विशेषता नहीं होती है, तब तक इसकी परिभाषा पारंपरिक बनी हुई है, जैसा कि मैंने समझाया।
मिकेल पर्सन

जवाबों:


176

यह विंडोज और यूनिक्स जैसी प्रणालियों के बीच एक बहुत प्रसिद्ध अंतर है।

कोई बात नहीं क्या:

  • प्रत्येक प्रक्रिया का अपना पता स्थान होता है, जिसका अर्थ है कि प्रक्रियाओं के बीच कोई भी मेमोरी साझा नहीं की जा रही है (जब तक कि आप कुछ अंतर-प्रक्रिया संचार पुस्तकालय या एक्सटेंशन का उपयोग नहीं करते हैं)।
  • एक परिभाषा नियम (ओडीआर) अभी भी लागू होता है, जिसका अर्थ है कि आप केवल वैश्विक चर लिंक-टाइम (स्थिर या गतिशील जोड़ने) पर दृश्यमान में से एक परिभाषा हो सकता है।

इसलिए, यहां प्रमुख मुद्दा वास्तव में दृश्यता है

सभी मामलों में, staticवैश्विक चर (या फ़ंक्शन) किसी मॉड्यूल (dll / so या निष्पादन योग्य) के बाहर से कभी दिखाई नहीं देते हैं। C ++ मानक के लिए आवश्यक है कि इनमें आंतरिक लिंकेज हो, जिसका अर्थ है कि वे अनुवाद इकाई के बाहर दिखाई नहीं दे रहे हैं (जो एक वस्तु फ़ाइल बन जाती है) जिसमें वे परिभाषित हैं। तो, उस मुद्दे को सुलझाता है।

यह जटिल हो जाता है जब आपके पास externवैश्विक चर होते हैं। यहां, विंडोज और यूनिक्स जैसी प्रणाली पूरी तरह से अलग हैं।

विंडोज (.exe और .dll) के मामले में, externवैश्विक चर निर्यात किए गए प्रतीकों का हिस्सा नहीं हैं। दूसरे शब्दों में, विभिन्न मॉड्यूल किसी भी तरह से अन्य मॉड्यूल में परिभाषित वैश्विक चरों के बारे में नहीं जानते हैं। इसका मतलब है कि यदि आप प्रयास करते हैं, तो आपको लिंकर की त्रुटियां मिलेंगी, उदाहरण के लिए, एक निष्पादन योग्य बनाने के लिए जो externएक DLL में परिभाषित चर का उपयोग करना चाहिए , क्योंकि यह अनुमति नहीं है। आपको उस बाह्य चर की परिभाषा के साथ एक वस्तु फ़ाइल (या स्थिर पुस्तकालय) प्रदान करने की आवश्यकता होगी और इसे निष्पादन योग्य और DLL दोनों के साथ सांख्यिकीय रूप से लिंक किया जाएगा , जिसके परिणामस्वरूप दो अलग-अलग वैश्विक चर (निष्पादन योग्य और DLL से संबंधित एक) होंगे )।

वास्तव में विंडोज में एक वैश्विक चर निर्यात करने के लिए, आपको फ़ंक्शन निर्यात / आयात सिंटैक्स के समान एक सिंटैक्स का उपयोग करना होगा, अर्थात:

#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वैश्विक चर को मॉड्यूल में साझा किया जाएगा, जिसका अर्थ है कि लोड किए गए सभी मॉड्यूलों में इसकी केवल एक परिभाषा होनी चाहिए।

अंत में, दोनों ही मामलों में, विंडोज या के लिए यूनिक्स सिस्टम, आप कर सकते हैं रन-टाइम , गतिशील पुस्तकालय को जोड़ने, यानी या तो का उपयोग कर LoadLibrary()/ GetProcAddress()/ FreeLibrary()या dlopen()/ dlsym()/ dlclose()। उस स्थिति में, आपको मैन्युअल रूप से उपयोग किए जाने वाले प्रत्येक प्रतीक के लिए एक संकेतक प्राप्त करना होगा, और इसमें आपके द्वारा उपयोग किए जाने वाले वैश्विक चर शामिल होंगे। वैश्विक चरों के लिए, आप उपयोग कर सकते हैं GetProcAddress()या dlsym()जैसा आप कार्य करते हैं, बशर्ते कि वैश्विक चर निर्यातित प्रतीक सूची (पिछले पैराग्राफ के नियमों द्वारा) का हिस्सा हों।

और निश्चित रूप से, एक आवश्यक अंतिम नोट के रूप में: वैश्विक चर से बचा जाना चाहिए । और मुझे विश्वास है कि आपने जो पाठ उद्धृत किया है ("अस्पष्ट" होने के बारे में) बिल्कुल उसी प्लेटफ़ॉर्म-विशिष्ट अंतर का उल्लेख कर रहा है जिसे मैंने अभी समझाया था (डायनेमिक लाइब्रेरी वास्तव में C ++ मानक द्वारा परिभाषित नहीं हैं, यह प्लेटफ़ॉर्म-विशिष्ट क्षेत्र है, जिसका अर्थ है) बहुत कम विश्वसनीय / पोर्टेबल है)।


5
शानदार जवाब, धन्यवाद! मेरे पास एक अनुवर्ती है: चूंकि DLL कोड और डेटा का एक स्व-निहित टुकड़ा है, क्या इसमें निष्पादन खंडों के समान डेटा खंड है? जब साझा लाइब्रेरी का उपयोग किया जाता है तो मैं यह समझने की कोशिश कर रहा हूं कि यह डेटा कहां और कैसे लोड किया गया है।
राजा

18
@ रजा हां, DLL का एक डेटा खंड है। वास्तव में, स्वयं फ़ाइलों के संदर्भ में, निष्पादन योग्य और DLL लगभग समान हैं, केवल वास्तविक अंतर एक ध्वज है जिसे निष्पादन योग्य में यह कहने के लिए सेट किया गया है कि इसमें "मुख्य" फ़ंक्शन है। जब कोई प्रक्रिया DLL को लोड करती है, तो उसके डेटा सेगमेंट को प्रोसेस के एड्रेस स्पेस में कहीं कॉपी किया जाता है, और स्टैटिक इनिशियलाइज़ेशन कोड (जो नॉन-ट्रिवियल ग्लोबल वैरिएबल्स को इनिशियलाइज़ करता है) प्रोसेस के एड्रेस स्पेस के भीतर भी चलाया जाता है। लोडिंग निष्पादन योग्य के लिए समान है, सिवाय इसके कि प्रक्रिया पता स्थान एक नया बनाया के बजाय विस्तारित किया गया है।
मिकेल पर्सन

4
किसी वर्ग के इनलाइन फ़ंक्शन के अंदर परिभाषित स्थिर चर के बारे में कैसे? उदाहरण के लिए "class A {void foo () {static int st_var = 0;}} को हेडर फ़ाइल में परिभाषित करें और इसे मॉड्यूल A और मॉड्यूल B में शामिल करें, क्या A / B उसी st_var को साझा करेगा या प्रत्येक की अपनी प्रति होगी?
कैमिनो

2
@camino यदि वर्ग निर्यात किया जाता है (अर्थात परिभाषित किया गया है __attribute__((visibility("default")))), तो A / B समान st_var साझा करेगा। लेकिन अगर वर्ग को परिभाषित किया गया है __attribute__((visibility("hidden"))), तो मॉड्यूल ए और मॉड्यूल बी की अपनी प्रतिलिपि होगी, साझा नहीं की जाएगी।
वी गुओ

1
@camino __declspec (dllexport)
ruipacheco
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.