سؤال

لدي برنامج أن Segfaults من الحساب المؤشر في بعض الأحيان. أعرف أن هذا يحدث، لكن لا يمكنني التحقق من ذلك بسهولة في الوقت المناسب لمعرفة ما إذا كان Segfachachens أم لا - إما أستطيع "مسح ما قبل المسح الضوئي" لمعرفة ما إذا كان سيؤدي ذلك إلى أن يسبب Segfault (يمكن أن يكون من المستحيل تحديده)، أو يمكنني إعادة مجددها لعدم استخدام حساب مؤشر، الأمر الذي يتطلب قدرا أكبر بكثير من العمل، أو يمكنني محاولة التقاط سيغفو. لذلك سؤالي:

1) كيف، في ج، هل يمكنني التقاط سيغافو؟ أنا أعرف شيئا ما في نظام التشغيل، يسبب Segfault، ولكن ما الذي يمكن أن يفعله برنامج C في الحدث الذي يقوم به Segfaults للموت أكثر قليلا من مجرد Segmentation fault?

2) كم هو محمول هذا؟

أتصور أن هذا سلوك غير قابل للجدل، لذلك إذا قمت بنشر أي رمز للقبض على segfault، فالرجاء أن تخبرني بما يعمل عليه. أنا على Mac OS X ولكنني أحب برنامجي للعمل على أكبر عدد ممكن من المنصات وأريد أن أرى ما هي خياراتي.

ولا تقلق - أساسا كل ما أريد القيام به هو طباعة رسالة خطأ سهلة الاستخدام وغيرها مجانا malloc()ed الذاكرة، ثم تموت. أنا لا أخطط فقط لتجاهل كل Segfaults أحصل عليه والحرث.

هل كانت مفيدة؟

المحلول

عليك تحديد معالج إشارة. يتم ذلك على أنظمة UNIX باستخدام الوظيفة sigaction. وبعد لقد قمت بذلك بنفس الرمز على Fedora 64 و 32 بت، وعلى Sun Solaris.

نصائح أخرى

حسنا، SIGSEGV هو thrapable، وهذا هو posix، لذلك هو المحمولة بهذا المعنى.

بو أنا قلق من أن يبدو أنك تريد التعامل مع Segfault بدلا من إصلاح المشكلة التي تسبب segfault. إذا اضطررت إلى اختيار ما إذا كان نظام التشغيل عند الخطأ، أو الرمز الخاص بي، فأنا أعرف أنني سأختار. أقترح عليك اصطياد هذا الخطأ، إصلاحه، ثم اكتب حالة اختبار للتأكد من أنه لا يعضك مرة أخرى.

يمكنك استخدام الوظيفة الإشارة لتثبيت معالج إشارة جديد للإشارة:

   #include <signal.h>
   void (*signal(int signum, void (*sighandler)(int)))(int);

شيء مثل الكود التالي:

signal(SIGINT , clean_exit_on_sig);
signal(SIGABRT , clean_exit_on_sig);
signal(SIGILL , clean_exit_on_sig);
signal(SIGFPE , clean_exit_on_sig);
signal(SIGSEGV, clean_exit_on_sig); // <-- this one is for segmentation fault
signal(SIGTERM , clean_exit_on_sig);

void 
clean_exit_on_sig(int sig_num)
{
        printf ("\n Signal %d received",sig_num);
}

الإجراءات الآمنة في معالج إشارة جدا محدود. إنه غير آمن للاتصال بأي وظيفة مكتبة غير معروفة أن تكون إعادة الدخول، والتي سوف تستبعد، على سبيل المثال، free() و printf(). وبعد أفضل الممارسات هي تحديد متغير وإعادة، ولكن هذا لا يساعدك كثيرا. من الآمن أيضا استخدام مكالمات النظام مثل write().

لاحظ أنه في أمثلة Backtrace المقدمة هنا، backtrace_symbols_fd() ستكون الدالة آمنة لأنها تستخدم FD RAW مباشرة، ولكن الدعوة إلى fprintf() غير صحيح، ويجب استبداله باستخدام write().

أعتقد أنك تحاول حل مشكلة غير موجودة. على الأقل أنت تعمل على النهاية الخاطئة. لن تكون قادرا على قبض على خطأ تجزئة، حيث يتم إلقاء هذا الخطأ / الاستثناء بواسطة نظام التشغيل (هو الناجمة بواسطة برنامجك، نظام التشغيل فقط المصيد هو - هي).

أنصحك بإعادة التفكير في استراتيجيتك فيما يتعلق بالإدخال: لماذا يستحيل تطهيرها؟ أهم ما يجب القيام به هو فحص الحجم، ولهذا سيكون C Stdlib وظائف مناسبة. ثم بالطبع يجب عليك التحقق من إدخال ساري المفعول فيما يتعلق بالمحتوى. نعم، هذا ربما يؤدي إلى الكثير من العمل، لكن هذه هي الطريقة الوحيدة لكتابة برنامج قوي.

تعديل: أنا لست كثيرا من خبير ج، لم أكن أعرف أنه يمكن التعامل مع خطأ تجزئة بواسطة معالج إشارة. ومع ذلك، أعتقد أنها ليست هي الطريقة الصحيحة للذهاب للأسباب المذكورة أعلاه.

ستحتاج إلى توفير معالج SIGSEGV، هذا واحد يبدو لائق جدا.

التعامل مع الإشارة هو (نسبيا) المحمولة عبر أجهزة UNIX (يتضمن ذلك Mac و Linux). الاختلافات الكبيرة هي في تفاصيل الاستثناء، والتي يتم تمريرها كوسيطة لروتين معالجة الإشارات. Sorpy، ولكن من المحتمل أنك ستتحتاج إلى مجموعة من #IFDEFS لذلك، إذا كنت ترغب في طباعة رسائل خطأ معقولة أكثر (مثل المكان الذي حدث خطأ في الخطأ) ...

حسنا، إليك جزء رمز لكي تبدأ به:

#include <signal.h>

/* reached when a segv occurrs */
void
SEGVFunction( SIGARGS )
{
     ...
}

...
main(...) {
    signal(SIGSEGV, SEGVFunction); /* tell the OS, where to go in case... */
    ...
    ... do your work ...
}

مهمتك هي:

  • تحقق ما هي السيجار (OS يعتمد، لذلك استخدم IFDEF)
  • معرفة كيفية استخراج عنوان الأخطاء والكمبيوتر الشخصي من معلومات الاستثناء في السيجار
  • طباعة رسالة معقولة
  • خروج

من الناحية النظرية، يمكنك حتى تصحيح جهاز الكمبيوتر في معالج الإشارة (بعد تعليمات الأخطاء)، والمضي قدما. ومع ذلك، فإن معالجات الإشارات النموذجية إما الخروج () أو إلى LongJMP () مرة أخرى في مكان حفظ في الرئيسية.

مع تحياتي

هناك مثال على كيفية التقاط SIGSEGV وطباعة تتبع مكدس باستخدام Backtrace's Glibc () هنا:

كيفية إنشاء StackTrace عند تعطل تطبيق C ++ الخاص بي

يمكنك استخدام ذلك للقبض على Segfault وتنظيفه، ولكن يجب تحذيره: يجب ألا تفعل الكثير من الأشياء في معالج إشارة، خاصة الأشياء التي تنطوي على إجراء مكالمات مثل Malloc (). هناك الكثير من المكالمات التي لا تشير إلى آمنة، ويمكنك الانتهاء من إطلاق النار على نفسك في المقدمة إذا قمت بها، على سبيل المثال، دعوة إلى Malloc من داخل Malloc.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top