2011-12-10 12 views
13

यहाँ मेरी कोड है,मैं SIGSEGV सिग्नल को अनदेखा क्यों नहीं कर सकता?

#include<signal.h> 
#include<stdio.h> 

int main(int argc,char ** argv) 
    { 
    char *p=NULL; 
    signal(SIGSEGV,SIG_IGN); //Ignoring the Signal 
    printf("%d",*p); 
    printf("Stack Overflow"); //This has to be printed. Right? 
    return 0; 
    } 

कोड को क्रियान्वित करते हैं, मैं विभाजन गलती हो रही है। मैंने SIG_IGN का उपयोग कर सिग्नल को अनदेखा कर दिया। तो मुझे सेगमेंटेशन गलती नहीं मिलनी चाहिए। सही? फिर, printf() प्रिंटिंग '* पी' मान के बाद कथन भी निष्पादित किया जाना चाहिए। सही?

+11

एक ऐसा समय होगा जिसमें सेगफाल्ट निगलने वाले लेखन कोड को प्रोग्रामर को जेल में रखने के लिए पर्याप्त माना जाएगा। – 6502

उत्तर

18

आपका कोड इसे पकड़ने के बजाय SIGSEGV को अनदेखा कर रहा है। याद रखें कि सिग्नल को संभालने के बाद सिग्नल ट्रिगर करने वाले निर्देश को पुनरारंभ किया जाता है। आपके मामले में, सिग्नल को संभालने से कुछ भी नहीं बदला गया है, अगली बार अपमानजनक निर्देश के चारों ओर प्रयास करने की कोशिश की जाती है, यह उसी तरह विफल हो जाती है।

आप इस

signal(SIGSEGV, sighandler); 

के संकेत परिवर्तन को पकड़ने के लिए इस

signal(SIGSEGV, SIG_IGN); 

इरादा है आप शायद भी sigaction() बजाय signal() उपयोग करना चाहिए। प्रासंगिक मैन पेज देखें।

आपके मामले में अपमानजनक निर्देश वह है जो नल पॉइंटर को कम करने की कोशिश करता है।

printf("%d", *p); 

आपके प्लेटफ़ॉर्म पर पूरी तरह से निर्भर है।

आप gdb का उपयोग यह निर्धारित करने के लिए कर सकते हैं कि कौन सा विशेष असेंबली निर्देश संकेत को ट्रिगर करता है।अपने मंच मेरा ऐसा कुछ है, तो आप पाएंगे अनुदेश Rax रजिस्टर मान 0 पकड़े, अर्थात NULL साथ

movl (%rax), %esi 

है। आपके सिग्नल हैंडलर में इसे ठीक करने के लिए एक (गैर-पोर्टेबल!) तरीका है कि आपके हैंडलर को तीसरे तर्क संकेत का उपयोग करना है, यानी उपयोगकर्ता संदर्भ।

#include <signal.h> 
#include <stdio.h> 

#define __USE_GNU 
#include <ucontext.h> 

int *p = NULL; 
int n = 100; 

void sighandler(int signo, siginfo_t *si, ucontext_t* context) 
{ 
    printf("Handler executed for signal %d\n", signo); 
    context->uc_mcontext.gregs[REG_RAX] = &n; 
} 

int main(int argc,char ** argv) 
{ 
    signal(SIGSEGV, sighandler); 
    printf("%d\n", *p); // ... movl (%rax), %esi ... 
    return 0; 
} 

इस कार्यक्रम को प्रदर्शित करता है:

Handler executed for signal 11 
100 

यह पहली बार का कारण बनता है हैंडलर एक शून्य पते भिन्नता के प्रयास के द्वारा निष्पादित किया जाना है यहाँ एक उदाहरण है। फिर हैंडलर n के पते पर रैक्स सेट करके समस्या को हल करता है। एक बार हैंडलर लौटने के बाद सिस्टम अपमानजनक निर्देश को पुनः प्राप्त करता है और इस बार सफल होता है। printf() अपने दूसरे तर्क के रूप में 100 प्राप्त करता है।

हालांकि, मैं आपके कार्यक्रमों में ऐसे गैर-पोर्टेबल समाधानों का उपयोग करने की दृढ़ता से अनुशंसा करता हूं।

+0

आपके विस्तृत स्पष्टीकरण के लिए धन्यवाद एडम :) – Dinesh

13

आप सिग्नल को अनदेखा कर सकते हैं लेकिन आपको इसके बारे में कुछ करना है। मेरा मानना ​​है कि आप पोस्ट किए गए कोड में क्या कर रहे हैं (SIGSEGVSIG_IGN के माध्यम से अनदेखा कर रहे हैं) बोल्ड बुलेट पढ़ने के बाद सभी कारणों के लिए पर काम नहीं करेगा।

जब आप कुछ है कि आप एक SIGSEGV भेजने के लिए कर्नेल का कारण बनता है कार्य करें:

  • आप एक संकेत हैंडलर नहीं है, तो, कर्नेल प्रक्रिया को मारता है और है कि उस
  • आप एक है है, तो है संकेत हैंडलर
    • आपका हैंडलर
    • बुलाया जाता गिरी पुन: प्रारंभ होता हमलावर आपरेशन

तो यदि आप इसे कुछ भी नहीं करते हैं, तो यह लगातार लूप होगा। आप पकड़ SIGSEGV कर और आप बाहर निकलने नहीं है, जिससे सामान्य प्रवाह के साथ दखल दे, तो आपको:

  • ठीक बातें इस तरह के अपमानजनक आपरेशन को पुनः आरंभ नहीं है कि या
  • स्मृति लेआउट इस तरह ठीक कि पर अगली रन
+0

ठीक है, तो मुझे बिल्कुल क्या करना है? – Dinesh

+0

@ दिनेश, यह क्या है कि आप इसे पूरा करने की कोशिश कर रहे हैं? – ibid

+0

@ibid, कोड में, मैं एक स्मृति शून्य स्मृति तक पहुंचने की कोशिश कर रहा हूं। इसलिए यह एसआईजीएसईजीवी सिग्नल के निर्माण की ओर जाता है। लेकिन मैंने इसके लिए एक हैंडलर बनाया जो सिर्फ "सिग्नल को पकड़ना" प्रिंट करेगा। बाद में बयान "वापसी 0" जो मुख्य() में रहता है निष्पादित करना होगा। सही? – Dinesh

9

एक अन्य विकल्प, setjmp/longjmp साथ जोखिम भरा आपरेशन ब्रैकेट करने के लिए है अर्थात

#include <setjmp.h> 
#include <signal.h> 

static jmp_buf jbuf; 
static void catch_segv() 
{ 
    longjmp(jbuf, 1); 
} 

int main() 
{ 
    int *p = NULL; 

    signal(SIGSEGV, catch_segv); 
    if (setjmp(jbuf) == 0) { 
     printf("%d\n", *p); 
    } else { 
     printf("Ouch! I crashed!\n"); 
    } 
    return 0; 
} 

setjmp/longjmp यहाँ पैटर्न एक आज़माएं/कैच ब्लॉक के समान है। हालांकि यह बहुत जोखिम भरा है, और यदि आपका जोखिम भरा कार्य ढेर को ओवररन्स करता है, या संसाधन आवंटित करता है लेकिन मुक्त होने से पहले दुर्घटनाग्रस्त हो जाता है। अपने पॉइंटर्स की जांच करना और बुरे लोगों के माध्यम से अप्रत्यक्ष नहीं होना बेहतर है।

+1

जहां तक ​​मैं यह कह सकता हूं कि यदि आप कई बार segfault में भाग लेते हैं (दूसरी बार प्रक्रिया अभी भी segfaults)। AFAIU 'longjmp' /' setjmp' सिग्नल संदर्भ को सही तरीके से संभाल नहीं करता है, और इसके बजाय 'sigsetjmp'/'siglongjmp' का उपयोग किया जाना चाहिए। Https://linux.die.net/man/2/setcontext में सीएफ "नोट्स" – mortenpi

0

उत्तर: आप पी सूचक को शून्य सौंपा

char *p = NULL; 

तो आप प्रिंट * p मूल्य:

printf("%d",*p); 

(मतलब है कि यह आप अशक्त पते पर स्थित मूल्य प्रिंट करना चाहते हैं यानी; 0 पता स्थान) जो संभव नहीं है। Thats क्यों यह विभाजन गलती दे रहा है। सेगमेंटेशन गलती स्मृति की पहुंच के मामले में होती है जो अभी प्रोग्राम के लिए उपलब्ध नहीं है।

आज़माएं:

char *p = (char*)malloc(sizeof(char)); 
*p = '\0'; 
printf("%d",*p); 

** नोट: ** इस तरह की त्रुटि सूचक त्रुटि झूलते के रूप में कहा जाता है। डांगलिंग पॉइंटर त्रुटि/अपवाद केस है जहां वाक्यविन्यास सही है लेकिन उपयोगकर्ता पॉइंटर से मूल्य तक पहुंचने का प्रयास करता है जो पहले से ही NULL को इंगित करता है।

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