ส่งต่อ SIGTERM ให้กับเด็กใน Bash


86

ฉันมี Bash script ซึ่งมีลักษณะคล้ายกับนี้:

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

ตอนนี้ถ้า bash shell ที่เรียกใช้สคริปต์ได้รับสัญญาณ 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จะละเว้นสัญญาณใด ๆ ในขณะที่กระบวนการลูกกำลังดำเนินการ การเริ่มต้นเซิร์ฟเวอร์ด้วย&จะทำให้พื้นหลังเป็นระบบควบคุมงานของเชลล์โดย$!ถือ PID ของเซิร์ฟเวอร์ไว้ (เพื่อใช้กับwaitและkill) โทรwaitแล้วจะรอให้งานที่มีระบุ PID (เซิร์ฟเวอร์) ที่จะเสร็จสิ้นหรือสัญญาณใด ๆ ที่จะถูกไล่ออก

เมื่อเชลล์ได้รับSIGTERM(หรือเซิร์ฟเวอร์ออกโดยอิสระ) การwaitโทรจะส่งคืน (ออกด้วยรหัสทางออกของเซิร์ฟเวอร์หรือด้วยหมายเลขสัญญาณ + 128 ในกรณีที่ได้รับสัญญาณ) หลังจากนั้นถ้าเชลล์ได้รับ SIGTERM มันจะเรียกใช้_termฟังก์ชันที่ระบุว่าเป็นตัวจัดการกับดัก SIGTERM ก่อนที่จะออก (ซึ่งเราทำการล้างข้อมูลใด ๆ และเผยแพร่สัญญาณไปยังกระบวนการเซิร์ฟเวอร์ด้วยตนเองkill)


ดูดี! ฉันจะลองและตอบกลับเมื่อฉันทดสอบ
Lorenz

7
แต่exec แทนที่เชลล์ด้วยโปรแกรมที่กำหนดฉันไม่ชัดเจนว่าทำไมwaitจึงจำเป็นต้องใช้การเรียกครั้งต่อไป?
iruvar

5
ฉันคิดว่าจุดของ 1_CR นั้นถูกต้อง ไม่ว่าคุณจะใช้เพียงแค่exec /bin/start/main/server --nodaemon(ในกรณีนี้กระบวนการเชลล์จะถูกแทนที่ด้วยกระบวนการเซิร์ฟเวอร์และคุณไม่จำเป็นต้องเผยแพร่สัญญาณใด ๆ ) หรือคุณใช้/bin/start/main/server --nodaemon &แต่ก็execไม่มีความหมายจริงๆ
Andreas Veithen

2
หากคุณต้องการให้เชลล์สคริปต์ของคุณสิ้นสุดลงหลังจากที่เด็กถูกยกเลิกใน_term()ฟังก์ชันที่คุณควรทำwait "$child"อีกครั้ง สิ่งนี้อาจจำเป็นถ้าคุณมีกระบวนการการดูแลอื่น ๆ ที่รอให้เชลล์สคริปต์ตายก่อนที่จะรีสตาร์ทอีกครั้งหรือถ้าคุณติดEXITกับการล้างข้อมูลและทำมันให้ทำงานหลังจากกระบวนการลูกเสร็จสิ้น
LeoRochael

1
@AlexanderMills อ่านคำตอบอื่น ๆ ไม่ว่าคุณกำลังมองหาexecหรือคุณต้องการที่จะติดตั้งกับดัก
Stuart P. Bentley

78

Bash ไม่ส่งต่อสัญญาณเช่น SIGTERM ไปยังกระบวนการที่กำลังรออยู่ ถ้าคุณต้องการที่จะจบสคริปต์ของคุณโดยแบ่งออกเป็นเซิร์ฟเวอร์ของคุณ (อนุญาตให้จัดการสัญญาณและสิ่งอื่น ๆ เช่นถ้าคุณเริ่มต้นเซิร์ฟเวอร์โดยตรง) คุณควรใช้execซึ่งจะแทนที่เชลล์ด้วยกระบวนการที่เปิด :

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

หากคุณจำเป็นต้องเก็บเปลือกหอยที่อยู่รอบ ๆ ด้วยเหตุผลบางอย่าง (เช่น. คุณต้องทำล้างบางหลังจากที่เซิร์ฟเวอร์สิ้นสุด) คุณควรใช้การรวมกันของtrap, และwait killดูคำตอบของ SensorSmith


นี่คือคำตอบที่ถูกต้อง! กระชับมากขึ้นและตอบคำถามเดิมของ OP อย่างแน่นอน
BrDaHa

20

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

2
งานที่ยอดเยี่ยม - ผมได้ปรับปรุงการเชื่อมโยงในคำตอบของฉันที่จะชี้นี่ (บนนี้เป็นวิธีการแก้ปัญหาที่ครอบคลุมมากขึ้นผมยังน้อยเป็นอันมากว่า StackExchange UI ไม่ให้เครดิตผมในคำตอบ cuonglm สำหรับการแก้ไขสคริปต์เพื่อ จริงๆแล้วทำในสิ่งที่ควรจะเป็นและเขียนข้อความอธิบายทั้งหมดหลังจาก OP ที่ไม่เข้าใจแม้แต่น้อยก็ทำการแก้ไขใหม่)
Stuart P. Bentley

2
@ StuartP.Bentley ขอบคุณ ผมก็แปลกใจประกอบนี้ต้องสอง (ไม่รับ) คำตอบและการอ้างอิงภายนอกและจากนั้นผมต้องวิ่งลงสภาพการแข่งขัน ฉันจะอัปเกรดการอ้างอิงเป็นลิงก์เป็นความรุ่งโรจน์เพิ่มเติมที่ฉันสามารถให้ได้
SensorSmith

3

โซลูชันที่ให้มาใช้ไม่ได้สำหรับฉันเนื่องจากกระบวนการถูกฆ่าก่อนที่คำสั่ง 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=$?
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.