विभाजन दोष (सेगमेंटेशन फॉल्ट)
कम्प्यूटिंग में, विभाजन दोष या एक्सेस उल्लंघन दोष (कंप्यूटिंग), या विफलता की स्थिति है, जो मेमोरी सुरक्षा के साथ हार्डवेयर द्वारा उठाया जाता है, ऑपरेटिंग प्रणाली (OS) को सूचित करता है, सॉफ्टवेयर ने प्रतिबंधित क्षेत्र तक पहुंचने का प्रयास किया है। मेमोरी ( मेमोरी एक्सेस उल्लंघन) मानक x86 कंप्यूटरों पर, यह सामान्य सुरक्षा दोष का रूप है। ऑपरेटिंग प्रणाली कर्नेल, प्रतिक्रिया में, सामान्यतः कुछ सुधारात्मक कार्रवाई करता है, सामान्यतः प्रक्रिया को संकेत (कंप्यूटिंग) भेजकर त्रुटि को आपत्तिजनक प्रक्रिया (कंप्यूटिंग) पर भेज देता है। प्रक्रियाएं कुछ स्थितियों में कस्टम सिग्नल हैंडलर स्थापित कर सकती हैं, जिससे उन्हें स्वयं ठीक होने की अनुमति मिलती है,[1] अन्यथा ओएस डिफ़ॉल्ट सिग्नल हैंडलर का उपयोग किया जाता है, सामान्यतः प्रक्रिया की असामान्य समाप्ति (प्रोग्राम क्रैश (कंप्यूटिंग)), एवं कभी-कभी कोर निपात होता है।
विभाजन दोष C (प्रोग्रामिंग भाषा) जैसी भाषाओं में लिखे गए प्रोग्रामों में त्रुटि का सामान्य वर्ग है जो निम्न-स्तरीय मेमोरी एक्सेस प्रदान करता है एवं कोई सुरक्षा निरिक्षण नहीं करता है। वे मुख्य रूप से आभासी मेमोरी एड्रेसिंग के लिए पॉइंटर (कंप्यूटर प्रोग्रामिंग) के उपयोग में त्रुटियों के कारण उत्पन्न होते हैं, विशेष रूप से अवैध पहुंच के कारण अन्य प्रकार की मेमोरी एक्सेस त्रुटि बस त्रुटि है, जिसके विभिन्न कारण भी हैं, किन्तु वर्तमान में यह अधिक दुर्लभ है; ये मुख्य रूप से गलत भौतिक मेमोरी एड्रेसिंग के कारण होते हैं, या गलत प्रविधि से मेमोरी एक्सेस के कारण होते हैं, ये ऐसे स्मृति संदर्भ होते हैं जिन्हें हार्डवेयर एड्रेस नहीं कर सकता है, उन रेफरेंस के जिन्हें एड्रेस करने की अनुमति नहीं होती है।
कई प्रोग्रामिंग भाषा विभाजन दोषों से बचने एवं मेमोरी सुरक्षा में सुधार के लिए चित्रित किए गए तंत्रों को नियोजित कर सकती हैं। उदाहरण के लिए, जंग (प्रोग्रामिंग भाषा) स्वामित्व आधारित को नियोजित करता है।[2] स्मृति सुरक्षा सुनिश्चित करने के लिए प्रतिरूप[3] अन्य भाषाएँ, जैसे कि लिस्प (प्रोग्रामिंग भाषा) एवं जावा (प्रोग्रामिंग भाषा), कचरा संग्रह (कंप्यूटर विज्ञान) को नियोजित करती हैं,[4] जो स्मृति त्रुटियों के कुछ वर्गों से बचती है, जिससे विभाजन दोष हो सकते हैं।[5]
सिंहावलोकन
विभाजन फॉल्ट तब होता है जब कोई प्रोग्राम स्मृति लोकेशन तक पहुंचने का प्रयास करता है जिसे एक्सेस करने की अनुमति नहीं है, या किसी मेमोरी लोकेशन को इस तरह एक्सेस करने का प्रयास करता है जिसकी अनुमति नहीं है (उदाहरण के लिए, केवल पढ़ने के लिये मेमोरी में लिखने का प्रयास | रीड-ओनली लोकेशन, या ऑपरेटिंग प्रणाली के हिस्से को ओवरराइट करने के लिए)।
शब्द विभाजन के कंप्यूटिंग में विभिन्न उपयोग हैं; विभाजन फॉल्ट के संदर्भ में, 1950 के दशक से इस्तेमाल किया जाने वाला शब्द,[citation needed] यह किसी प्रोग्राम के एड्रेस स्पेस को संदर्भित करता है।[6] स्मृति सुरक्षा के साथ, केवल प्रोग्राम का अपना पता स्थान ही पढ़ने योग्य होता है, एवं इसमें से केवल कॉल स्टैक एवं प्रोग्राम के डेटा सेगमेंट का रीड/राइट भाग लिखने योग्य होता है, जबकि रीड-ओनली डेटा एवं कोड खंड लिखने योग्य नहीं होते हैं। इस प्रकार प्रोग्राम के एड्रेस स्पेस के बाहर पढ़ने का प्रयास, या एड्रेस स्पेस के रीड-ओनली सेगमेंट में लिखने से विभाजन फॉल्ट होता है, इसलिए नाम।
वर्चुअल मेमोरी प्रदान करने के लिए हार्डवेयर स्मृति विभाजन का उपयोग करने वाले प्रणाली पर, विभाजन त्रुटि तब होती है जब हार्डवेयर गैर-मौजूद सेगमेंट को संदर्भित करने का प्रयास करता है, डेटा खंड की सीमाओं के बाहर किसी स्थान को संदर्भित करता है, या किसी स्थान को संदर्भित करता है। उस सेगमेंट के लिए दी गई अनुमतियों द्वारा अनुमत फैशन नहीं। केवल पेजिंग का उपयोग करने वाले प्रणाली पर, एक अमान्य पृष्ठ दोष सामान्यतः एक विभाजन दोष की ओर जाता है, एवं विभाजन दोष एवं पृष्ठ दोष दोनों ही वर्चुअल मेमोरी मैनेजमेंट प्रणाली द्वारा उठाए गए दोष हैं। विभाजन दोष पृष्ठ दोषों से स्वतंत्र रूप से भी हो सकते हैं: वैध पृष्ठ तक अवैध पहुंच एक विभाजन त्रुटि है, किन्तु अमान्य पृष्ठ त्रुटि नहीं है, एवं विभाजन दोष पृष्ठ के मध्य में हो सकते हैं (इसलिए कोई पृष्ठ त्रुटि नहीं है), उदाहरण के लिए बफ़र अधिकता जो एक पृष्ठ के भीतर रहता है किन्तु अवैध रूप से स्मृति को अधिलेखित कर देता है।
हार्डवेयर स्तर पर, स्मृति प्रबंधन इकाई (एमएमयू) द्वारा अवैध पहुंच (यदि संदर्भित मेमोरी मौजूद है) द्वारा इसकी स्मृति सुरक्षा सुविधा के भाग के रूप में, या एक अमान्य पृष्ठ दोष (यदि संदर्भित स्मृति मौजूद नहीं है) द्वारा शुरू में त्रुटि उठाई जाती है ). यदि समस्या एक अमान्य तार्किक पता नहीं है बल्कि एक अमान्य भौतिक पता है, तो इसके बजाय एक बस त्रुटि उठाई जाती है, हालांकि ये हमेशा अलग नहीं होते हैं।
ऑपरेटिंग प्रणाली के स्तर पर, यह त्रुटि पकड़ी जाती है एवं उस सिग्नल के लिए प्रक्रिया के हैंडलर को सक्रिय करते हुए, आपत्तिजनक प्रक्रिया को एक संकेत दिया जाता है। अलग-अलग ऑपरेटिंग प्रणाली में अलग-अलग सिग्नल नाम होते हैं जो इंगित करते हैं कि विभाजन त्रुटि हुई है। यूनिक्स जैसे ऑपरेटिंग प्रणाली पर, SIGSEGV नामक एक सिग्नल (विभाजन उल्लंघन से संक्षिप्त) को आपत्तिजनक प्रक्रिया के लिए भेजा जाता है। Microsoft Windows पर, आपत्तिजनक प्रक्रिया को एक STATUS_ACCESS_VIOLATION अपवाद प्रबंधन प्राप्त होता है।
कारण
जिन परिस्थितियों में विभाजन का उल्लंघन होता है एवं वे खुद को कैसे प्रकट करते हैं, वे हार्डवेयर एवं ऑपरेटिंग प्रणाली के लिए विशिष्ट हैं: अलग-अलग हार्डवेयर दी गई शर्तों के लिए अलग-अलग दोष पैदा करते हैं, एवं अलग-अलग ऑपरेटिंग प्रणाली इन्हें अलग-अलग सिग्नल में परिवर्तित करते हैं जो प्रक्रियाओं पर पारित होते हैं। निकटस्थ कारण एक मेमोरी एक्सेस उल्लंघन है, जबकि अंतर्निहित कारण सामान्यतः किसी प्रकार का सॉफ्टवेयर बग है। मूल कारण विश्लेषण का निर्धारण - बग को डिबग करना - कुछ मामलों में सरल हो सकता है, जहां प्रोग्राम लगातार विभाजन त्रुटि का कारण बनता है (उदाहरण के लिए, शून्य सूचक को संदर्भित करना), जबकि अन्य मामलों में बग को पुन: पेश करना एवं स्मृति पर निर्भर करना मुश्किल हो सकता है प्रत्येक रन पर आवंटन (उदाहरण के लिए, झूलने वाले सूचक को संदर्भित करना)।
विभाजन दोष के कुछ विशिष्ट कारण निम्नलिखित हैं:
- एक गैर-मौजूद स्मृति पते तक पहुँचने का प्रयास (प्रक्रिया के पता स्थान के बाहर)
- स्मृति तक पहुँचने का प्रयास कार्यक्रम के पास अधिकार नहीं है (जैसे कि प्रक्रिया के संदर्भ में कर्नेल संरचनाएँ)
- रीड-ओनली मेमोरी (जैसे कोड सेगमेंट) लिखने का प्रयास
ये बदले में प्रायः प्रोग्रामिंग त्रुटियों के कारण होते हैं, जिसके परिणामस्वरूप अमान्य मेमोरी एक्सेस होती है:
- एक अशक्त सूचक को संदर्भित करना, जो सामान्यतः एक ऐसे पते की ओर इशारा करता है जो प्रक्रिया के पता स्थान का हिस्सा नहीं है
- एक गैर-प्रारंभिक सूचक (जंगली सूचक, जो एक यादृच्छिक स्मृति पते को इंगित करता है) को संदर्भित या असाइन करना
- फ्रीड पॉइंटर को डिफ्रेंसिंग या असाइन करना (झूलने वाला पॉइंटर, जो उस मेमोरी को इंगित करता है जिसे मुक्त/डीललोकेटेड/डिलीट किया गया है)
- एक बफर अतिप्रवाह
- स्टैक ओवरफ़्लो
- ऐसे प्रोग्राम को निष्पादित करने का प्रयास करना जो सही ढंग से संकलित नहीं होता है। (कुछ संकलक[which?] संकलन-समय त्रुटियों की उपस्थिति के बावजूद निष्पादन योग्य फ़ाइल आउटपुट करेगा।)
सी कोड में, विभाजन दोष प्रायः पॉइंटर उपयोग में त्रुटियों के कारण होते हैं, खासकर सी गतिशील स्मृति आवंटन में। अशक्त सूचक को संदर्भित करना, जिसके परिणामस्वरूप अपरिभाषित व्यवहार होता है, सामान्यतः एक विभाजन दोष का कारण होगा। ऐसा इसलिए है क्योंकि एक शून्य सूचक वैध स्मृति पता नहीं हो सकता है। दूसरी ओर, वाइल्ड पॉइंटर्स एवं डैंगलिंग पॉइंटर्स उस मेमोरी की ओर इशारा करते हैं जो मौजूद हो भी सकती है एवं नहीं भी, एवं पढ़ने योग्य या लिखने योग्य हो भी सकती है एवं नहीं भी, एवं इस प्रकार क्षणिक बग का परिणाम हो सकता है। उदाहरण के लिए:
char *p1 = NULL; // Null pointer
char *p2; // Wild pointer: not initialized at all.
char *p3 = malloc(10 * sizeof(char)); // Initialized pointer to allocated memory
// (assuming malloc did not fail)
free(p3); // p3 is now a dangling pointer, as memory has been freed
इनमें से किसी भी वेरिएबल को डिफ्रेंस करने से विभाजन फॉल्ट हो सकता है: नल पॉइंटर को डीरेफर करने से सामान्यतः एक सेगफॉल्ट होता है, जबकि वाइल्ड पॉइंटर से पढ़ने के बजाय रैंडम डेटा हो सकता है, किन्तु कोई सेगफॉल्ट नहीं होता है, एवं डैंगलिंग पॉइंटर से पढ़ने के परिणामस्वरूप वैध डेटा हो सकता है। जबकि, एवं फिर यादृच्छिक डेटा, क्योंकि यह ओवरराइट किया गया है।
हैंडलिंग
विभाजन त्रुटि या बस त्रुटि के लिए डिफ़ॉल्ट क्रिया उस प्रक्रिया की असामान्य समाप्ति है जिसने इसे ट्रिगर किया। डिबगिंग में सहायता के लिए एक कोर फ़ाइल उत्पन्न की जा सकती है, एवं अन्य प्लेटफ़ॉर्म-निर्भर क्रियाएं भी की जा सकती हैं। उदाहरण के लिए, ग्रसिक्युरिटी पैच का उपयोग करने वाले लिनक्स प्रणाली बफर ओवरफ्लो का उपयोग करके संभावित घुसपैठ के प्रयासों की निगरानी के लिए SIGSEGV संकेतों को लॉग कर सकते हैं।
कुछ प्रणालियों पर, जैसे कि लिनक्स एवं विंडोज, यह संभव है कि कार्यक्रम स्वयं एक विभाजन दोष को संभाले।[7] आर्किटेक्चर एवं ऑपरेटिंग प्रणाली के आधार पर, रनिंग प्रोग्राम न केवल ईवेंट को हैंडल कर सकता है, बल्कि इसकी स्थिति के बारे में कुछ जानकारी निकाल सकता है जैसे स्टैक ट्रेस प्राप्त करना, प्रोसेसर रजिस्टर वैल्यू, स्रोत कोड की लाइन जब इसे ट्रिगर किया गया था, मेमोरी एड्रेस जो था अमान्य रूप से एक्सेस किया गया[8] एवं क्या कार्रवाई पढ़ी गई या लिखी गई थी।[9] हालांकि एक विभाजन त्रुटि का सामान्यतः मतलब है कि प्रोग्राम में एक बग है जिसे ठीक करने की आवश्यकता है, जानबूझकर ऐसी विफलता का कारण परीक्षण, डिबगिंग एवं प्लेटफॉर्म का अनुकरण करना भी संभव है जहां मेमोरी तक सीधी पहुंच की आवश्यकता है। बाद वाले मामले में, प्रणाली को त्रुटि होने के बाद भी प्रोग्राम को चलाने की अनुमति देने में सक्षम होना चाहिए। इस मामले में, जब प्रणाली अनुमति देता है, तो घटना को संभालना एवं निष्पादन जारी रखने के लिए असफल निर्देश पर कूदने के लिए प्रोसेसर प्रोग्राम काउंटर को बढ़ाना संभव है।[10]
उदाहरण
रीड ओनली मेमोरी में लिखना
रीड-ओनली मेमोरी में लिखने से विभाजन फॉल्ट होता है। कोड त्रुटियों के स्तर पर, यह तब होता है जब प्रोग्राम अपने स्वयं के कोड सेगमेंट या डेटा सेगमेंट के रीड-ओनली हिस्से को लिखता है, क्योंकि ये OS द्वारा रीड-ओनली मेमोरी में लोड किए जाते हैं।
यहां एएनएसआई सी कोड का एक उदाहरण दिया गया है जो सामान्यतः स्मृति सुरक्षा वाले प्लेटफॉर्म पर विभाजन त्रुटि का कारण बनता है। यह एक स्ट्रिंग शाब्दिक को संशोधित करने का प्रयास करता है, जो एएनएसआई सी मानक के अनुसार अपरिभाषित व्यवहार है। अधिकांश संकलक इसे संकलित समय पर नहीं पकड़ेंगे, एवं इसके बजाय इसे निष्पादन योग्य कोड में संकलित करेंगे जो क्रैश हो जाएगा:
int main(void)
{
char *s = "hello world";
*s = 'H';
}
जब इस कोड वाले प्रोग्राम को संकलित किया जाता है, तो स्ट्रिंग हैलो वर्ल्ड को प्रोग्राम एक्जीक्यूटेबल फ़ाइल के रोडाटा सेक्शन में रखा जाता है: डेटा सेगमेंट का रीड-ओनली सेक्शन। जब लोड किया जाता है, तो ऑपरेटिंग प्रणाली इसे अन्य स्ट्रिंग्स एवं निरंतर (प्रोग्रामिंग) डेटा के साथ मेमोरी के रीड-ओनली सेगमेंट में रखता है। जब निष्पादित किया जाता है, तो एक चर, s, स्ट्रिंग के स्थान को इंगित करने के लिए सेट किया जाता है, एवं चर के माध्यम से स्मृति में H वर्ण लिखने का प्रयास किया जाता है, जिससे विभाजन दोष उत्पन्न होता है। ऐसे प्रोग्राम को एक कंपाइलर के साथ संकलित करना जो संकलन समय पर केवल-पढ़ने के लिए स्थानों के असाइनमेंट की जांच नहीं करता है, एवं इसे यूनिक्स-जैसे ऑपरेटिंग प्रणाली पर चलाने से निम्न रनटाइम त्रुटि उत्पन्न होती है:
$ gcc segfault.c -g -o segfault
$ ./segfault
Segmentation fault
जीडीबी से कोर फ़ाइल का पश्व-अनुरेखन:
Program received signal SIGSEGV, Segmentation fault.
0x1c0005c2 in main () at segfault.c:6
6 *s = 'H';
इस कोड को वर्ण सूचक के बजाय सरणी का उपयोग करके ठीक किया जा सकता है, क्योंकि यह ढेर पर स्मृति आवंटित करता है एवं इसे स्ट्रिंग अक्षर के मान में प्रारंभ करता है:
char s[] = "hello world";
s[0] = 'H'; // equivalently, *s = 'H';
भले ही स्ट्रिंग अक्षर को संशोधित नहीं किया जाना चाहिए (यह सी मानक में अपरिभाषित व्यवहार है), सी में वे हैं static char []
प्रकार,[11][12][13] इसलिए मूल कोड में कोई निहित रूपांतरण नहीं है (जो इंगित करता है a char *
उस सरणी में), जबकि सी ++ में वे हैं static const char []
प्रकार, एवं इस प्रकार एक निहित रूपांतरण होता है, इसलिए संकलक सामान्यतः इस विशेष त्रुटि को पकड़ लेंगे।
अशक्त सूचक dereference
सी एवं सी-जैसी भाषाओं में, अशक्त संकेतकों का उपयोग बिना किसी वस्तु के संकेतक एवं एक त्रुटि संकेतक के रूप में किया जाता है, एवं एक अशक्त सूचक (एक अशक्त सूचक के माध्यम से पढ़ना या लिखना) को संदर्भित करना एक अधिक ही सामान्य कार्यक्रम त्रुटि है। सी मानक यह नहीं कहता है कि शून्य सूचक स्मृति पता 0 के सूचक के समान है, हालांकि व्यवहार में ऐसा हो सकता है। अधिकांश ऑपरेटिंग प्रणाली नल पॉइंटर के पते को इस तरह से मैप करते हैं कि इसे एक्सेस करने से विभाजन त्रुटि हो जाती है। सी मानक द्वारा इस व्यवहार की गारंटी नहीं है। एक अशक्त सूचक को संदर्भित करना C में अपरिभाषित व्यवहार है, एवं एक अनुरूप कार्यान्वयन को यह मानने की अनुमति है कि कोई भी सूचक जो अशक्त है, अशक्त नहीं है।
int *ptr = NULL;
printf("%d", *ptr);
यह नमूना कोड एक शून्य सूचक बनाता है, एवं फिर इसके मान तक पहुँचने का प्रयास करता है (मान पढ़ें)। ऐसा करने से कई ऑपरेटिंग प्रणाली पर रनटाइम में विभाजन त्रुटि हो जाती है।
एक अशक्त सूचक को हटाना एवं फिर उसे निर्दिष्ट करना (एक गैर-मौजूद लक्ष्य के लिए एक मान लिखना) भी सामान्यतः एक विभाजन दोष का कारण बनता है:
int *ptr = NULL;
*ptr = 1;
निम्नलिखित कोड में एक नल पॉइंटर डिरेफरेंस शामिल है, किन्तु जब संकलित किया जाता है तो प्रायः विभाजन त्रुटि नहीं होती है, क्योंकि मूल्य अप्रयुक्त होता है एवं इस प्रकार डीरेफरेंस को प्रायः मृत कोड उन्मूलन द्वारा अनुकूलित किया जाएगा:
int *ptr = NULL;
*ptr;
बफर अतिप्रवाह
निम्न कोड वर्ण सरणी तक पहुँचता है s
इसकी ऊपरी सीमा से परे। संकलक एवं प्रोसेसर के आधार पर, इसका परिणाम विभाजन दोष हो सकता है।
char s[] = "hello world";
char c = s[20];
ढेर अतिप्रवाह
आधार मामले के बिना एक एवं उदाहरण प्रत्यावर्तन है:
int main(void)
{
return main();
}
जो स्टैक ओवरफ़्लो का कारण बनता है जिसके परिणामस्वरूप विभाजन त्रुटि होती है।[14] भाषा, संकलक द्वारा किए गए अनुकूलन एवं कोड की सटीक संरचना के आधार पर अनंत पुनरावर्तन आवश्यक रूप से स्टैक ओवरफ़्लो का परिणाम नहीं हो सकता है। इस मामले में, अगम्य कोड (रिटर्न स्टेटमेंट) का व्यवहार अपरिभाषित है, इसलिए कंपाइलर इसे समाप्त कर सकता है एवं टेल कॉल ऑप्टिमाइज़ेशन का उपयोग कर सकता है जिसके परिणामस्वरूप कोई स्टैक उपयोग नहीं हो सकता है। अन्य अनुकूलन में पुनरावर्तन को पुनरावृत्ति में अनुवाद करना शामिल हो सकता है, जो उदाहरण फ़ंक्शन की संरचना को देखते हुए प्रोग्राम को हमेशा के लिए चलाने में परिणाम देगा, जबकि संभवतः इसके स्टैक को ओवरफ्लो नहीं करेगा।
यह भी देखें
- सामान्य संरक्षण त्रुटि
- भंडारण उल्लंघन
- गुरु ध्यान
संदर्भ
- ↑ Expert C programming: deep C secrets By Peter Van der Linden, page 188
- ↑ "The Rust Programming Language - Ownership".
- ↑ "Fearless Concurrency with Rust - The Rust Programming Language Blog".
- ↑ McCarthy, John (April 1960). "सांकेतिक भावों के पुनरावर्ती कार्य और मशीन द्वारा उनकी गणना, भाग I". Communications of the ACM. 4 (3): 184–195. doi:10.1145/367177.367199. S2CID 1489409. Retrieved 2018-09-22.
- ↑ Dhurjati, Dinakar; Kowshik, Sumant; Adve, Vikram; Lattner, Chris (1 January 2003). "मेमोरी सुरक्षा रनटाइम चेक या कचरा संग्रह के बिना" (PDF). Proceedings of the 2003 ACM SIGPLAN Conference on Language, Compiler, and Tool for Embedded Systems (in English). ACM. 38 (7): 69–80. doi:10.1145/780732.780743. ISBN 1581136471. S2CID 1459540. Retrieved 2018-09-22.
- ↑ "डिबगिंग विभाजन दोष और सूचक समस्याएं - Cprogramming.com". www.cprogramming.com. Retrieved 2021-02-03.
- ↑ "Cleanly recovering from Segfaults under Windows and Linux (32-bit, x86)". Retrieved 2020-08-23.
- ↑ "Implementation of the SIGSEGV/SIGABRT handler which prints the debug stack trace". GitHub. Retrieved 2020-08-23.
- ↑ "How to identify read or write operations of page fault when using sigaction handler on SIGSEGV?(LINUX)". Retrieved 2020-08-23.
- ↑ "LINUX – WRITING FAULT HANDLERS". Retrieved 2020-08-23.
- ↑ "6.1.4 String literals". ISO/IEC 9899:1990 - Programming languages -- C.
- ↑ "6.4.5 String literals". ISO/IEC 9899:1999 - Programming languages -- C.
- ↑ "6.4.5 String literals". ISO/IEC 9899:2011 - Programming languages -- C.
- ↑ What is the difference between a segmentation fault and a stack overflow? at Stack Overflow