كيفية استخدام إشارات Linux في نصوص Bash
نشرت: 2022-08-09
ترسل نواة Linux إشارات إلى العمليات المتعلقة بالأحداث التي يحتاجون إلى التفاعل معها. تتعامل البرامج النصية حسنة التصرف مع الإشارات بأناقة وقوة ويمكنها تنظيف ما وراءها حتى لو ضغطت على Ctrl + C. إليك الطريقة.
الإشارات والعمليات
الإشارات عبارة عن رسائل قصيرة وسريعة أحادية الاتجاه يتم إرسالها إلى عمليات مثل البرامج النصية والبرامج والشياطين. سمحوا للعملية بمعرفة شيء حدث. ربما يكون المستخدم قد ضغط على Ctrl + C ، أو ربما حاول التطبيق الكتابة إلى ذاكرة لا يمكنه الوصول إليها.
إذا توقع مؤلف العملية أنه قد يتم إرسال إشارة معينة إليه ، فيمكنه كتابة روتين في البرنامج أو البرنامج النصي للتعامل مع هذه الإشارة. يسمى هذا الروتين بمعالج الإشارة . يلتقط الإشارة أو يحبسها ، ويقوم ببعض الإجراءات استجابة لها.
يستخدم Linux الكثير من الإشارات ، كما سنرى ، ولكن من وجهة نظر البرمجة النصية ، لا يوجد سوى مجموعة فرعية صغيرة من الإشارات التي من المحتمل أن تكون مهتمًا بها. وعلى وجه الخصوص ، في النصوص غير التافهة ، الإشارات التي تخبر البرنامج النصي لإغلاقه يجب أن يكون محاصرًا (حيثما أمكن ذلك) ويتم تنفيذ إيقاف التشغيل بشكل رشيق.
على سبيل المثال ، يمكن إعطاء البرامج النصية التي تنشئ ملفات مؤقتة أو فتح منافذ جدار الحماية الفرصة لحذف الملفات المؤقتة أو إغلاق المنافذ قبل إغلاقها. إذا مات البرنامج النصي بمجرد تلقيه للإشارة ، فيمكن ترك جهاز الكمبيوتر الخاص بك في حالة غير متوقعة.
إليك كيفية التعامل مع الإشارات في البرامج النصية الخاصة بك.
تلبية الإشارات
بعض أوامر Linux لها أسماء مشفرة. ليس الأمر كذلك الأمر الذي يحبس الإشارات. انها تسمى trap
. يمكننا أيضًا استخدام trap
مع الخيار -l
(list) لتظهر لنا القائمة الكاملة للإشارات التي يستخدمها Linux.
فخ -l
على الرغم من أن قائمتنا المرقمة تنتهي عند 64 ، إلا أن هناك بالفعل 62 إشارة. الإشارات 32 و 33 مفقودة. لم يتم تنفيذها في Linux. لقد تم استبدالها بوظائف في برنامج التحويل البرمجي gcc
للتعامل مع سلاسل الرسائل في الوقت الفعلي. كل شيء من الإشارة 34 ، SIGRTMIN
، إلى الإشارة 64 ، SIGRTMAX
، هي إشارات في الوقت الفعلي.
سترى قوائم مختلفة لأنظمة تشغيل مختلفة شبيهة بـ Unix. على OpenIndiana على سبيل المثال ، توجد الإشارات 32 و 33 ، إلى جانب مجموعة من الإشارات الإضافية التي تجعل العدد الإجمالي يصل إلى 73.
يمكن الإشارة إلى الإشارات بالاسم أو الرقم أو بالاسم المختصر. اسمهم المختصر هو ببساطة اسمهم مع إزالة "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.
لمعرفة ما إذا تم تعيين الملائمة على إشارة ، استخدم الخيار -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
باقي النص بسيط للغاية. نحتاج إلى معرفة معرّف العملية للنص ، لذلك لدينا البرنامج النصي يردد ذلك لنا. يحتفظ المتغير $$
بمعرف عملية البرنامج النصي.
نقوم بإنشاء متغير يسمى counter
وقمنا بتعيينه على الصفر.
تستمر الحلقة while
loop إلى الأبد ما لم يتم إيقافها بالقوة. يقوم بزيادة متغير counter
، ويردده على الشاشة ، وينام لمدة ثانية.
لنقم بتشغيل البرنامج النصي ونرسل إشارات مختلفة إليه.
./simple-loop.sh
عندما نضغط على "Ctrl + C" ، تتم طباعة رسالتنا في نافذة المحطة ويتم إنهاء البرنامج النصي.
لنقم بتشغيله مرة أخرى ونرسل إشارة SIGQUIT
باستخدام الأمر kill
. سنحتاج إلى القيام بذلك من نافذة طرفية أخرى. ستحتاج إلى استخدام معرف العملية الذي تم الإبلاغ عنه بواسطة البرنامج النصي الخاص بك.
./simple-loop.sh
4575
كما هو متوقع ، يقوم البرنامج النصي بالإبلاغ عن وصول الإشارة ثم ينتهي. وأخيرًا ، لإثبات هذه النقطة ، سنفعلها مرة أخرى باستخدام إشارة SIGTERM
.
./simple-loop.sh
اقتل سيغرم 4584
لقد تحققنا من قدرتنا على حجز إشارات متعددة في البرنامج النصي ، والرد على كل إشارة بشكل مستقل. الخطوة التي تروج لكل هذا من المثير للاهتمام إلى المفيد هي إضافة معالجات الإشارة.
التعامل مع الإشارات في النصوص
يمكننا استبدال سلسلة الاستجابة باسم وظيفة في البرنامج النصي الخاص بك. ثم يستدعي الأمر 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
بعد ثلاث ضغطات على Ctrl + C ، ينتهي البرنامج النصي ويستدعي تلقائيًا وظيفة exit_handler()
.
اقرأ الإشارات
من خلال محاصرة الإشارات والتعامل معها في وظائف معالج مباشرة ، يمكنك جعل نصوص Bash الخاصة بك مرتبة خلف نفسها حتى لو تم إنهاؤها بشكل غير متوقع. هذا يمنحك نظام ملفات أنظف. كما أنه يمنع عدم الاستقرار في المرة التالية التي تقوم فيها بتشغيل البرنامج النصي ، و- اعتمادًا على الغرض من البرنامج النصي الخاص بك - يمكنه أيضًا منع الثغرات الأمنية.
ذات صلة: كيفية تدقيق أمان نظام Linux الخاص بك مع Lynis