كيفية استخدام إشارات Linux في نصوص Bash

نشرت: 2022-08-09
كمبيوتر محمول Linux يعرض موجه bash
fatmawati achmad zaenuri / Shutterstock.com

ترسل نواة Linux إشارات إلى العمليات المتعلقة بالأحداث التي يحتاجون إلى التفاعل معها. تتعامل البرامج النصية حسنة التصرف مع الإشارات بأناقة وقوة ويمكنها تنظيف ما وراءها حتى لو ضغطت على Ctrl + C. إليك الطريقة.

الإشارات والعمليات

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

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

كيفية إدارة العمليات من Linux Terminal: 10 أوامر تحتاج إلى معرفتها
ذات صلة كيفية إدارة العمليات من Linux Terminal: 10 أوامر تحتاج إلى معرفتها

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

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

إليك كيفية التعامل مع الإشارات في البرامج النصية الخاصة بك.

تلبية الإشارات

بعض أوامر Linux لها أسماء مشفرة. ليس الأمر كذلك الأمر الذي يحبس الإشارات. انها تسمى trap . يمكننا أيضًا استخدام trap مع الخيار -l (list) لتظهر لنا القائمة الكاملة للإشارات التي يستخدمها Linux.

 فخ -l 

سرد الإشارات في Ubuntu مع trap -l

على الرغم من أن قائمتنا المرقمة تنتهي عند 64 ، إلا أن هناك بالفعل 62 إشارة. الإشارات 32 و 33 مفقودة. لم يتم تنفيذها في Linux. لقد تم استبدالها بوظائف في برنامج التحويل البرمجي gcc للتعامل مع سلاسل الرسائل في الوقت الفعلي. كل شيء من الإشارة 34 ، SIGRTMIN ، إلى الإشارة 64 ، SIGRTMAX ، هي إشارات في الوقت الفعلي.

سترى قوائم مختلفة لأنظمة تشغيل مختلفة شبيهة بـ Unix. على OpenIndiana على سبيل المثال ، توجد الإشارات 32 و 33 ، إلى جانب مجموعة من الإشارات الإضافية التي تجعل العدد الإجمالي يصل إلى 73.

سرد الإشارات في OpenIndiana باستخدام trap -l

يمكن الإشارة إلى الإشارات بالاسم أو الرقم أو بالاسم المختصر. اسمهم المختصر هو ببساطة اسمهم مع إزالة "SIG" البادئة.

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

  • الإنهاء: تم إنهاء العملية.
  • تجاهل: لا تؤثر الإشارة على العملية. هذه إشارة للمعلومات فقط.
  • الأساسية: يتم إنشاء ملف تفريغ. يتم ذلك عادةً لأن العملية قد تجاوزت بطريقة ما ، مثل انتهاك الذاكرة.
  • توقف: توقفت العملية. أي أنه تم إيقافه مؤقتًا ولم يتم إنهاؤه.
  • متابعة: يخبرنا بإيقاف العملية لمواصلة التنفيذ.

هذه هي الإشارات التي ستواجهها كثيرًا.

  • SIGHUP : Signal 1. تم قطع الاتصال بمضيف بعيد - مثل خادم SSH - بشكل غير متوقع أو قام المستخدم بتسجيل الخروج. قد ينتهي البرنامج النصي الذي يتلقى هذه الإشارة بأمان ، أو قد يختار محاولة إعادة الاتصال بالمضيف البعيد.
  • SIGINT : Signal 2. ضغط المستخدم على تركيبة Ctrl + C لإجبار العملية على الإغلاق ، أو تم استخدام أمر kill مع الإشارة 2. من الناحية الفنية ، هذه إشارة مقاطعة ، وليست إشارة إنهاء ، ولكنها نصية متقطعة بدون عادةً ما ينتهي معالج الإشارة.
  • SIGQUIT : Signal 3. ضغط المستخدم على تركيبة Ctrl + D لإجبار العملية على الإنهاء ، أو تم استخدام أمر kill مع الإشارة 3.
  • SIGFPE : الإشارة 8. حاولت العملية إجراء عملية حسابية غير قانونية (مستحيلة) ، مثل القسمة على صفر.
  • SIGKILL : الإشارة 9. هذه إشارة مكافئة للمقصلة. لا يمكنك الإمساك به أو تجاهله ، ويحدث ذلك على الفور. يتم إنهاء العملية على الفور.
  • SIGTERM : Signal 15. هذه هي النسخة الأكثر مراعاة من SIGKILL . يخبر SIGTERM أيضًا أن العملية تنتهي ، ولكن يمكن أن يتم حصرها ويمكن للعملية تشغيل عمليات التنظيف الخاصة بها قبل الإغلاق. هذا يسمح بإغلاق رشيق. هذه هي الإشارة الافتراضية التي يرفعها أمر kill .

إشارات على سطر الأوامر

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

يحبس هذا الأمر إشارة SIGINT . الرد هو طباعة سطر من النص إلى النافذة الطرفية. نحن نستخدم الخيار -e (تمكين الهروب) مع echo حتى نتمكن من استخدام محدد التنسيق " \n ".

 تم اكتشاف اعتراض 'echo -e "\ nCtrl + c."' SIGINT 

تعويض اللون Ctrl + C في سطر الأوامر

تتم طباعة سطر النص الخاص بنا في كل مرة نضغط فيها على تركيبة Ctrl + C.

لمعرفة ما إذا تم تعيين الملائمة على إشارة ، استخدم الخيار -p (ملاءمة الطباعة).

 مصيدة -p SIGINT 

التحقق من ضبط المصيدة على إشارة

استخدام trap بدون خيارات يفعل نفس الشيء.

لإعادة ضبط الإشارة إلى حالتها الطبيعية غير المقيدة ، استخدم واصلة " - " واسم الإشارة المحاصرة.

 فخ - SIGINT
 مصيدة -p SIGINT 

إزالة مصيدة من إشارة

لا يوجد خرج من الأمر trap -p يشير إلى عدم وجود تعيين ملائم لتلك الإشارة.

إشارات محاصرة في النصوص

يمكننا استخدام نفس الأمر trap للتنسيق العام داخل البرنامج النصي. يقوم هذا البرنامج النصي باعتراض ثلاث إشارات مختلفة ، SIGINT و SIGQUIT و SIGTERM .

 #! / بن / باش

trap "صدى لقد تم إنهاء SIGINT ؛ خروج" SIGINT
trap "صدى لقد تم إنهاء SIGQUIT ؛ خروج" SIGQUIT
trap "صدى لقد تم إنهاء SIGTERM ؛ خروج" SIGTERM

صدى $$
العداد = 0

احيانا صحيح
فعل 
  صدى "رقم الحلقة:" $ ((عداد ++))
  النوم 1
فعله

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

انسخ النص إلى المحرر الخاص بك واحفظه في ملف يسمى “simple-loop.sh” ، واجعله قابلاً للتنفيذ باستخدام الأمر chmod . ستحتاج إلى القيام بذلك مع جميع البرامج النصية في هذه المقالة إذا كنت تريد المتابعة على جهاز الكمبيوتر الخاص بك. ما عليك سوى استخدام اسم البرنامج النصي المناسب في كل حالة.

 chmod + x simple-loop.sh 

جعل نص قابل للتنفيذ مع chmod

باقي النص بسيط للغاية. نحتاج إلى معرفة معرّف العملية للنص ، لذلك لدينا البرنامج النصي يردد ذلك لنا. يحتفظ المتغير $$ بمعرف عملية البرنامج النصي.

نقوم بإنشاء متغير يسمى counter وقمنا بتعيينه على الصفر.

تستمر الحلقة while loop إلى الأبد ما لم يتم إيقافها بالقوة. يقوم بزيادة متغير counter ، ويردده على الشاشة ، وينام لمدة ثانية.

لنقم بتشغيل البرنامج النصي ونرسل إشارات مختلفة إليه.

 ./simple-loop.sh 

تم إنهاء البرنامج النصي الذي يحدده باستخدام Ctrl + C

عندما نضغط على "Ctrl + C" ، تتم طباعة رسالتنا في نافذة المحطة ويتم إنهاء البرنامج النصي.

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

 ./simple-loop.sh
 4575 

تم إنهاء البرنامج النصي الذي يحدده بـ SIGQUIT

كما هو متوقع ، يقوم البرنامج النصي بالإبلاغ عن وصول الإشارة ثم ينتهي. وأخيرًا ، لإثبات هذه النقطة ، سنفعلها مرة أخرى باستخدام إشارة SIGTERM .

 ./simple-loop.sh
 اقتل سيغرم 4584 

تم إنهاء البرنامج النصي الذي يحدده باستخدام SIGTERM

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

التعامل مع الإشارات في النصوص

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

انسخ هذا النص في محرر واحفظه كملف يسمى "grace.sh" ، واجعله قابلاً للتنفيذ باستخدام chmod .

 #! / بن / باش

فخ graceful_shutdown توقع إشارة SIGTERM

graceful_shutdown ()
{
  echo -e "\ n إزالة الملف المؤقت:" $ temp_file
  rm -rf "$ temp_file"
  خروج
}

temp_file = $ (mktemp -p / tmp tmp.XXXXXXXXXX)
صدى "إنشاء ملف مؤقت:" $ temp_file

العداد = 0

احيانا صحيح
فعل 
  صدى "رقم الحلقة:" $ ((عداد ++))
  النوم 1
فعله

يقوم البرنامج النصي بتعيين فخ لثلاث إشارات مختلفة - SIGHUP و SIGINT و SIGTERM - باستخدام عبارة trap واحدة. الاستجابة هي اسم وظيفة graceful_shutdown() . يتم استدعاء الوظيفة عند تلقي إحدى الإشارات الثلاث المحاصرة.

يقوم البرنامج النصي بإنشاء ملف مؤقت في الدليل “/ tmp” ، باستخدام mktemp . نموذج اسم الملف هو "tmp.XXXXXXXXXX" ، لذا سيكون اسم الملف "tmp". متبوعًا بعشرة أحرف أبجدية رقمية عشوائية. يتردد صدى اسم الملف على الشاشة.

باقي النص هو نفسه السابق ، مع متغير counter وحلقة while لانهائية.

 ./grace.sh 

برنامج نصي يقوم بإغلاق رشيق عن طريق حذف ملف مؤقت

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

أيضًا ، قمنا بتجميع كل إشاراتنا المحاصرة معًا وتعاملنا معها بوظيفة واحدة. يمكنك تعقب الإشارات بشكل فردي وإرسالها إلى وظائف المعالج المخصصة الخاصة بها.

انسخ هذا النص واحفظه في ملف يسمى "triple.sh" ، واجعله قابلاً للتنفيذ باستخدام الأمر chmod .

 #! / بن / باش

مصيدة sigint_handler SIGINT
المصيدة sigusr1_ مناول SIGUSR1
خروج فخ EXIT

الوظيفة sigint_handler () {
  ((++ sigint_count))

  صدى -e "\ n استقبل SIGINT $ sigint_count مرة (مرات)."

  إذا [["$ sigint_count" -eq 3]] ؛ ومن بعد
    صدى "بدء إغلاق".
    loop_flag = 1
  فاي
}

الوظيفة sigusr1_handler () {
  صدى "SIGUSR1 أرسلت واستقبلت $ ((++ sigusr1_count)) مرة (مرات)."
}

وظيفة exit_handler () { 
  صدى "معالج الخروج: يتم إغلاق البرنامج النصي ..."
}

صدى $$
sigusr1_count = 0
sigint_count = 0
loop_flag = 0

بينما [[$ loop_flag -eq 0]] ؛ فعل
  قتل -SIGUSR1 $$
  النوم 1
فعله

نحدد ثلاثة فخاخ في الجزء العلوي من البرنامج النصي.

  • واحد يلائم SIGINT وله معالج يسمى sigint_handler() .
  • يحبس الثاني إشارة تسمى SIGUSR1 ويستخدم معالجًا يسمى sigusr1_handler() .
  • رقم ثلاثة يلائم إشارة EXIT . يتم رفع هذه الإشارة بواسطة البرنامج النصي نفسه عند إغلاقه. يعني تعيين معالج إشارة لـ EXIT أنه يمكنك تعيين وظيفة سيتم استدعاؤها دائمًا عند إنهاء البرنامج النصي (ما لم يتم قتلها باستخدام إشارة SIGKILL ). يسمى المعالج الخاص بنا exit_handler() .

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

ترك معالجات الإشارة جانباً في الوقت الحالي ، يجب أن يكون نص البرنامج النصي مألوفًا لك. إنه يردد معرّف العملية إلى النافذة الطرفية ويخلق بعض المتغيرات. يسجل المتغير sigusr1_count عدد المرات التي تمت فيها معالجة SIGUSR1 ، ويسجل sigint_count عدد مرات معالجة SIGINT . يتم ضبط المتغير loop_flag على الصفر.

الحلقة while ليست حلقة لا نهائية. سيتوقف عن التكرار إذا تم تعيين متغير loop_flag على أي قيمة غير صفرية. تستخدم كل دورة من حلقات while loop kill لإرسال إشارة SIGUSR1 إلى هذا البرنامج النصي ، عن طريق إرسالها إلى معرّف العملية الخاص بالبرنامج النصي. يمكن للنصوص إرسال إشارات لأنفسهم!

تعمل الدالة sigusr1_handler() على زيادة متغير sigusr1_count وترسل رسالة إلى نافذة المحطة الطرفية.

في كل مرة يتم فيها استقبال إشارة SIGINT ، تزيد الدالة siguint_handler() من متغير sigint_count قيمته في النافذة الطرفية.

إذا كان المتغير sigint_count يساوي ثلاثة ، يتم تعيين متغير loop_flag على واحد ويتم إرسال رسالة إلى نافذة المحطة الطرفية لإعلام المستخدم بأن عملية الإغلاق قد بدأت.

نظرًا لأن loop_flag لم تعد مساوية للصفر ، تنتهي حلقة while وينتهي البرنامج النصي. لكن هذا الإجراء يرفع إشارة EXIT تلقائيًا ويتم استدعاء وظيفة exit_handler() .

 ./triple.sh 

برنامج نصي يستخدم SIGUSR1 ، يتطلب ثلاث مجموعات Ctrl + C للإغلاق ، والتقاط إشارة الخروج عند الإغلاق

بعد ثلاث ضغطات على Ctrl + C ، ينتهي البرنامج النصي ويستدعي تلقائيًا وظيفة exit_handler() .

اقرأ الإشارات

من خلال محاصرة الإشارات والتعامل معها في وظائف معالج مباشرة ، يمكنك جعل نصوص Bash الخاصة بك مرتبة خلف نفسها حتى لو تم إنهاؤها بشكل غير متوقع. هذا يمنحك نظام ملفات أنظف. كما أنه يمنع عدم الاستقرار في المرة التالية التي تقوم فيها بتشغيل البرنامج النصي ، و- اعتمادًا على الغرض من البرنامج النصي الخاص بك - يمكنه أيضًا منع الثغرات الأمنية.

ذات صلة: كيفية تدقيق أمان نظام Linux الخاص بك مع Lynis