سؤال

لدي خادم صغير البرنامج أن يقبل الاتصالات على TCP أو المحلية UNIX socket, يقرأ في الأمر بسيط ، اعتمادا على الأوامر ، يرسل الرد.المشكلة هي أن العميل قد لا مصلحة في الجواب في بعض الأحيان ويخرج مبكرا حتى كتابة هذا المقبس سوف يسبب SIGPIPE و أجعل تعطل الخادم.ما هي أفضل الممارسات لمنع تحطم هنا ؟ هل هناك طريقة للتحقق مما إذا كان الجانب الآخر من الخط لا يزال القراءة ؟ (حدد() لا يبدو أن العمل هنا كما يقول دائما المقبس هو قابل للكتابة).أو يجب علي اللحاق SIGPIPE مع معالج و تجاهل ذلك ؟

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

المحلول

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

الأكثر المحمولة طريقة للقيام بذلك هو وضع SIGPIPE معالج SIG_IGN.وهذا يمنع أي مأخذ أو الأنابيب الكتابة من تسبب SIGPIPE إشارة.

تجاهل SIGPIPE إشارة استخدام التعليمات البرمجية التالية:

signal(SIGPIPE, SIG_IGN);

إذا كنت تستخدم send() المكالمة ، وثمة خيار آخر هو استخدام MSG_NOSIGNAL الخيار, والتي سوف تتحول SIGPIPE السلوك لكل دعوة أساس.علما أن ليس كل أنظمة التشغيل تدعم MSG_NOSIGNAL العلم.

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

نصائح أخرى

أسلوب آخر هو تغيير المقبس لذلك لم يولد SIGPIPE على الكتابة().هذا هو أكثر ملاءمة في المكتبات ، حيث قد لا تريد عالمي إشارة معالج SIGPIPE.

على معظم BSD القائم (ماك ، فري...) نظم (على افتراض أنك تستخدم C/C++), يمكنك أن تفعل هذا مع:

int set = 1;
setsockopt(sd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&set, sizeof(int));

مع هذا في الواقع بدلا من SIGPIPE الإشارة التي يتم إنشاؤها ، EPIPE سوف تعاد.

أنا السوبر في وقت متأخر إلى الحزب ، ولكن SO_NOSIGPIPE ليس المحمولة, و قد لا تعمل على النظام الخاص بك (يبدو أن BSD شيء).

بديل جيد إذا كنت على نظام لينكس دون SO_NOSIGPIPE سيكون تعيين MSG_NOSIGNAL العلم على إرسال(2) الدعوة.

على سبيل المثال استبدال write(...) قبل send(...,MSG_NOSIGNAL) (انظر نوبار's تعليق)

char buf[888];
//write( sockfd, buf, sizeof(buf) );
send(    sockfd, buf, sizeof(buf), MSG_NOSIGNAL );

في هذا بعد وصفت ممكن حل سولاريس الحالة عندما لا SO_NOSIGPIPE ولا MSG_NOSIGNAL هو متاح.

بدلا من ذلك علينا أن قمع مؤقتا SIGPIPE في مؤشر الترابط الحالي الذي ينفذ مكتبة التعليمات البرمجية.وفيما يلي كيفية القيام بذلك:لقمع SIGPIPE علينا أولا التحقق إذا كان معلقا.إذا كان كذلك هذا يعني أنه محظور في هذا الموضوع, و علينا أن نفعل شيئا.إذا كانت المكتبة يولد إضافية SIGPIPE ، فإنه سيتم دمجها مع معلقة واحدة و لا العملية.إذا SIGPIPE ليست معلقة ثم نحن كتلة في هذا الموضوع, و أيضا التحقق من ما إذا كان بالفعل قد تم حظره.ثم نحن أحرار في تنفيذ أعمالنا يكتب.عندما أردنا استعادة SIGPIPE إلى حالته الأصلية ، ونحن نفعل ما يلي:إذا SIGPIPE كانت معلقة في الأصل نتمكن من فعل أي شيء.وإلا فإننا تحقق مما إذا كان معلقا الآن.إذا كان كذلك (وهو ما يعني أن الإجراءات قد ولدت واحدة أو أكثر SIGPIPEs) ، ثم ننتظر في هذا الموضوع ، وبالتالي تطهير لها ريثما يتم وضع (للقيام بذلك نستخدم sigtimedwait() مع صفر مهلة;هذا هو لتجنب عرقلة في سيناريو حيث الخبيثة المستخدم إرسال SIGPIPE يدويا إلى العملية برمتها:في هذه الحالة سوف نرى ذلك في انتظار ، ولكن الصفحات الأخرى يمكن التعامل معها من قبل كان لدينا تغيير في الانتظار لذلك).بعد تطهير حالة "معلق" نحن لا تمنع SIGPIPE في هذا الموضوع, ولكن فقط إذا لم يكن منعت أصلا.

رمز المثال في https://github.com/kroki/XProbes/blob/1447f3d93b6dbf273919af15e59f35cca58fcc23/src/libxprobes.c#L156

التعامل مع SIGPIPE محليا

فمن الأفضل عادة لمعالجة الخطأ محليا بدلا من في إشارة معالج الحدث منذ محليا سيكون لديك المزيد من سياق ما يجري وما اللجوء إلى اتخاذ.

لدي اتصال طبقة واحدة من التطبيقات التي تسمح بلدي التطبيق للتواصل مع ملحق خارجي.عندما أكتب يحدث خطأ أنا رمي استثناء في طبقة الاتصال والسماح لها فقاعة تصل إلى محاولة اللحاق كتلة التعامل معها هناك.

كود:

رمز تجاهل SIGPIPE إشارة بحيث يمكنك التعامل معها محليا:

// We expect write failures to occur but we want to handle them where 
// the error occurs rather than in a SIGPIPE handler.
signal(SIGPIPE, SIG_IGN);

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

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

أو يجب علي اللحاق SIGPIPE مع معالج و تجاهل ذلك ؟

أعتقد أن هذا هو على حق.كنت تريد أن تعرف عند النهاية الأخرى قد أغلقت بهم واصف و هذا ما SIGPIPE يقول لك.

سام

لينكس دليل قال:

EPIPE المحلية النهاية تم إيقاف على اتصال الموجهة المقبس.في هذه الحالة العملية سوف تتلقى أيضا SIGPIPE إلا إذا MSG_NOSIGNAL تعيين.

ولكن أوبونتو 12.04 هذا ليس صحيح.كتبت اختبار هذه الحالة وأنا دائما الحصول على EPIPE withot SIGPIPE.SIGPIPE هو genereated إذا حاولت كتابة نفس مكسورة مأخذ المرة الثانية.لذلك أنت لا تحتاج إلى تجاهل SIGPIPE إذا كان هذا إشارة يحدث يعني المنطق خطأ في البرنامج.

ما هي أفضل الممارسات لمنع تحطم هنا ؟

إما تعطيل sigpipes في الجميع أو الصيد تجاهل الخطأ.

هل هناك طريقة للتحقق مما إذا كان الجانب الآخر من الخط لا يزال القراءة ؟

نعم استخدم حدد().

حدد() لا يبدو أن العمل هنا كما يقول دائما المقبس هو قابل للكتابة.

تحتاج إلى تحديد في قراءة بت.ربما يمكنك تجاهل الكتابة بت.

عند نهاية بكثير يغلق فيها التعامل مع ملف ، حدد وسوف أقول لكم أن هناك بيانات جاهزة للقراءة.عندما تذهب و تقرأ ذلك سوف نعود 0 بايت, وهو كيف نظام التشغيل يقول لك أن مؤشر الملف قد أغلق.

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

لاحظ أنه يمكنك الحصول على sigpipe من قريب () ، وكذلك عند الكتابة.

بالقرب من الإحمرار أي بيانات مخزنة بشكل مؤقت.إذا كان الطرف الآخر قد تم بالفعل إغلاق, ثم أغلق سوف تفشل وسوف تتلقى sigpipe.

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

Sigpipe يقول لك شيئا ما خطأ ، فإنه لا اقول لكم ما ، أو ما يجب القيام به حيال ذلك.

تحت الحديث POSIX النظام (أيلينكس) ، يمكنك استخدام sigprocmask() وظيفة.

#include <signal.h>

void block_signal(int signal_to_block /* i.e. SIGPIPE */ )
{
    sigset_t set;
    sigset_t old_state;

    // get the current state
    //
    sigprocmask(SIG_BLOCK, NULL, &old_state);

    // add signal_to_block to that existing state
    //
    set = old_state;
    sigaddset(&set, signal_to_block);

    // block that signal also
    //
    sigprocmask(SIG_BLOCK, &set, NULL);

    // ... deal with old_state if required ...
}

إذا كنت ترغب في استعادة الدولة السابقة في وقت لاحق ، تأكد من حفظ old_state في مكان آمن.إذا كنت استدعاء هذه الدالة عدة مرات, تحتاج إلى استخدام كومة أو فقط حفظ أول أو آخر old_state...أو ربما لديها وظيفة الذي يزيل محددة منعت إشارة.

لمزيد من المعلومات اقرأ رجل الصفحة.

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