सेगमेंट फॉल्ट अंडर-द-हूड कैसे काम करता है?


266

मैं "सीपीयू के एमएमयू सिग्नल भेजता है" और "कर्नेल इसे समाप्त करने के निर्देशन करता है," से इस पर कोई जानकारी नहीं मिल रही है।

मैंने यह मान लिया कि यह संभवतः शेल को सिग्नल भेजता है और शेल इसे अपमानजनक प्रक्रिया और मुद्रण को समाप्त करके संभालता है "Segmentation fault"। तो मैंने परीक्षण किया कि मैं एक अत्यंत न्यूनतम खोल लिखकर इस धारणा को क्रश (बकवास खोल) कहता हूं । यह शेल उपयोगकर्ता इनपुट लेने और system()विधि को खिलाने के अलावा कुछ नहीं करता है ।

#include <stdio.h>
#include <stdlib.h>

int main(){
    char cmdbuf[1000];
    while (1){
        printf("Crap Shell> ");
        fgets(cmdbuf, 1000, stdin);
        system(cmdbuf);
    }
}

इसलिए मैंने इस शेल को एक नंगे टर्मिनल में चलाया (बिना bashनीचे चलाए )। फिर मैं एक प्रोग्राम चलाने के लिए आगे बढ़ा जो एक segfault का उत्पादन करता है। अगर मेरी धारणा सही थी, तो यह या तो) क्रैश होगा crsh, xterm को बंद करके, b) प्रिंट नहीं "Segmentation fault", या c) दोनों।

braden@system ~/code/crsh/ $ xterm -e ./crsh
Crap Shell> ./segfault
Segmentation fault
Crap Shell> [still running]

मुझे लगता है कि वर्ग एक पर, मुझे लगता है। मैंने केवल यह प्रदर्शित किया है कि यह ऐसा शेल नहीं है जो ऐसा करता है, लेकिन सिस्टम नीचे है। "सेगमेंटेशन फाल्ट" भी कैसे छपता है? “यह कौन कर रहा है? गिरी? कुछ और? सिग्नल और इसके सभी साइड इफेक्ट्स हार्डवेयर से प्रोग्राम के अंतिम समाप्ति तक कैसे फैलते हैं?


43
crshइस तरह के प्रयोग के लिए एक महान विचार है। हम सभी को इसके बारे में और इसके पीछे के विचार के बारे में बताने के लिए धन्यवाद।
ब्रूस एडिगर

30
जब मैंने पहली बार देखा crsh, तो मुझे लगा कि इसका उच्चारण "दुर्घटना" होगा। मुझे यकीन नहीं है कि यह एक समान रूप से फिटिंग नाम है।
jpmc26

56
यह एक अच्छा प्रयोग है ... लेकिन आपको पता होना चाहिए कि system()हुड के तहत क्या होता है। यह पता चला है कि system()एक खोल प्रक्रिया होगी! तो आपकी शेल प्रक्रिया एक और शेल प्रक्रिया को जन्म देती है और वह शेल प्रक्रिया (शायद /bin/shया ऐसा कुछ) प्रोग्राम को चलाने वाली है। जिस तरह से /bin/shया bashकाम करता है fork()और exec()(या execve()परिवार में एक और कार्य ) है।
डायट्रिच एप

4
@ ब्रैडेनबेस्ट: बिल्कुल। मैनुअल पेज पढ़ें man 2 wait, इसमें मैक्रोज़ WIFSIGNALED()और शामिल होंगे WTERMSIG()
डायट्रिच एप

4
@DietrichEpp जैसा आपने कहा था! मैंने (WIFSIGNALED(status) && WTERMSIG(status) == 11)इसे कुछ नासमझ ( "YOU DUN GOOFED AND TRIGGERED A SEGFAULT") प्रिंट करने के लिए एक चेक जोड़ने की कोशिश की । जब मैंने segfaultकार्यक्रम को भीतर से चलाया crsh, तो यह ठीक उसी तरह छपा। इस बीच, सामान्य रूप से बाहर निकलने वाले कमांड त्रुटि संदेश का उत्पादन नहीं करते हैं।
ब्रैडेन बेस्ट

जवाबों:


248

सभी आधुनिक सीपीयू में वर्तमान में निष्पादित मशीन अनुदेश को बाधित करने की क्षमता है । वे पर्याप्त स्थिति को बचाते हैं (आमतौर पर, लेकिन हमेशा नहीं, स्टैक पर) बाद में निष्पादन को फिर से शुरू करना संभव बनाने के लिए , जैसे कि कुछ भी नहीं हुआ था (बाधित निर्देश को खरोंच से फिर से शुरू किया जाएगा, आमतौर पर)। फिर वे एक बाधा हैंडलर को निष्पादित करना शुरू करते हैं , जो सिर्फ अधिक मशीन कोड है, लेकिन एक विशेष स्थान पर रखा गया है ताकि सीपीयू को पता हो कि यह पहले से कहां है। बाधित हैंडलर हमेशा ऑपरेटिंग सिस्टम के कर्नेल का हिस्सा होते हैं : वह घटक जो सबसे बड़े विशेषाधिकार के साथ चलता है और अन्य सभी घटकों के निष्पादन की निगरानी के लिए जिम्मेदार होता है। 1,2

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

अब, अधिकांश आधुनिक ऑपरेटिंग सिस्टम में प्रक्रियाओं की धारणा है । इसके सबसे बुनियादी में, यह एक ऐसा तंत्र है जिससे कंप्यूटर एक ही समय में एक से अधिक प्रोग्राम चला सकता है, लेकिन यह भी एक प्रमुख पहलू है कि ऑपरेटिंग सिस्टम मेमोरी प्रोटेक्शन को कैसे कॉन्फ़िगर करते हैं , जो कि अधिकांश (लेकिन, अफसोस) की विशेषता है अभी भी सभी नहीं ) आधुनिक सीपीयू। यह वर्चुअल मेमोरी के साथ जाता है, जो रैम में मेमोरी एड्रेस और वास्तविक स्थानों के बीच मैपिंग को बदलने की क्षमता है। मैमोरी प्रोटेक्शन ऑपरेटिंग सिस्टम को प्रत्येक प्रक्रिया को रैम के अपने निजी हिस्से को देने की अनुमति देता है, कि केवल यह एक्सेस कर सकता है। यह ऑपरेटिंग सिस्टम (कुछ प्रक्रिया की ओर से अभिनय) को रैम के क्षेत्रों को पढ़ने के लिए नामित करने के लिए अनुमति देता है, केवल पढ़ने योग्य, निष्पादन योग्य, सहयोग करने वाली प्रक्रियाओं के समूह के बीच साझा किया जाता है, आदि में मेमोरी का एक हिस्सा भी होगा जो केवल द्वारा पहुँचा जा सकता है। गिरी। 3

जब तक प्रत्येक प्रक्रिया केवल उन तरीकों से मेमोरी तक पहुंचती है जो सीपीयू को अनुमति देने के लिए कॉन्फ़िगर किया गया है, स्मृति सुरक्षा अदृश्य है। जब एक प्रक्रिया नियमों को तोड़ती है, तो सीपीयू एक तुल्यकालिक व्यवधान उत्पन्न करेगा, जिससे कर्नेल को चीजों को छाँटने के लिए कहा जाएगा। यह नियमित रूप से होता है कि प्रक्रिया वास्तव में नियमों को नहीं तोड़ती है, प्रक्रिया को जारी रखने की अनुमति देने से पहले केवल कर्नेल को कुछ काम करने की आवश्यकता होती है। उदाहरण के लिए, यदि किसी प्रक्रिया की मेमोरी के किसी पृष्ठ को स्वैप फ़ाइल में "बेदखल" करने की आवश्यकता होती है, तो कुछ और के लिए रैम में जगह खाली करने के लिए, कर्नेल उस पृष्ठ को अप्राप्य चिह्नित करेगा। अगली बार जब प्रक्रिया इसका उपयोग करने की कोशिश करेगी, तो सीपीयू एक मेमोरी-सुरक्षा व्यवधान उत्पन्न करेगा; कर्नेल स्वैप से पृष्ठ को फिर से प्राप्त करेगा, इसे वापस वहीं रखें जहां यह था, इसे फिर से चिह्नित करें, और निष्पादन को फिर से शुरू करें।

लेकिन मान लीजिए कि इस प्रक्रिया ने वास्तव में नियमों को तोड़ दिया। इसने एक ऐसे पेज को एक्सेस करने की कोशिश की, जिसमें कभी भी कोई रैम मैप नहीं की गई हो, या उसने किसी ऐसे पेज को अंजाम देने की कोशिश की हो, जो मशीन कोड या नहीं के रूप में चिह्नित हो। ऑपरेटिंग सिस्टम के परिवार को आमतौर पर "यूनिक्स" के रूप में जाना जाता है, सभी इस स्थिति से निपटने के लिए संकेतों का उपयोग करते हैं4 सिग्नल इंटरप्ट के समान होते हैं, लेकिन वे कर्नेल द्वारा उत्पन्न होते हैं और प्रक्रियाओं द्वारा फील्ड किए जाते हैं, बजाय हार्डवेयर द्वारा उत्पन्न किए और कर्नेल द्वारा फील्ड किए जाते हैं। प्रक्रियाएं सिग्नल हैंडलर को परिभाषित कर सकती हैंअपने कोड में, और कर्नेल को बताएं कि वे कहां हैं। उन सिग्नल हैंडलर को तब नियंत्रित किया जाएगा, जब आवश्यक हो, नियंत्रण के सामान्य प्रवाह को बाधित करें। सिग्नल सभी में एक नंबर और दो नाम होते हैं, जिनमें से एक क्रिप्टोकरंसी और दूसरा थोड़ा कम क्रिप्टोकरंसी होता है। जब एक प्रक्रिया स्मृति-सुरक्षा नियमों को तोड़ती है तो संकेत उत्पन्न होता है (कन्वेंशन द्वारा) संख्या 11, और इसके नाम हैं SIGSEGVऔर "विभाजन दोष"। 5,6

सिग्नल और इंटरप्ट के बीच एक महत्वपूर्ण अंतर यह है कि हर सिग्नल के लिए एक डिफ़ॉल्ट व्यवहार होता है। यदि ऑपरेटिंग सिस्टम सभी अवरोधों के लिए हैंडलर को परिभाषित करने में विफल रहता है, तो यह ओएस में एक बग है, और सीपीयू एक लापता हैंडलर को लागू करने की कोशिश करने पर पूरा कंप्यूटर क्रैश हो जाएगा। लेकिन सभी सिग्नल के लिए सिग्नल हैंडलर को परिभाषित करने के लिए प्रक्रियाएं किसी दायित्व के तहत नहीं हैं। यदि कर्नेल किसी प्रक्रिया के लिए एक संकेत उत्पन्न करता है, और वह संकेत अपने डिफ़ॉल्ट व्यवहार पर छोड़ दिया गया है, तो कर्नेल बस आगे बढ़ेगा और जो कुछ भी डिफ़ॉल्ट है और प्रक्रिया को परेशान नहीं करेगा। अधिकांश संकेतों के डिफ़ॉल्ट व्यवहार या तो "कुछ नहीं करते हैं" या "इस प्रक्रिया को समाप्त करते हैं और शायद एक कोर डंप का उत्पादन भी करते हैं।" SIGSEGVउत्तरार्द्ध में से एक है।

इसलिए, पुनरावृत्ति के लिए, हमारे पास एक प्रक्रिया है जिसने मेमोरी-सुरक्षा नियमों को तोड़ दिया है। सीपीयू ने प्रक्रिया को निलंबित कर दिया और एक तुल्यकालिक व्यवधान उत्पन्न किया। कर्नेल ने उस रुकावट को उत्पन्न SIGSEGVकिया और प्रक्रिया के लिए एक संकेत उत्पन्न किया । मान लेते हैं कि प्रक्रिया ने सिग्नल हैंडलर स्थापित नहीं किया है SIGSEGV, इसलिए कर्नेल डिफ़ॉल्ट व्यवहार करता है, जो प्रक्रिया को समाप्त करना है। _exitसिस्टम कॉल के रूप में इसका सभी समान प्रभाव पड़ता है : खुली फाइलें बंद हो जाती हैं, मेमोरी डीलॉक्लेटेड होती है, आदि।

इस बिंदु तक कुछ भी किसी भी संदेश को मुद्रित नहीं किया गया है जिसे एक मानव देख सकता है, और शेल (या, आमतौर पर, इस प्रक्रिया की मूल प्रक्रिया जो अभी समाप्त हो गई है) बिल्कुल भी शामिल नहीं है। SIGSEGVउस प्रक्रिया में जाता है जिसने नियमों को तोड़ा, उसके माता-पिता को नहींअगले अनुक्रम में कदम है, हालांकि, माता पिता प्रक्रिया है कि अपने बच्चे को समाप्त कर दिया गया सूचित करने के लिए है। यह कई अलग अलग तरीकों, जिनमें से सबसे सरल है, जब माता-पिता पहले से ही इस अधिसूचना के लिए इंतजार कर रहा है, में से एक का उपयोग करने में भी हो सकता है waitसिस्टम कॉल ( wait, waitpid, wait4, आदि)। उस स्थिति में, कर्नेल सिर्फ उस सिस्टम कॉल को वापस करने का कारण बनेगा, और पेरेंट प्रक्रिया को एक कोड संख्या के साथ आपूर्ति करेगा, जिसे एक्ज़िट स्टेटस कहा जाता है7 बाहर निकलने की स्थिति माता-पिता को सूचित करती है कि बच्चे की प्रक्रिया क्यों समाप्त की गई; इस मामले में, यह सीखेगा कि SIGSEGVसंकेत के डिफ़ॉल्ट व्यवहार के कारण बच्चे को समाप्त कर दिया गया था ।

माता-पिता की प्रक्रिया तब संदेश को मुद्रित करके मानव को घटना की सूचना दे सकती है; शेल प्रोग्राम लगभग हमेशा ऐसा करते हैं। आपके crshपास ऐसा करने के लिए कोड शामिल नहीं है, लेकिन यह वैसे भी होता है, क्योंकि सी लाइब्रेरी की दिनचर्या systemपूर्ण रूप से चित्रित शेल /bin/sh, "हूड के तहत" चलती है । crshहै दादा-दादी इस परिदृश्य में, पेरेंट-प्रोसेस नोटिफिकेशन फ़ील्ड द्वारा दिया गया है /bin/sh, जो इसके सामान्य संदेश को प्रिंट करता है। उसके बाद /bin/shही बाहर निकलता है, क्योंकि इसके पास करने के लिए अधिक कुछ नहीं है, और सी लाइब्रेरी के कार्यान्वयन से उस निकास अधिसूचना को systemप्राप्त होता है । आप अपने कोड में उस निकास सूचना को देख सकते हैं, के रिटर्न मान का निरीक्षण करकेsystem; लेकिन यह आपको यह नहीं बताएगा कि पोते की प्रक्रिया एक सेगफॉल्ट पर मृत्यु हो गई, क्योंकि वह मध्यवर्ती शेल प्रक्रिया द्वारा भस्म हो गई थी।


फुटनोट

  1. कुछ ऑपरेटिंग सिस्टम कर्नेल के हिस्से के रूप में डिवाइस ड्राइवरों को लागू नहीं करते हैं ; हालांकि, सभी को बाधित संचालकों अभी भी गिरी का हिस्सा बनने के लिए है, और क्योंकि हार्डवेयर कुछ भी अनुमति नहीं देता है तो, कोड है कि स्मृति संरक्षण कॉन्फ़िगर करता है लेकिन इन कार्यों को गिरी।

  2. एक "हाइपरवाइज़र" या "वर्चुअल मशीन मैनेजर" नामक एक कार्यक्रम हो सकता है जो कर्नेल की तुलना में अधिक विशेषाधिकार प्राप्त है, लेकिन इस उत्तर के प्रयोजनों के लिए इसे हार्डवेयर का हिस्सा माना जा सकता है ।

  3. कर्नेल एक कार्यक्रम है , लेकिन यह एक प्रक्रिया नहीं है; यह पुस्तकालय जैसा है। सभी प्रक्रियाएं कर्नेल कोड के कुछ हिस्सों को समय-समय पर अपने कोड के अलावा निष्पादित करती हैं। कई "कर्नेल थ्रेड्स" हो सकते हैं जो केवल कर्नेल कोड निष्पादित करते हैं, लेकिन वे हमें यहां चिंता नहीं करते हैं।

  4. एक और केवल OS की संभावना है कि आप अब और यूनिक्स के कार्यान्वयन पर विचार नहीं कर सकते, जो कि विंडोज से संबंधित है। यह इस स्थिति में संकेतों का उपयोग नहीं करता है। (वास्तव में, इसमें संकेत नहीं हैं । विंडोज पर <signal.h>इंटरफ़ेस पूरी तरह से सी लाइब्रेरी द्वारा फेक है।) इसके बजाय " संरचित अपवाद हैंडलिंग " नामक कुछ का उपयोग करता है ।

  5. कुछ स्मृति-सुरक्षा उल्लंघन उत्पन्न होते हैं SIGBUS("बस त्रुटि") के बजाय SIGSEGV। दोनों के बीच की रेखा अंडरस्क्राइब है और सिस्टम से सिस्टम में भिन्न होती है। यदि आपने एक प्रोग्राम लिखा है SIGSEGV, जो हैंडलर को परिभाषित करता है , तो संभवतः उसी हैंडलर को परिभाषित करना एक अच्छा विचार है SIGBUS

  6. "सेगमेंटेशन फॉल्ट" मूल यूनिक्स को चलाने वाले कंप्यूटरों में से एक , संभवतः पीडीपी -11 द्वारा मेमोरी-प्रोटेक्शन उल्लंघन के लिए उत्पन्न बाधा का नाम था । " सेगमेंटेशन " एक प्रकार का मेमोरी प्रोटेक्शन है, लेकिन आजकल "सेगमेंटेशन फ़ॉल्ट " शब्द किसी भी प्रकार के मेमोरी प्रोटेक्शन उल्लंघन को संदर्भित करता है।

  7. अन्य सभी तरीकों से माता-पिता की प्रक्रिया को समाप्त होने वाले बच्चे के बारे में सूचित किया जा सकता है, माता-पिता को कॉल waitकरने और बाहर निकलने की स्थिति प्राप्त करने के साथ समाप्त हो सकता है । यह सिर्फ इतना है कि पहले कुछ और होता है।


@zvol: ad 2) मुझे नहीं लगता कि यह कहना सही है कि सीपीयू प्रक्रियाओं के बारे में कुछ भी जानता है। आपको कहना चाहिए कि यह एक बाधा हैंडलर को आमंत्रित करता है, जो नियंत्रण स्थानांतरित करता है।
user323094

9
@ user323094 आधुनिक मल्टीकोर सीपीयू वास्तव में प्रक्रियाओं के बारे में काफी कुछ जानते हैं; बस इतना है कि, इस स्थिति में, वे केवल निष्पादन के धागे को निलंबित कर सकते हैं जिसने स्मृति-सुरक्षा दोष को ट्रिगर किया है। इसके अलावा, मैं कोशिश कर रहा था कि निम्न-स्तरीय विवरण में न जाऊं। उपयोगकर्ता-अंतरिक्ष प्रोग्रामर के दृष्टिकोण से, चरण 2 के बारे में समझने के लिए सबसे महत्वपूर्ण बात यह है कि यह हार्डवेयर है जो मेमोरी सुरक्षा के उल्लंघन का पता लगाता है; हार्डवेयर, फ़र्मवेयर, और ऑपरेटिंग सिस्टम के बीच श्रम का सटीक विभाजन कम होने पर "ऑफ़ेंडिंग प्रोसेस" को पहचानने की बात आती है।
zwol

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

2
SIGBUS (बस त्रुटि) और SIGSEGV (विभाजन दोष) के बीच महत्वपूर्ण अंतर यह है: SIGSEGV तब होता है जब CPU जानता है कि आपको किसी पते तक नहीं पहुंचना चाहिए (और इसलिए यह कोई बाहरी मेमोरी बस अनुरोध नहीं करता है)। SIGBUS तब होता है जब CPU केवल पते की समस्या के बारे में पता लगाता है, जब उसने अपना अनुरोध अपने बाहरी पते की बस पर रखा होता है। उदाहरण के लिए, एक भौतिक पते की मांग करना, जिस पर बस का कोई भी जवाब नहीं देता है, या एक गलत संरेखित सीमा पर डेटा पढ़ने के लिए कह रहा है (जिसे एक के बजाय दो भौतिक अनुरोधों की आवश्यकता होगी)
स्टुअर्ट कैई

2
@StuartCaie आप व्यवधान के व्यवहार का वर्णन कर रहे हैं ; वास्तव में, कई सीपीयू आप को रेखांकित करते हैं (हालांकि कुछ नहीं करते हैं, और दोनों के बीच की रेखा भिन्न होती है)। संकेत SIGSEGV और SIGBUS, तथापि, कर रहे हैं नहीं मज़बूती से उन दो सीपीयू स्तर की स्थिति को मैप किया। एकमात्र शर्त जहां POSIX को SIGSEGV के बजाय SIGBUS की आवश्यकता होती है, जब आप mmapकिसी फ़ाइल को मेमोरी क्षेत्र में रखते हैं जो फ़ाइल से बड़ा है, और फिर फ़ाइल के अंत से परे "पूरे पृष्ठ" तक पहुंचें। (POSIX अन्यथा काफी अस्पष्ट है, जब SIGSEGV / SIGBUS / SIGILL / etc होता है।)
zwol

42

शेल का वास्तव में उस संदेश के साथ कुछ करना है, और crshअप्रत्यक्ष रूप से एक शेल को कॉल करता है, जो संभवतः है bash

मैंने एक छोटा सी प्रोग्राम लिखा था जिसमें हमेशा दोष होते थे:

#include <stdio.h>

int
main(int ac, char **av)
{
        int *i = NULL;

        *i = 12;

        return 0;
}

जब मैं इसे अपने डिफ़ॉल्ट शेल से चलाता हूं, तो zsh, मुझे यह मिल जाता है:

4 % ./segv
zsh: 13512 segmentation fault  ./segv

जब मैं इसे चलाता हूं bash, तो मुझे वह मिलता है जो आपने अपने प्रश्न में दिया था:

bediger@flq123:csrc % ./segv
Segmentation fault

मैं अपने कोड में एक सिग्नल हैंडलर लिखने जा रहा था, तब मुझे एहसास हुआ कि निष्पादन के लिए एक शेल system()द्वारा इस्तेमाल की जाने वाली लाइब्रेरी कॉल crsh, /bin/shतदनुसार man 3 system। यह /bin/shलगभग निश्चित रूप से "सेगमेंटेशन फॉल्ट" को प्रिंट कर रहा है, क्योंकि crshनिश्चित रूप से नहीं है।

यदि आप प्रोग्राम को चलाने के लिए सिस्टम कॉल crshका उपयोग करने के लिए फिर से लिखते हैं execve(), तो आपको "विभाजन दोष" स्ट्रिंग नहीं दिखाई देगा। यह द्वारा आहूत शेल से आता है system()


5
मैं सिर्फ डिट्रीच एप के साथ इस पर चर्चा कर रहा था। मैंने एक साथ crsh का एक संस्करण हैकexecvp किया, जो उपयोग करता है और फिर से परीक्षण किया कि यह पता लगाने के लिए कि शेल अभी भी क्रैश नहीं करता है (जिसका अर्थ है कि SIGSEGV को कभी भी शेल में नहीं भेजा जाता है), यह "सेगमेंटेशन फ़ॉल्ट" को प्रिंट नहीं करता है । कुछ भी नहीं छपा है। यह इंगित करने के लिए प्रतीत होता है कि शेल पता लगाता है कि इसकी बच्चे की प्रक्रियाएं कब खत्म हो जाती हैं और "सेगमेंटेशन फॉल्ट" (या इसके कुछ संस्करण) को प्रिंट करने के लिए जिम्मेदार है।
ब्रैडेन बेस्ट

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

21

मैं "सीपीयू के एमएमयू सिग्नल भेजता है" और "कर्नेल इसे समाप्त करने के निर्देशन करता है," से इस पर कोई जानकारी नहीं मिल रही है।

यह एक गढ़ा हुआ सारांश है। यूनिक्स सिग्नल तंत्र सीपीयू-विशिष्ट घटनाओं से पूरी तरह से अलग है जो प्रक्रिया शुरू करते हैं।

सामान्य तौर पर, जब एक खराब पता एक्सेस किया जाता है (या एक रीड-ओनली एरिया में लिखा जाता है, तो एक गैर-निष्पादन योग्य सेक्शन को निष्पादित करने का प्रयास किया जाता है, आदि), सीपीयू कुछ सीपीयू-विशिष्ट ईवेंट (पारंपरिक गैर-वीएम आर्किटेक्चर पर) उत्पन्न करेगा। एक विभाजन उल्लंघन कहा जाता है, क्योंकि प्रत्येक "खंड" (परंपरागत रूप से, केवल पढ़ने योग्य निष्पादन योग्य "पाठ", लिखने योग्य और परिवर्तनशील लंबाई "डेटा", और पारंपरिक रूप से स्मृति के विपरीत छोर पर स्टैक) के पते की एक निश्चित सीमा होती है - आधुनिक वास्तुकला पर यह पृष्ठ दोष [अनमैप मेमोरी के लिए] या एक्सेस उल्लंघन [पढ़ने, लिखने और अनुमति के मुद्दों को निष्पादित करने के लिए] होने की अधिक संभावना है, और मैं बाकी उत्तर के लिए इस पर ध्यान केंद्रित करूंगा)।

अब, इस बिंदु पर, कर्नेल कई काम कर सकता है। पृष्ठ दोष भी मेमोरी के लिए उत्पन्न होते हैं जो मान्य है लेकिन लोड नहीं किया गया है (जैसे कि स्वैप किया गया, या मिमीडैप फ़ाइल में, आदि), और इस स्थिति में कर्नेल मेमोरी को मैप करेगा और फिर उस अनुदेश से उपयोगकर्ता प्रोग्राम को पुनरारंभ करेगा जो कारण हुआ त्रुटि। अन्यथा, यह एक संकेत भेजता है। यह बिल्कुल "प्रत्यक्ष [मूल घटना] अपमानजनक कार्यक्रम के लिए" नहीं है, क्योंकि सिग्नल हैंडलर स्थापित करने की प्रक्रिया अलग है और ज्यादातर वास्तुकला-स्वतंत्र, बनाम यदि प्रोग्राम को एक बाधा हैंडलर स्थापित करने की उम्मीद थी।

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

यदि उपयोगकर्ता प्रोग्राम में सिग्नल हैंडलर स्थापित नहीं है, तो इसे बस समाप्त कर दिया जाता है। कुछ आर्किटेक्चर पर, यदि सिग्नल को नजरअंदाज किया जाता है, तो यह निर्देश को बार-बार फिर से शुरू कर सकता है, जिससे एक अनंत लूप हो सकता है।


+1, केवल एक उत्तर है जो स्वीकृत किसी भी चीज़ को जोड़ता है। "विभाजन" इतिहास का अच्छा वर्णन। मजेदार तथ्य: x86 वास्तव में अभी भी 32bit संरक्षित मोड में खंड सीमाएँ हैं (पेजिंग या वर्चुअल मेमोरी के साथ या बिना सक्षम), इसलिए निर्देश जो एक्सेस मेमोरी उत्पन्न कर सकते हैं #PF(fault-code)(पेज फॉल्ट) या #GP(0)("यदि कोई मेमोरी ऑपरेंड प्रभावी पता CS के बाहर है,) DS, ES, FS या GS खंड सीमा। ")। 64 बिट मोड सेगमेंट लिमिट की जांच को रोक देता है, क्योंकि OSes ने इसके बजाय पेजिंग का उपयोग किया है, और उपयोगकर्ता-स्पेस के लिए एक फ्लैट मेमोरी मॉडल।
पीटर कॉर्डेस

वास्तव में, मेरा मानना ​​है कि x86 पर अधिकांश OSes खंडित पृष्ठांकन का उपयोग करते हैं: एक फ्लैट, पृष्ठांकित पते स्थान के अंदर बड़े खंडों का एक समूह। यह है कि आप प्रत्येक एड्रेस स्पेस में कर्नेल मेमोरी की सुरक्षा और मैप करते हैं: रिंग्स (सुरक्षा स्तर) सेगमेंट से जुड़े होते हैं, पेज नहीं
Lorenzo Dematté

इसके अलावा, NT पर (लेकिन मुझे यह जानना अच्छा लगेगा कि क्या अधिकांश यूनिक्स एक ही है!) "विभाजन दोष" काफी बार हो सकता है: उपयोगकर्ता स्थान की शुरुआत में 64k संरक्षित खंड है, इसलिए एक पूर्ण संकेत को dereferencing करता है (उचित?) विभाजन का दोष
लोरेंजो डेमेटे

1
@ LorenzoDematté हाँ, ऑल-ऑर-ऑल-ऑल-ऑल-ऑल-मॉडर्न यूनिक्स, NULL डेरीफेर को पकड़ने के लिए एड्रेस स्पेस की शुरुआत में स्थायी रूप से अनमैक्ड एड्रेस का एक हिस्सा छोड़ देंगे। यह काफी बड़ा हो सकता है - 64-बिट सिस्टम पर, वास्तव में, यह चार गीगाबाइट हो सकता है , जिससे कि 32 बिट्स को पॉइंटर्स के आकस्मिक ट्रंकेशन को तुरंत पकड़ा जाएगा। हालांकि, सख्त x86 अर्थ में विभाजन का उपयोग मुश्किल से ही किया जाता है; उपयोगकर्ता स्थान के लिए एक और कर्नेल के लिए एक फ्लैट खंड है, और शायद विशेष चाल के लिए एक जोड़े जैसे एफएस और जीएस से कुछ उपयोग हो रहा है।
zwol

1
@ LorenzoDematté NT संकेतों के बजाय अपवादों का उपयोग करता है; इस स्थिति में STATUS_ACCESS_VIOLATION
रैंडम 832

18

एक विभाजन दोष एक मेमोरी एड्रेस तक पहुंच है जिसे अनुमति नहीं है (प्रक्रिया का हिस्सा नहीं है, या केवल पढ़ने के लिए डेटा लिखने की कोशिश कर रहा है, या गैर-निष्पादन योग्य डेटा, निष्पादित करता है ...)। यह MMU (मेमोरी मैनेजमेंट यूनिट, सीपीयू का आज का हिस्सा) द्वारा पकड़ा जाता है, जिससे एक व्यवधान उत्पन्न होता है। इंटरप्ट को कर्नेल द्वारा नियंत्रित किया जाता है, जो आपत्तिजनक प्रक्रिया के लिए एक SIGSEGFAULTसंकेत ( signal(2)उदाहरण के लिए देखें ) भेजता है । इस सिग्नल के लिए डिफ़ॉल्ट हैंडलर कोर (देखें core(5)) को डंप करता है और प्रक्रिया को समाप्त करता है।

खोल का इसमें कोई हाथ नहीं है।


3
तो आपकी सी लाइब्रेरी, जैसे डेस्कटॉप पर ग्लिबक , स्ट्रिंग को परिभाषित करता है?
drewbenn

7
यह भी ध्यान देने योग्य है कि SIGSEGV को संभाला / अनदेखा किया जा सकता है। इसलिए ऐसा प्रोग्राम लिखना संभव है जो इसके द्वारा समाप्त न हो। जावा वर्चुअल मशीन एक उल्लेखनीय उदाहरण है जो विभिन्न प्रयोजनों के लिए आंतरिक रूप से SIGSEGV का उपयोग करता है, जैसा कि यहां उल्लेख किया गया है: stackoverflow.com/questions/3731784/…
करोल नोक

2
इसी तरह, विंडोज पर, .NET ज्यादातर मामलों में अशक्त सूचक चेक जोड़ने से परेशान नहीं है - यह सिर्फ एक्सेस उल्लंघन (segfaults के बराबर) को पकड़ता है।
इबिबिस
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.