طريقة محمولة لالتقاط الإشارات وإبلاغ المستخدم بالمشكلة

StackOverflow https://stackoverflow.com/questions/103280

سؤال

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

يبدو معالج الإشارة الخاص بنا اليوم كما يلي:

void catchSignal (int reason) {
  std :: cerr << "Caught a signal: " << reason << std::endl;
  exit (1);
}

أستطيع سماع صرخات الرعب مما سبق، كما قرأت من هذا خيط أنه من الشر استدعاء وظيفة عدم إعادة الدخول من معالج الإشارة.

هل هناك طريقة محمولة للتعامل مع الإشارة وتوفير المعلومات للمستخدمين؟

يحرر: أو على الأقل محمولة ضمن إطار عمل POSIX؟

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

المحلول

هذا طاولة يسرد جميع الوظائف التي يضمن POSIX أن تكون آمنة للإشارة غير المتزامنة وبالتالي يمكن استدعاؤها من معالج الإشارة.

باستخدام أمر "الكتابة" من هذا الجدول، نأمل أن يؤدي الحل "القبيح" نسبيًا التالي إلى حل المشكلة:

#include <csignal>

#ifdef _WINDOWS_
#define _exit _Exit
#else
#include <unistd.h>
#endif

#define PRINT_SIGNAL(X) case X: \
          write (STDERR_FILENO, #X ")\n" , sizeof(#X ")\n")-1); \
          break;

void catchSignal (int reason) {
  char s[] = "Caught signal: (";
  write (STDERR_FILENO, s, sizeof(s) - 1);
  switch (reason)
  {
    // These are the handlers that we catch
    PRINT_SIGNAL(SIGUSR1);
    PRINT_SIGNAL(SIGHUP);
    PRINT_SIGNAL(SIGINT);
    PRINT_SIGNAL(SIGQUIT);
    PRINT_SIGNAL(SIGABRT);
    PRINT_SIGNAL(SIGILL);
    PRINT_SIGNAL(SIGFPE);
    PRINT_SIGNAL(SIGBUS);
    PRINT_SIGNAL(SIGSEGV);
    PRINT_SIGNAL(SIGTERM);
  }

  _Exit (1);  // 'exit' is not async-signal-safe
}

يحرر: بناء على النوافذ.

بعد محاولة إنشاء هذه النوافذ، يبدو أن "STDERR_FILENO" لم يتم تعريفه.ولكن من الوثائق يبدو أن قيمتها هي "2".

#include <io.h>
#define STDIO_FILENO 2

يحرر: لا ينبغي استدعاء "الخروج" من معالج الإشارة أيضًا!

كما أشار fizzer, ، يعد استدعاء _Exit في ما سبق أسلوب مطرقة ثقيلة لإشارات مثل HUP وTERM.من الناحية المثالية، عندما يتم التقاط هذه الإشارات، يمكن استخدام علامة من النوع "volatile sig_atomic_t" لإعلام البرنامج الرئيسي بضرورة الخروج.

ما يلي وجدته مفيدًا في عمليات البحث التي أجريتها.

  1. مقدمة لبرمجة إشارات يونكس
  2. توسيع الإشارات التقليدية

نصائح أخرى

يعد FWIW, 2 خطأً قياسيًا في نظام التشغيل Windows أيضًا، ولكنك ستحتاج إلى بعض الترجمة الشرطية لأن عملية الكتابة الخاصة بهم تسمى _write().سوف تريد أيضا

#ifdef SIGUSR1 /* or whatever */

وما إلى ذلك حول جميع الإشارات إلى الإشارات التي لا يضمن تعريفها معيار C.

وأيضًا، كما هو مذكور أعلاه، فإنك لا تريد التعامل مع SIGUSR1 وSIGHUP وSIGINT وSIGQUIT وSIGTERM بهذه الطريقة.

ريتشارد، لا تزال الكارما غير كافية للتعليق، لذا أخشى أن تكون هناك إجابة جديدة.هذه إشارات غير متزامنة؛ليس لديك أي فكرة عن موعد تسليمها، لذلك من المحتمل أن تكون في رمز المكتبة الذي يحتاج إلى إكماله ليظل متسقًا.ولذلك يتعين على معالجات الإشارة لهذه الإشارات العودة.إذا قمت باستدعاء ()exit، فستقوم المكتبة ببعض الأعمال ()post-main، بما في ذلك استدعاء الوظائف المسجلة في ()atexit وتنظيف التدفقات القياسية.قد تفشل هذه المعالجة، على سبيل المثال، في حالة وصول إشارتك إلى وظيفة الإدخال/الإخراج في المكتبة القياسية.ولذلك في C90 غير مسموح لك بالاتصال بـ "exit()".أرى الآن أن C99 يخفف المتطلبات من خلال توفير وظيفة جديدة _Exit() في stdlib.h.قد يتم استدعاء _Exit() بأمان من معالج للإشارة غير المتزامنة._Exit() لن يستدعي وظائف atexit() وقد يحذف تنظيف التدفقات القياسية وفقًا لتقدير التنفيذ.

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

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

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

اكتب برنامج تشغيل لتشغيل البرنامج الخاص بك وإبلاغ المستخدم برمز الخروج غير الطبيعي.

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