2011-12-24 5 views
28

पर साझा पुस्तकालयों में सिंगलटन के कई उदाहरण मेरे प्रश्न, जैसा कि शीर्षक का उल्लेख है, स्पष्ट है, और मैं विवरण में परिदृश्य का वर्णन करता हूं।लिनक्स

#include <iostream> 
#include "singleton.h" 

extern "C" void hello() { 
    std::cout << "singleton.num in hello.so : " << singleton::instance().num << std::endl; 
    ++singleton::instance().num; 
    std::cout << "singleton.num in hello.so after ++ : " << singleton::instance().num << std::endl; 
} 

आप देख सकते हैं:

/* 
* singleton.h 
* 
* Created on: 2011-12-24 
*  Author: bourneli 
*/ 

#ifndef SINGLETON_H_ 
#define SINGLETON_H_ 

class singleton 
{ 
private: 
    singleton() {num = -1;} 
    static singleton* pInstance; 
public: 
    static singleton& instance() 
    { 
     if (NULL == pInstance) 
     { 
      pInstance = new singleton(); 
     } 
     return *pInstance; 
    } 
public: 
    int num; 
}; 

singleton* singleton::pInstance = NULL; 

#endif /* SINGLETON_H_ */ 

तो, वहाँ निम्नलिखित के रूप में एक प्लगइन hello.cpp कहा जाता है: सिंगलटन निम्नलिखित के रूप में सिंगलटन पैटर्न द्वारा कार्यान्वित नाम के एक वर्ग, फ़ाइल singleton.h में नहीं है कि प्लगइन सिंगलटन को कॉल करता है और सिंगलटन में विशेषता संख्या बदलता है।

पिछले है, एक मुख्य कार्य निम्नलिखित के रूप में सिंगलटन और प्लगइन का उपयोग:

#include <iostream> 
#include <dlfcn.h> 
#include "singleton.h" 

int main() { 
    using std::cout; 
    using std::cerr; 
    using std::endl; 

    singleton::instance().num = 100; // call singleton 
    cout << "singleton.num in main : " << singleton::instance().num << endl;// call singleton 

    // open the library 
    void* handle = dlopen("./hello.so", RTLD_LAZY); 

    if (!handle) { 
     cerr << "Cannot open library: " << dlerror() << '\n'; 
     return 1; 
    } 

    // load the symbol 
    typedef void (*hello_t)(); 

    // reset errors 
    dlerror(); 
    hello_t hello = (hello_t) dlsym(handle, "hello"); 
    const char *dlsym_error = dlerror(); 
    if (dlsym_error) { 
     cerr << "Cannot load symbol 'hello': " << dlerror() << '\n'; 
     dlclose(handle); 
     return 1; 
    } 

    hello(); // call plugin function hello 

    cout << "singleton.num in main : " << singleton::instance().num << endl;// call singleton 
    dlclose(handle); 
} 

और makefile पीछा कर रहा है:

example1: main.cpp hello.so 
    $(CXX) $(CXXFLAGS) -o example1 main.cpp -ldl 

hello.so: hello.cpp 
    $(CXX) $(CXXFLAGS) -shared -o hello.so hello.cpp 

clean: 
    rm -f example1 hello.so 

.PHONY: clean 

हां, तो क्या उत्पादन है? मैंने सोचा था कि वहाँ पीछा कर रहा है:

singleton.num in main : 100 
singleton.num in hello.so : 100 
singleton.num in hello.so after ++ : 101 
singleton.num in main : 101 

हालांकि, वास्तविक उत्पादन पीछा कर रहा है:

singleton.num in main : 100 
singleton.num in hello.so : -1 
singleton.num in hello.so after ++ : 0 
singleton.num in main : 100 

यह साबित होता है सिंगलटन वर्ग के दो उदाहरणों देखते हैं कि।

क्यों?

+0

क्या आप चाहते हैं कि यह सिंगलटन आपके द्वारा निष्पादित की जा रही प्रक्रिया के लिए सिंगलटन हो? या एक संरक्षित स्मृति के उल्लंघन में "सिस्टम-व्यापी" सिंगलटन हमें प्रदान करता है? – sarnold

+0

प्रश्न, स्पष्ट रूप से कहा गया है, आमतौर पर कुछ भी स्पष्ट है। क्या आप साझा पुस्तकालयों को सिंगलटन साझा करना चाहते हैं या नहीं?क्या आप किसी भी व्यवहार के बारे में सोचते हैं या वास्तव में इसका अनुभव करते हैं? जब तक आप हमें नहीं बताते, तब तक जानने का कोई तरीका नहीं है। – 9000

+0

@ कर्नाल्ड: सिस्टम-व्यापी सिंगलेट्स का एक प्रसिद्ध पैटर्न है जो पता स्थान द्वारा सीमित नहीं है: इसे सर्वर कहा जाता है। लेकिन जब तक मूल पोस्टर अपने कोड के _purpose_ को नहीं बताता है, तब तक यह बताने में मुश्किल होती है कि यह पैटर्न फिट बैठता है या नहीं। – 9000

उत्तर

45

सबसे पहले, आपको आमतौर पर साझा पुस्तकालयों के निर्माण के दौरान -fPIC ध्वज का उपयोग करना चाहिए।

इसे प्रयोग नहीं 32-बिट लिनक्स पर "काम" है, लेकिन करने के लिए इसी तरह की एक त्रुटि के साथ 64-बिट एक पर विफल हो जाएगा:

/usr/bin/ld: /tmp/ccUUrz9c.o: relocation R_X86_64_32 against `.rodata' can not be used when making a shared object; recompile with -fPIC 

दूसरा, अपने कार्यक्रम के रूप में आप आप -rdynamic जोड़े जाने के बाद उम्मीद काम करेंगे मुख्य निष्पादन के लिए लिंक लाइन के लिए:

singleton.num in main : 100 
singleton.num in hello.so : 100 
singleton.num in hello.so after ++ : 101 
singleton.num in main : 101 

आदेश में समझने के लिए क्यों -rdynamic की आवश्यकता है, आप जिस तरह से गतिशील लिंकर प्रतीकों का समाधान करता है के बारे में पता करने की जरूरत है, और गतिशील symbo के बारे में एल टेबल वैश्विक चर singleton::pInstance कि गतिशील लिंकर को दिखाई देते हैं

$ nm -C -D hello.so | grep singleton 
0000000000000b8c W singleton::instance() 
0000000000201068 B singleton::pInstance 
0000000000000b78 W singleton::singleton() 

यह हमें बताता है दो कमजोर समारोह परिभाषाओं देखते हैं कि, और एक:

पहले, आइए hello.so के लिए गतिशील प्रतीक तालिका को देखो।

अब के मूल example1 (-rdynamic बिना जुड़े) के लिए स्थिर और गतिशील प्रतीक मेज पर नजर डालते हैं:

$ nm -C example1 | grep singleton 
0000000000400d0f t global constructors keyed to singleton::pInstance 
0000000000400d38 W singleton::instance() 
00000000006022e0 B singleton::pInstance 
0000000000400d24 W singleton::singleton() 

$ nm -C -D example1 | grep singleton 
$ 

यह सही है: भले ही singleton::pInstance एक वैश्विक चर के रूप में निष्पादन में मौजूद है, कि प्रतीक गतिशील प्रतीक तालिका में मौजूद नहीं है, और इसलिए गतिशील लिंकर को "अदृश्य" है।

चूंकि प्रभावी लिंकर "पता नहीं है" example1 पहले से ही singleton::pInstance की एक परिभाषा में शामिल है कि, यह है कि चर hello.so अंदर मौजूदा परिभाषा करने के लिए बाध्य नहीं है (जो आप वास्तव में क्या चाहते हैं)।

हम लिंक लाइन के लिए -rdynamic जोड़ते हैं:

$ nm -C example1-rdynamic | grep singleton 
0000000000400fdf t global constructors keyed to singleton::pInstance 
0000000000401008 W singleton::instance() 
00000000006022e0 B singleton::pInstance 
0000000000400ff4 W singleton::singleton() 

$ nm -C -D example1-rdynamic | grep singleton 
0000000000401008 W singleton::instance() 
00000000006022e0 B singleton::pInstance 
0000000000400ff4 W singleton::singleton() 

अब मुख्य निष्पादन के अंदर singleton::pInstance की परिभाषा गतिशील लिंकर को दिखाई है, और इसलिए यह है कि परिभाषा "पुन: उपयोग" होगा जब hello.so लोड हो रहा है :

LD_DEBUG=bindings ./example1-rdynamic |& grep pInstance 
    31972: binding file ./hello.so [0] to ./example1-rdynamic [0]: normal symbol `_ZN9singleton9pInstanceE' 
+0

-rdynmaic विकल्प इस समस्या को हल करें, धन्यवाद। मैं आपकी मदद की सराहना करता हूं :) – bourneli

+1

@ बॉर्नले अगर उत्तर आपके लिए काम करता है, तो आपको इसे स्वीकार करना होगा। –

+0

क्या आप कृपया समझा सकते हैं कि विपरीत कैसे करें? मेरे पास एक साझा बेस लाइब्रेरी है जो दो प्लगइन्स से जुड़ा हुआ है, साझा लाइब्रेरी एक सिंगलटन निर्यात करती है; लेकिन मैं चाहता हूं कि प्रत्येक प्लगइन सिंगलटन की अपनी प्रति बनाए रखे। मेरे पास संकलित ध्वज के रूप में सूचीबद्ध नहीं है। –

4

रनटाइम-लोड किए गए साझा पुस्तकालयों का उपयोग करते समय आपको सावधान रहना होगा। ऐसा निर्माण सख्ती से सी ++ मानक का हिस्सा नहीं है, और आपको सावधानी से विचार करना होगा कि इस तरह की प्रक्रिया के अर्थशास्त्र क्या हैं।

सबसे पहले, क्या हो रहा है यह है कि साझा लाइब्रेरी अपने स्वयं के, अलग वैश्विक चर singleton::pInstance देखती है। ऐसा क्यों है? एक लाइब्रेरी जो रनटाइम पर लोड की जाती है वह अनिवार्य रूप से एक अलग, स्वतंत्र प्रोग्राम है जो केवल प्रवेश बिंदु नहीं होता है। लेकिन बाकी सब कुछ एक अलग कार्यक्रम की तरह है, और गतिशील लोडर इसका इलाज करेगा, उदाहरण के लिए वैश्विक चर आदि शुरू करें

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

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

उदाहरण के लिए, आपकी साझा लाइब्रेरी इस तरह दिख सकती है।सबसे पहले, सिंगलटन वर्ग के लिए एक सूचक-टू-सूचक जोड़ें:

class singleton 
{ 
    static singleton * pInstance; 
public: 
    static singleton ** ppinstance; 
    // ... 
}; 

singleton ** singleton::ppInstance(&singleton::pInstance); 

अब हर जगह pInstance के बजाय *ppInstance का उपयोग करें।

प्लगइन में, मुख्य कार्यक्रम से सूचक को सिंगलटन कॉन्फ़िगर करें:

init_fn init; 
hello_fn hello; 
*reinterpret_cast<void**>(&init) = dlsym(lib, "init"); 
*reinterpret_cast<void**>(&hello) = dlsym(lib, "hello"); 

init(singleton::ppInstance); 
hello(); 

अब प्लगइन शेयरों ही सूचक:

void init(singleton ** p) 
{ 
    singleton::ppInsance = p; 
} 

और मुख्य कार्य, प्लगइन intialization फोन कार्यक्रम के बाकी हिस्सों के रूप में सिंगलटन उदाहरण के लिए।

+2

यदि आप कुछ ग्लोबल के पते के साथ "सिंगलटन" को प्रारंभ करने के लिए मजबूर करते हैं, तो यह अब * सिंगलटन * नहीं है। आपका उत्तर कई विवरणों में गलत है, और आपके द्वारा प्रस्तावित समाधान (आईएमएचओ) फर्जी है। –

+0

मैं सहमत हूं। इस समाधान के साथ सिंगलटन पैटर्न का उपयोग करने का पूरा उद्देश्य खो गया है। – volpato

2

मुझे लगता है कि इसका सरल उत्तर यहाँ है: http://www.yolinux.com/TUTORIALS/LibraryArchives-StaticAndDynamic.html

आप एक स्थिर चर उपलब्ध हो तो उसे वस्तु में संग्रहीत है (ओ, ए और/या .so)

अंतिम वस्तु निष्पादित करने के लिए वस्तु के दो संस्करण हैं, तो व्यवहार अप्रत्याशित है उदाहरण के लिए, सिंगलटन ऑब्जेक्ट के विनाशक को बुलाओ।

उचित डिज़ाइन का उपयोग करना, जैसे मुख्य फ़ाइल में स्थिर सदस्य घोषित करना और-गतिशील/एफपीआईसी का उपयोग करना और संकलक निर्देशों का उपयोग करना आपके लिए चाल का हिस्सा होगा।

उदाहरण makefile बयान:

$ g++ -rdynamic -o appexe $(OBJ) $(LINKFLAGS) -Wl,--whole-archive -L./Singleton/ -lsingleton -Wl,--no-whole-archive $(LIBS) 

आशा इस काम करता है!

0

आपके उत्तरों के लिए सभी को धन्यवाद!

लिनक्स के लिए फॉलो-अप के रूप में, आप RTLD_GLOBALdlopen(...), प्रति man dlopen (और इसके उदाहरणों के साथ) का भी उपयोग कर सकते हैं। मैं इस निर्देशिका में ओपी के उदाहरण का एक संस्करण बनाया है: github tree उदाहरण आउटपुट: output.txt

त्वरित और गंदे:

  • आपके पास करने के लिए नहीं करना चाहते हैं मैन्युअल रूप से करने के लिए प्रत्येक प्रतीक में जोड़ने के लिए अपना main, साझा वस्तुओं को चारों ओर रखें। (उदाहरण के लिए, यदि आपने *.so ऑब्जेक्ट्स को पायथन में आयात करने के लिए बनाया है)
  • आप प्रारंभ में वैश्विक प्रतीक तालिका में लोड कर सकते हैं, या NOLOAD + GLOBAL फिर से खोल सकते हैं।

कोड:

#if MODE == 1 
// Add to static symbol table. 
#include "producer.h" 
#endif 
... 
    #if MODE == 0 || MODE == 1 
     handle = dlopen(lib, RTLD_LAZY); 
    #elif MODE == 2 
     handle = dlopen(lib, RTLD_LAZY | RTLD_GLOBAL); 
    #elif MODE == 3 
     handle = dlopen(lib, RTLD_LAZY); 
     handle = dlopen(lib, RTLD_LAZY | RTLD_NOLOAD | RTLD_GLOBAL); 
    #endif 

मोड:

  • मोड 0: नाममात्र आलसी लोड हो रहा है (काम नहीं करेगा)
  • मोड 1: स्थिर प्रतीक तालिका में जोड़ने के लिए फ़ाइल को शामिल करें।
  • मोड 2: शुरुआत में लोड करें RTLD_GLOBAL
  • मोड 3: RTLD_NOLOAD का उपयोग करके पुनः लोड करें। RTLD_GLOBAL