ฉันมี Bash script ซึ่งมีลักษณะคล้ายกับนี้:
#!/bin/bash
echo "Doing some initial work....";
/bin/start/main/server --nodaemon
ตอนนี้ถ้า bash shell ที่เรียกใช้สคริปต์ได้รับสัญญาณ SIGTERM ก็ควรส่ง SIGTERM ไปยังเซิร์ฟเวอร์ที่กำลังรันอยู่ เป็นไปได้ไหม
ฉันมี Bash script ซึ่งมีลักษณะคล้ายกับนี้:
#!/bin/bash
echo "Doing some initial work....";
/bin/start/main/server --nodaemon
ตอนนี้ถ้า bash shell ที่เรียกใช้สคริปต์ได้รับสัญญาณ SIGTERM ก็ควรส่ง SIGTERM ไปยังเซิร์ฟเวอร์ที่กำลังรันอยู่ เป็นไปได้ไหม
คำตอบ:
ลอง:
#!/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จะละเว้นสัญญาณใด ๆ ในขณะที่กระบวนการลูกกำลังดำเนินการ การเริ่มต้นเซิร์ฟเวอร์ด้วย&จะทำให้พื้นหลังเป็นระบบควบคุมงานของเชลล์โดย$!ถือ PID ของเซิร์ฟเวอร์ไว้ (เพื่อใช้กับwaitและkill) โทรwaitแล้วจะรอให้งานที่มีระบุ PID (เซิร์ฟเวอร์) ที่จะเสร็จสิ้นหรือสัญญาณใด ๆ ที่จะถูกไล่ออก
เมื่อเชลล์ได้รับSIGTERM(หรือเซิร์ฟเวอร์ออกโดยอิสระ) การwaitโทรจะส่งคืน (ออกด้วยรหัสทางออกของเซิร์ฟเวอร์หรือด้วยหมายเลขสัญญาณ + 128 ในกรณีที่ได้รับสัญญาณ) หลังจากนั้นถ้าเชลล์ได้รับ SIGTERM มันจะเรียกใช้_termฟังก์ชันที่ระบุว่าเป็นตัวจัดการกับดัก SIGTERM ก่อนที่จะออก (ซึ่งเราทำการล้างข้อมูลใด ๆ และเผยแพร่สัญญาณไปยังกระบวนการเซิร์ฟเวอร์ด้วยตนเองkill)
waitจึงจำเป็นต้องใช้การเรียกครั้งต่อไป?
exec /bin/start/main/server --nodaemon(ในกรณีนี้กระบวนการเชลล์จะถูกแทนที่ด้วยกระบวนการเซิร์ฟเวอร์และคุณไม่จำเป็นต้องเผยแพร่สัญญาณใด ๆ ) หรือคุณใช้/bin/start/main/server --nodaemon &แต่ก็execไม่มีความหมายจริงๆ
_term()ฟังก์ชันที่คุณควรทำwait "$child"อีกครั้ง สิ่งนี้อาจจำเป็นถ้าคุณมีกระบวนการการดูแลอื่น ๆ ที่รอให้เชลล์สคริปต์ตายก่อนที่จะรีสตาร์ทอีกครั้งหรือถ้าคุณติดEXITกับการล้างข้อมูลและทำมันให้ทำงานหลังจากกระบวนการลูกเสร็จสิ้น
execหรือคุณต้องการที่จะติดตั้งกับดัก
Bash ไม่ส่งต่อสัญญาณเช่น SIGTERM ไปยังกระบวนการที่กำลังรออยู่ ถ้าคุณต้องการที่จะจบสคริปต์ของคุณโดยแบ่งออกเป็นเซิร์ฟเวอร์ของคุณ (อนุญาตให้จัดการสัญญาณและสิ่งอื่น ๆ เช่นถ้าคุณเริ่มต้นเซิร์ฟเวอร์โดยตรง) คุณควรใช้execซึ่งจะแทนที่เชลล์ด้วยกระบวนการที่เปิด :
#!/bin/bash
echo "Doing some initial work....";
exec /bin/start/main/server --nodaemon
หากคุณจำเป็นต้องเก็บเปลือกหอยที่อยู่รอบ ๆ ด้วยเหตุผลบางอย่าง (เช่น. คุณต้องทำล้างบางหลังจากที่เซิร์ฟเวอร์สิ้นสุด) คุณควรใช้การรวมกันของtrap, และwait killดูคำตอบของ SensorSmith
Andreas Veithen ชี้ให้เห็นว่าถ้าคุณไม่ต้องการกลับจากการโทร (เช่นในตัวอย่างของ OP) เพียงแค่เรียกผ่านexecคำสั่งก็เพียงพอแล้ว ( @Stuart P. Bentley คำตอบของ ) มิฉะนั้น "ดั้งเดิม" trap 'kill $CHILDPID' TERM(คำตอบของ @ cuonglm) เป็นการเริ่มต้น แต่การwaitโทรกลับจริง ๆ หลังจากตัวจัดการกับดักทำงานซึ่งยังคงเป็นก่อนที่กระบวนการลูกออกจริง ดังนั้นwaitขอแนะนำให้โทร "พิเศษ" ( คำตอบของ @ user1463361 )
ขณะนี้เป็นการปรับปรุงมันยังคงมีสภาพการแข่งขันซึ่งหมายความว่ากระบวนการอาจไม่เคยออก (เว้นแต่ผู้ส่งสัญญาณลองส่งสัญญาณ TERM) หน้าต่างของช่องโหว่อยู่ระหว่างการลงทะเบียนกับตัวจัดการกับดักและการบันทึก PID ของเด็ก
ต่อไปนี้จะกำจัดจุดอ่อนนั้น (บรรจุอยู่ในฟังก์ชันเพื่อนำมาใช้ซ้ำ)
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
โซลูชันที่ให้มาใช้ไม่ได้สำหรับฉันเนื่องจากกระบวนการถูกฆ่าก่อนที่คำสั่ง wait จะเสร็จสิ้น ฉันพบว่าบทความhttp://veithen.github.io/2014/11/16/sigterm-propagation.htmlข้อมูลโค้ดตัวสุดท้ายทำงานได้ดีในกรณีของแอปพลิเคชันของฉันเริ่มต้นใน OpenShift พร้อมตัวเลื่อน sh ที่กำหนดเอง จำเป็นต้องใช้สคริปต์ sh เพราะฉันต้องมีความสามารถในการรับเธรดดัมพ์ซึ่งเป็นไปไม่ได้ในกรณีที่ PID ของกระบวนการ Java เป็น 1
trap 'kill -TERM $PID' TERM INT
$JAVA_EXECUTABLE $JAVA_ARGS &
PID=$!
wait $PID
trap - TERM INT
wait $PID
EXIT_STATUS=$?