2014-10-24 9 views
9

मान लें कि मेरे पास एक संकलन इकाई में स्थिर चर है जो स्थिर लाइब्रेरी libA में समाप्त होता है। इसके बाद मेरे पास इस चर का उपयोग करने वाली एक और संकलन इकाई है जो में लाइब्रेरी libB.so में समाप्त होती है (इसलिए libA libB में लिंक होना चाहिए)। आखिरकार मेरे पास मुख्य कार्य भी ए 0 से सीधे और से स्थिर वैरिएबल तक पहुंच रहा है, जिसमें libB की निर्भरता है (इसलिए मैं libA और libB के खिलाफ लिंक करता हूं)।स्टेटिक वैरिएबल दो बार शुरू किया गया है

मैं तब निरीक्षण करता हूं कि स्थैतिक चर दो बार शुरू होता है, यानी इसका कन्स्ट्रक्टर दो बार चलाया जाता है! यह सही प्रतीत नहीं होता है। क्या लिंकर दोनों वैरिएबल को समान नहीं मानना ​​चाहिए और उन्हें एक के रूप में अनुकूलित करना चाहिए?

मेरा भ्रम सही करने के लिए, मुझे लगता है कि यह एक ही पते के साथ दो बार चलाया जाता है! तो हो सकता है कि लिंकर इसे पहचानें, लेकिन static_initialization_and_destruction code में दूसरी कॉल को हटा दें?

ClassA.hpp:

#ifndef CLASSA_HPP 
#define CLASSA_HPP 

class ClassA 
{ 
public: 
    ClassA(); 
    ~ClassA(); 
    static ClassA staticA; 

    void test(); 
}; 

#endif // CLASSA_HPP 

ClassA.cpp:

#include <cstdio> 
#include "ClassA.hpp" 

ClassA ClassA::staticA; 

ClassA::ClassA() 
{ 
    printf("ClassA::ClassA() this=%p\n", this); 
} 

ClassA::~ClassA() 
{ 
    printf("ClassA::~ClassA() this=%p\n", this); 
} 

void ClassA::test() 
{ 
    printf("ClassA::test() this=%p\n", this); 
} 

ClassB.hpp:

#ifndef CLASSB_HPP 
#define CLASSB_HPP 

class ClassB 
{ 
public: 
    ClassB(); 
    ~ClassB(); 

    void test(); 
}; 

#endif // CLASSB_HPP 

ClassB.cpp:

यहाँ एक प्रदर्शन है

#include <cstdio> 
#include "ClassA.hpp" 
#include "ClassB.hpp" 

ClassB::ClassB() 
{ 
    printf("ClassB::ClassB() this=%p\n", this); 
} 

ClassB::~ClassB() 
{ 
    printf("ClassB::~ClassB() this=%p\n", this); 
} 

void ClassB::test() 
{ 
    printf("ClassB::test() this=%p\n", this); 
    printf("ClassB::test: call staticA.test()\n"); 
    ClassA::staticA.test(); 
} 

Test.cpp:

#include <cstdio> 
#include "ClassA.hpp" 
#include "ClassB.hpp" 

int main(int argc, char * argv[]) 
{ 
    printf("main()\n"); 
    ClassA::staticA.test(); 
    ClassB b; 
    b.test(); 
    printf("main: END\n"); 

    return 0; 
} 

मैं तो संकलन और लिंक इस प्रकार है:

g++ -c ClassA.cpp 
ar rvs libA.a ClassA.o 
g++ -c ClassB.cpp 
g++ -shared -o libB.so ClassB.o libA.a 
g++ -c Test.cpp 
g++ -o test Test.cpp libA.a libB.so 

आउटपुट है:

ClassA::ClassA() this=0x804a040 
ClassA::ClassA() this=0x804a040 
main() 
ClassA::test() this=0x804a040 
ClassB::ClassB() this=0xbfcb064f 
ClassB::test() this=0xbfcb064f 
ClassB::test: call staticA.test() 
ClassA::test() this=0x804a040 
main: END 
ClassB::~ClassB() this=0xbfcb064f 
ClassA::~ClassA() this=0x804a040 
ClassA::~ClassA() this=0x804a040 

किसी कृपया समझा सकते हैं कि क्या हो रहा है यहाँ? लिंकर क्या कर रहा है? एक ही वैरिएबल को दो बार शुरू किया जा सकता है?

+1

संबंधित (शायद नकल): http://stackoverflow.com/questions/6714046/c-linux-double-destruction -of-static-variable-linking-symbols-overlap – jogojapan

+1

क्या यह एक स्थिर lib को संकलित करने और उसके बाद साझा lib को संकलित करने के लिए उपयोग किया जा सकता है? ताकि libB.so में ClassA.o और ClassB.o दोनों से init कोड है? – heksesang

+0

@heksesang: हाँ, यह केवल इस नक्षत्र में होता है। अगर मैं 'ए' और' बी 'स्थिर libs या दोनों साझा libs दोनों बनाता हूं, तो मुझे इस मुद्दे का सामना नहीं करना पड़ता है ('ए' का coror केवल एक बार चलाया जाता है)। हालांकि, मैं लिंकर को डुप्लिकेट प्रतीकों और इनिट कॉल को पहचानने और खत्म करने की अपेक्षा करता हूं। क्या मेरी धारणा गलत है या क्या यह लिंकर है? – bselu

उत्तर

8

आप libA.alibB.so में शामिल हैं। ऐसा करके, libB.so और libA.a दोनों में ClassA.o शामिल है, जो स्थैतिक सदस्य को परिभाषित करता है।

लिंक आदेश में आपके द्वारा निर्दिष्ट, लिंकर ClassA.o स्थिर पुस्तकालय libA.a से में खींचती है, तो ClassA.o प्रवर्तन कोड main() से पहले चलाया जाता है।जब गतिशील libB.so में पहला फ़ंक्शन एक्सेस किया जाता है, सभीlibB.so के लिए प्रारंभकर्ता चलाए जाते हैं। चूंकि libB.so में ClassA.o, ClassA.o का स्थैतिक प्रारंभकर्ता चलाना आवश्यक है (दोबारा)।

संभव फिक्स:

  1. दोनों libA.a और libB.so. में ClassA.o न रखें

    g++ -shared -o libB.so ClassB.o 
    
  2. दोनों पुस्तकालयों का उपयोग न करें; libA.a की जरूरत नहीं है।

    g++ -o test Test.cpp libB.so 
    

ऊपर फिक्स दोनों में से किसी समस्या आवेदन:

ClassA::ClassA() this=0x600e58 
main() 
ClassA::test() this=0x600e58 
ClassB::ClassB() this=0x7fff1a69f0cf 
ClassB::test() this=0x7fff1a69f0cf 
ClassB::test: call staticA.test() 
ClassA::test() this=0x600e58 
main: END 
ClassB::~ClassB() this=0x7fff1a69f0cf 
ClassA::~ClassA() this=0x600e58 
+0

फिक्स 1 के बारे में: यदि मैं 'libA.a' को 'libB.so' में नहीं डालता, तो मैं' libB.so' के साथ एक स्थिर पुस्तकालय की निहित निर्भरता के साथ समाप्त होता हूं। तो अगर मैं 'libB.so' वितरित करता हूं और 'libA.a' के बारे में भूल जाता हूं, तो रिसीवर को अनसुलझे प्रतीकों मिलेगा और मुझे सोचना चाहिए कि मैंने एक अपूर्ण पुस्तकालय दिया है। क्या हम यहां एक सेने लाइब्रेरी की बात कर सकते हैं? फिक्स 2 के बारे में: यह काम नहीं करेगा, अगर 'test.cpp'' libA.a' के भीतर एक प्रतीक को संदर्भित करता है जिसका उपयोग 'libB.so' द्वारा नहीं किया गया था। 'LibA.a' को 'libB.so' में लिंक करते समय लिंकर' libA.a' के अप्रयुक्त प्रतीकों को फेंक देगा। तो मुझे अभी भी 'libA.a' के खिलाफ अपने निष्पादन योग्य लिंक करना होगा। – bselu

+0

क्या आप libA.a को libA.so में बदल सकते हैं, और libA.so और libB.so दोनों भेज सकते हैं? यह शायद सबसे आसान समाधान है। कठिन समाधान आपके पुस्तकालयों को दोबारा बनाना है। प्रत्येक ऑब्जेक्ट फ़ाइल केवल एक लाइब्रेरी में मौजूद होनी चाहिए जिसका उपयोग अंतिम लिंक चरण के लिए किया जाता है। –

6

क्या कोई यहां बता सकता है कि यहां क्या हो रहा है?

यह जटिल है। मुख्य निष्पादन में एक है, और libB.so में एक और:

पहले, तरीका है कि आप अपने मुख्य निष्पादन जुड़ा हुआ है और शेयर की गई लाइब्रेरी staticA की दो उदाहरणों (और ClassA.cpp से सभी अन्य कोड) उपस्थित होने का कारण बनता है।

आप और चल

nm -AD ./test ./libB.so | grep staticA 

यह तो बहुत ही आश्चर्य है कि दो उदाहरणों के लिए ClassA निर्माता दो बार चलाता नहीं है इसकी पुष्टि कर सकते हैं, लेकिन यह अभी भी आश्चर्य की बात है कि this सूचक ही है (मेल खाती है मुख्य निष्पादन योग्य में staticA)।

क्योंकि क्रम लोडर (असफल) संग्रह पुस्तकालयों के साथ जोड़ने के व्यवहार का अनुकरण करने की कोशिश करता है, और पहले विश्व स्तर पर निर्यात उदाहरण यह मानना ​​है ( test में से एक) को staticA के सभी संदर्भ बांधता हो रहा है यही कारण है कि

तो आप इसे ठीक करने के लिए क्या कर सकते हैं? यह वास्तव में दर्शाता है कि staticA वास्तव में क्या दर्शाता है।

यदि यह किसी प्रकार का सिंगलटन है, जो किसी भी प्रोग्राम में केवल एक बार मौजूद होना चाहिए, तो आसान समाधान यह है कि staticA का केवल एक उदाहरण है। और ऐसा करने का एक तरीका यह है कि libB.so का उपयोग करने वाले किसी भी प्रोग्राम libA.a, और लिंक libB.solibA.a के विरुद्ध लिंक के साथ भी लिंक करता है। इससे libB.so के अंदर sttaicA के उदाहरण को खत्म कर दिया जाएगा। आपने दावा किया है कि "libA को libB में जोड़ा जाना चाहिए", लेकिन वह दावा गलत है।

वैकल्पिक रूप से, अगर आप libA.solibA.a के बजाय का निर्माण, तो आप libB.solibA.so के खिलाफ लिंक कर सकते हैं (ताकि libB.so आत्म निहित है)। यदि मुख्य एप्लिकेशन libA.so के खिलाफ भी लिंक करता है, तो यह कोई समस्या नहीं होगी: libA.so के अंदर केवल staticA का एक उदाहरण होगा, इससे कोई फर्क नहीं पड़ता कि लाइब्रेरी का कितनी बार उपयोग किया जाता है।

दूसरी तरफ, यदि staticA किसी प्रकार के आंतरिक कार्यान्वयन विवरण का प्रतिनिधित्व करता है, और आप इसके दो उदाहरण होने के ठीक हैं (जब तक वे एक दूसरे के साथ हस्तक्षेप नहीं करते हैं), तो समाधान सभी को चिह्नित करना है ClassA छिपे हुए दृश्यता वाले प्रतीक, this answer के अनुसार सुझाव।

अद्यतन:

क्यों लिंकर निष्पादन से staticA के दूसरे उदाहरण समाप्त नहीं करती है।

क्योंकि लिंकर ऐसा करता है जो आपने इसे करने के लिए कहा था। आप अपने लिंक कमांड लाइन को बदलते हैं:

g++ -o test Test.cpp libB.so libA.a 

तो लिंकर मुख्य निष्पादन में ClassA लिंक नहीं होना चाहिए। समझने के लिए कि कमांड लाइन पर पुस्तकालयों का क्रम क्यों मायने रखता है, this पढ़ें।

+0

मुझे अभी भी यह नहीं मिला है, क्यों लिंकर निष्पादन योग्य से 'staticA' के दूसरे उदाहरण को खत्म नहीं करता है। सिद्धांत में यह संभव होना चाहिए। – bselu

+0

@bselu उत्तर अपडेट किया गया। –

+0

ठीक है, मैं देखता हूं। अपने अपडेट को पढ़ना मैंने पहली बार सोचा कि लिंक ऑर्डर में बदलाव सामान्य रूप से मेरी समस्या का समाधान कर सकता है। हालांकि, यह नहीं है। यदि मैं उदाहरण के लिए 'libC.so'' जोड़ता हूं तो 'staticA' तक पहुंचता है और 'libA.a' के विरुद्ध जुड़ा होता है, फिर फिर से coror को दो बार बुलाया जाता है। मुझे लगता है कि एकमात्र समझदार समाधान libA.a को libB.so में लिंक नहीं कर रहा है। लेकिन फिर libB.so एक स्थिर lib के लिए एक अंतर्निहित (दृश्यमान नहीं) निर्भरता है। क्या यह अनुमति है? – bselu

संबंधित मुद्दे