बाश में बच्चे को आगे SIGTERM


86

मेरे पास एक बैश स्क्रिप्ट है, जो इस तरह दिखाई देती है:

#!/bin/bash
echo "Doing some initial work....";
/bin/start/main/server --nodaemon

अब यदि स्क्रिप्ट चलाने वाले बैश शेल को SIGTERM सिग्नल मिलता है, तो उसे रनिंग सर्वर को SIGTERM भी भेजना चाहिए (जो ब्लॉक करता है, इसलिए कोई ट्रैप संभव नहीं है)। क्या यह संभव है?

जवाबों:


91

प्रयत्न, कोशिश:

#!/bin/bash 

_term() { 
  echo "Caught SIGTERM signal!" 
  kill -TERM "$child" 2>/dev/null
}

trap _term SIGTERM

echo "Doing some initial work...";
/bin/start/main/server --nodaemon &

child=$! 
wait "$child"

आम तौर पर, bashकिसी भी संकेत को अनदेखा करेगा , जबकि एक बच्चे की प्रक्रिया निष्पादित हो रही है। सर्वर के साथ शुरू करने से &यह शेल के जॉब कंट्रोल सिस्टम में दिखाई देगा, $!सर्वर के पीआईडी को पकड़े रहने के साथ ( waitऔर इस्तेमाल किया जा सकता है kill)। कॉलिंग waitतब निर्दिष्ट पीआईडी ​​(सर्वर) के साथ या समाप्त होने वाले किसी भी सिग्नल के लिए नौकरी की प्रतीक्षा करेगी ।

जब शेल प्राप्त होता है SIGTERM(या सर्वर स्वतंत्र रूप से बाहर निकलता है), तो waitकॉल वापस आ जाएगी (सर्वर से बाहर निकलने के कोड के साथ बाहर निकल रहा है, या सिग्नल नंबर + 128 के साथ सिग्नल के प्राप्त होने पर)। बाद में, यदि शेल को SIGTERM प्राप्त हुआ, तो यह _termबाहर निकलने से पहले SIGTERM ट्रैप हैंडलर के रूप में निर्दिष्ट फ़ंक्शन को कॉल करेगा (जिसमें हम कोई भी क्लीनअप करते हैं और मैन्युअल रूप से उपयोग करते हुए सर्वर प्रक्रिया के लिए सिग्नल का प्रचार करते हैं kill)।


अछा लगता है! मैं इसे आज़माऊंगा और जब मैंने इसका परीक्षण किया तो इसका जवाब दूंगा।
लोरेंज

7
लेकिन निष्पादित शेल को दिए गए प्रोग्राम के साथ बदल देता है , मुझे इस पर स्पष्ट नहीं है कि बाद में waitकॉल की आवश्यकता क्यों है?
इरुवर

5
मुझे लगता है कि 1_CR की बात मान्य है। या तो आप बस उपयोग करते हैं exec /bin/start/main/server --nodaemon(जिस स्थिति में शेल प्रक्रिया को सर्वर प्रक्रिया से बदल दिया जाता है और आपको किसी भी सिग्नल को प्रचारित करने की आवश्यकता नहीं है) या आप उपयोग करते हैं /bin/start/main/server --nodaemon &, लेकिन तब execवास्तव में सार्थक नहीं है।
एंड्रियास वीथेन

2
यदि आप चाहते हैं कि आपकी शेल स्क्रिप्ट बच्चे की समाप्ति के बाद ही समाप्त हो जाए, तो _term()फ़ंक्शन में आपको wait "$child"फिर से होना चाहिए । यह आवश्यक हो सकता है यदि आपके पास कुछ अन्य पर्यवेक्षण प्रक्रिया है जो शेल स्क्रिप्ट के फिर से शुरू होने से पहले मरने की प्रतीक्षा कर रही है, या यदि आप भी EXITकुछ सफाई करने के लिए फंसे हुए हैं और केवल बच्चे की प्रक्रिया समाप्त होने के बाद इसे चलाने के लिए बाध्य करते हैं।
सिंह राशि

1
@AlexanderMills अन्य उत्तर पढ़ें। या तो आप ढूंढ रहे हैं exec, या आप जाल सेट करना चाहते हैं ।
स्टुअर्ट पी। बेंटले

78

बैश सिग्नल को अग्रेषित नहीं करता है जैसे कि SIGTERM प्रक्रियाओं के लिए यह वर्तमान में प्रतीक्षा कर रहा है। यदि आप अपने सर्वर में बहस करके अपनी स्क्रिप्ट को समाप्त करना चाहते हैं (यह सिग्नल और कुछ और चीज़ों को संभालने की अनुमति देता है, जैसे कि आपने सर्वर को सीधे शुरू किया था), तो आपको इसका उपयोग करना चाहिए exec, जो शेल को खोले जाने की प्रक्रिया से बदल देगा :

#!/bin/bash
echo "Doing some initial work....";
exec /bin/start/main/server --nodaemon

यदि आपको किसी कारण से शेल को इधर-उधर रखने की आवश्यकता है (यानी आपको सर्वर समाप्त होने के बाद कुछ सफाई करने की आवश्यकता है), तो आपको , और trap, के संयोजन का उपयोग करना चाहिए । देखिए SensorSmith का जवाबwaitkill


यह सही जवाब है! बहुत अधिक संक्षिप्त और पते ओपी के मूल पूछते हैं
BrDaHa

20

एंड्रियास वीथेन बताते हैं कि यदि आपको कॉल से वापस आने की आवश्यकता नहीं है (जैसे ओपी के उदाहरण में) तो केवल execकमांड के माध्यम से कॉल करना पर्याप्त है ( @Stuart पी। बेंटले का जवाब )। अन्यथा "पारंपरिक" trap 'kill $CHILDPID' TERM(@ congongm का जवाब) एक शुरुआत है, लेकिन waitकॉल वास्तव में ट्रैप हैंडलर के चलने के बाद लौटती है जो कि अभी भी बच्चे की प्रक्रिया से बाहर निकलने से पहले हो सकती है। इसलिए "अतिरिक्त" कॉल waitउचित है ( @ user1463361 का उत्तर )।

हालांकि यह एक सुधार है लेकिन इसमें अभी भी एक दौड़ की स्थिति है जिसका अर्थ है कि प्रक्रिया कभी भी बाहर नहीं निकल सकती है (जब तक कि सिग्नलर टीईआरएम सिग्नल भेजने से पीछे नहीं हटता)। भेद्यता की खिड़की जाल हैंडलर को पंजीकृत करने और बच्चे के पीआईडी ​​को रिकॉर्ड करने के बीच है।

निम्नलिखित उस भेद्यता को समाप्त करता है (पुन: उपयोग के लिए कार्यों में पैक)।

prep_term()
{
    unset term_child_pid
    unset term_kill_needed
    trap 'handle_term' TERM INT
}

handle_term()
{
    if [ "${term_child_pid}" ]; then
        kill -TERM "${term_child_pid}" 2>/dev/null
    else
        term_kill_needed="yes"
    fi
}

wait_term()
{
    term_child_pid=$!
    if [ "${term_kill_needed}" ]; then
        kill -TERM "${term_child_pid}" 2>/dev/null 
    fi
    wait ${term_child_pid}
    trap - TERM INT
    wait ${term_child_pid}
}

# EXAMPLE USAGE
prep_term
/bin/something &
wait_term

2
बहुत बढ़िया काम - मैंने अपने उत्तर के लिंक को यहाँ इंगित करने के लिए अद्यतन किया है (इसके शीर्ष पर एक अधिक व्यापक समाधान होने के कारण, मैं अभी भी थोड़ा परेशान हूँ कि StackExchange UI ने मुझे स्क्रिप्ट को ठीक करने के लिए cuonglm के उत्तर में श्रेय नहीं दिया है: वास्तव में यह है कि यह ओपी के बाद सभी व्याख्यात्मक पाठ के लिए बहुत कुछ समझा और लिखने वाला है जो कुछ मामूली री-एडिट भी नहीं समझ पाया है)।
स्टुअर्ट पी। बेंटले

2
@ StuartP.Bentley, धन्यवाद। मैं इस आवश्यक दो (नहीं स्वीकार किए गए) उत्तरों और एक बाहरी संदर्भ को इकट्ठा करने के लिए आश्चर्यचकित था , और फिर मुझे दौड़ की स्थिति से नीचे भागना पड़ा। मैं अपने संदर्भों को लिंक के रूप में अपग्रेड करूंगा जो कि कुछ अतिरिक्त कुडोस दे सकते हैं।
सेंसॉरस्मिथ

3

बशर्ते समाधान मेरे लिए काम नहीं करता है क्योंकि वास्तव में प्रतीक्षा आदेश समाप्त होने से पहले प्रक्रिया को मार दिया गया था। मैंने पाया कि लेख http://veithen.github.io/2014/11/16/sigterm-propagation.html , ओपन शिप में कस्टम श रनर के साथ शुरू हुए आवेदन के मेरे मामले में अंतिम स्निपेट अच्छा है। श स्क्रिप्ट की आवश्यकता है क्योंकि मुझे थ्रेड डंप प्राप्त करने की क्षमता होनी चाहिए जो कि जावा प्रक्रिया के पीआईडी ​​1 के मामले में असंभव है।

trap 'kill -TERM $PID' TERM INT
$JAVA_EXECUTABLE $JAVA_ARGS &
PID=$!
wait $PID
trap - TERM INT
wait $PID
EXIT_STATUS=$?
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.