2010-07-31 8 views
6

कृपया ध्यान दें कि मैं अपने प्रश्न के साथ किसी भी समस्या का समाधान नहीं करना चाहते हैं - मैं बातें होती हैं करने के लिए की संभावनाओं के बारे में सोच रहा था और इस तरह कुछ के बारे में सोच रहा था:यदि आप कोई ऑब्जेक्ट हटाते हैं तो वास्तव में क्या होता है? (जीसीसी) (जब दुर्घटनाओं डबल हटाना चाहते हैं?)

यदि आप ऑब्जेक्ट पर हटाते हैं और जीसीसी को कंपाइलर के रूप में उपयोग करते हैं तो वास्तव में क्या होता है?

पिछले हफ्ते मैं एक दुर्घटना की जांच कर रहा था, जहां एक दौड़ की स्थिति किसी वस्तु के दोहरे डिलीट की ओर ले जाती है।

ऑब्जेक्ट के वर्चुअल विनाशक को कॉल करते समय क्रैश हुआ, क्योंकि वर्चुअल फ़ंक्शन तालिका के पॉइंटर को पहले ही ओवरराइट किया गया था।

क्या वर्चुअल फ़ंक्शन पॉइंटर पहले डिलीट द्वारा ओवरराइट किया गया है?

यदि नहीं, तो दूसरा हटाना सुरक्षित है, जब तक इस दौरान कोई नई स्मृति आवंटन नहीं किया जाता है?

मुझे आश्चर्य है कि मुझे पहले की समस्या क्यों पहचान नहीं मिली थी और केवल उन्मूलन यह है कि या तो वर्चुअल फ़ंक्शन तालिका को पहले डिलीट के दौरान तत्काल ओवरराइट किया जाता है या दूसरा डिलीट क्रैश नहीं होता है।

(पहला मतलब है कि "दौड़" होने पर दुर्घटना हमेशा एक ही स्थान पर होती है - दूसरा, आमतौर पर दौड़ होने पर कुछ भी नहीं होता है - और केवल तभी जब कोई तीसरा धागा डिलीट ऑब्जेक्ट को ओवरराइट करता है इस बीच समस्या तब होती है)


संपादित करें/अद्यतन:।

मैं एक परीक्षण, निम्नलिखित कोड एक segfault साथ दुर्घटनाओं था (जीसीसी 4.4, i686 और amd64):

012,351,
class M 
{ 
private: 
    int* ptr; 
public: 
    M() { 
    ptr = new int[1]; 
    } 
    virtual ~M() {delete ptr;} 
}; 

int main(int argc, char** argv) 
{ 
    M* ptr = new M(); 
    delete ptr; 
    delete ptr; 
} 

अगर मैं dtor से 'वर्चुअल' को हटा देता हूं, तो प्रोग्राम glibc द्वारा छोड़ा जाता है क्योंकि यह डबल-फ्री का पता लगाता है। 'वर्चुअल' के साथ क्रैश तब होता है जब अप्रत्यक्ष फ़ंक्शन को विनाशक को कॉल करते हैं, क्योंकि वर्चुअल फ़ंक्शन तालिका का सूचक अमान्य है।

दोनों amd64 और i686 पर पॉइंटर एक मान्य मेमोरी क्षेत्र (ढेर) को इंगित करता है, लेकिन मूल्य अमान्य है (काउंटर? यह बहुत कम है, उदाहरण के लिए 0x11, या 0x21) तो 'कॉल' (या 'jmp 'जब संकलक ने रिटर्न-ऑप्टिमाइज़ेशन किया था) एक अमान्य क्षेत्र में कूदता है।

कार्यक्रम संकेत SIGSEGV,

विभाजन गलती प्राप्त किया। 0x0000000000000021

में ??() (जीडीबी)

# 0x0000000000000021 में ??()

# 1 मुख्य() में 0x000000000040083e

तो ऊपर उल्लेख किया है शर्तों के साथ, आभासी समारोह मेज पर सूचक हमेशा द्वारा ओवरराइट है पहले, हटाने इसलिए अगले हटाने निर्वाण अगर पर चला जाएगा कक्षा में एक आभासी विनाशक है।

+0

ध्वनि भी –

+0

0A0D: यह मेरा prelimary समाधान (वैकल्पिक हल) है। असल में एक डिजाइन दोष था, क्योंकि यह निष्ठावान था कि वहां दो धागे हैं जो वस्तु को हटा सकते हैं। – IanH

उत्तर

6

यह स्मृति ऑलोकेटर के कार्यान्वयन पर बहुत निर्भर है, किसी ऑब्जेक्ट की वी-टेबल ओवरराइटिंग के रूप में किसी भी अनुप्रयोग निर्भर विफलताओं का उल्लेख न करें। कई मेमोरी आवंटन योजनाएं हैं जिनमें से सभी क्षमताओं और डबल मुक्त() के प्रतिरोध में भिन्न हैं, लेकिन इनमें से सभी एक आम संपत्ति साझा करते हैं: आपका आवेदन दूसरे मुक्त() के बाद कुछ समय में दुर्घटनाग्रस्त हो जाएगा।

दुर्घटना का कारण आमतौर पर स्मृति आवंटक कुछ क्रियान्वयन विशिष्ट विवरणों को संग्रहीत करने के लिए स्मृति के प्रत्येक आवंटित खंड (हेडर) से पहले और बाद में (पाद लेख) को समर्पित करता है। शीर्षलेख आमतौर पर अगले खंड के खंड और पते के आकार को परिभाषित करता है। पाद लेख आम तौर पर खंड के शीर्ष पर सूचक होता है। आम तौर पर कम से कम दो बार हटाने से जांच होती है कि आसन्न भाग मुक्त हैं या नहीं। इस प्रकार आपका प्रोग्राम क्रैश हो जाएगा यदि:

1) अगले खंड में पॉइंटर ओवरराइट किया गया है और दूसरा फ्री() अगले खंड तक पहुंचने का प्रयास करते समय segfault का कारण बनता है।

2) पिछले खंड के पाद लेख को संशोधित किया गया है और पिछले खंड के शीर्षलेख तक पहुंच segfault का कारण बनती है।

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

+0

हटाए गए तत्व के साथ क्या होता है, अगर हटाए जाने के बाद कोई और मॉलोक नहीं है? – IanH

+0

मैंने जीसीसी 4.4 के साथ कुछ परीक्षण किए: यह वास्तव में लगता है कि पहला डिलीट वर्चुअल फ़ंक्शन टेबल को ओवरराइट करता है, इसलिए क्रैश तब होता है जब आभासी विनाशक को दूसरी बार कहा जाता है। गैर-वर्चुअल विनाशक के साथ, ग्लिबैक डबल-फ्री का पता लगाता है और प्रोग्राम को बंद करता है। – IanH

+0

मैंने जो परीक्षण किया और मैंने अपने प्रश्न के परिणाम को जोड़ा। – IanH

6

दो बार कुछ हटाना अपरिभाषित व्यवहार है - आपको उससे आगे की कोई स्पष्टीकरण की आवश्यकता नहीं है, और आमतौर पर यह देखने के लिए फलहीन होता है। यह किसी प्रोग्राम को क्रैश करने का कारण बन सकता है, हो सकता है, लेकिन यह हमेशा एक बुरी चीज है और यह करने के बाद कार्यक्रम हमेशा अज्ञात स्थिति में होगा।

+2

हटाए गए ऑब्जेक्ट्स के साथ क्या हो सकता है और कोर डंप विश्लेषण में क्या मदद नहीं करता है इसके बारे में ज्ञान। – IanH

+0

@ इयान मैं पहले स्थान पर कोर डंप नहीं करना पसंद करता हूं। साथ ही, आप वास्तव में यह नहीं बता सकते कि क्या होगा जब तक कि आपके पास स्मृति आवंटन प्रणाली का अंतरंग ज्ञान न हो, जो कुछ लोग करते हैं, और जो संकलक से रिलीज होने के लिए रिलीज से अच्छी तरह से बदल सकते हैं। –

+0

मैं बिल्कुल बग नहीं करना पसंद करता हूं, लेकिन वे होते हैं - और इसलिए दुर्घटनाएं होती हैं। फिर यह जानना बहुत उपयोगी है कि क्या हो सकता है (और क्या हुआ इसके कारण क्या हो सकता है) और क्या नहीं। लंबी चल रही परियोजनाओं में आप आमतौर पर संकलक को अक्सर नहीं बदलते हैं। और उम्मीद है कि संकलक और इसका libc बिना ध्यान दिए स्मृति स्मृति आवंटक को बदल नहीं है। – IanH

1

delete को दो बार (या यहां तक ​​कि free) निष्पादित करके, स्मृति को पहले से ही आवंटित किया जा सकता है और delete निष्पादित करके फिर से स्मृति भ्रष्टाचार हो सकता है। आवंटित स्मृति के ब्लॉक का आकार अक्सर स्मृति ब्लॉक से पहले आयोजित किया जाता है।

यदि आपके पास व्युत्पन्न कक्षा है, तो व्युत्पन्न कक्षा (बच्चे) पर हटाएं कॉल न करें। यदि इसे वर्चुअल घोषित नहीं किया गया है तो केवल ~BaseClass() विनाशक को DerivedClass से किसी भी आवंटित स्मृति को जारी रखने और रिसाव करने के लिए कहा जाता है। यह मानता है कि DerivedClass में BaseClass के ऊपर और उससे अधिक आवंटित अतिरिक्त मेमोरी है जिसे मुक्त किया जाना चाहिए। है कि आपने कुछ म्युटेक्स या महत्वपूर्ण वर्गों में निवेश करने की जरूरत है

अर्थात

BaseClass* obj_ptr = new DerivedClass; // Allowed due to polymorphism. 
... 
delete obj_ptr; // this will call the destructor ~Parent() and NOT ~Child() 
+0

उपरोक्त मेरा अपडेट देखें: कम से कम एक नए जीसीसी के साथ एक बार डबल-डिलीट क्रैश हो जाता है, भले ही इस दौरान कोई नया/मॉलोक न हो। और मुझे कारण पता है कि विनाशकों को हमेशा वर्चुअल होने की आवश्यकता क्यों होती है। – IanH