Bash - จำกัด subshells ในการเรียกซ้ำ
ฉันตัดสินใจทดสอบการเรียกซ้ำใน bash บนเซิร์ฟเวอร์การผลิตของฉัน
อย่ารันโค้ดนี้บนเครื่องผลิต!
#!/usr/bin/bash
function fibo {
if [ $1 -le 1 ]; then echo $1
else
echo $(( $( fibo $(( $1 - 1 )) ) +
$( fibo $(( $1 - 2 )) )
))
fi
}
fibo 100
ฉันสงสัยว่ามันจะช้า แต่ไม่ได้คาดหวังว่ามันจะทำงานคู่ขนานกันหลายสิบงาน ฉันคิดว่าเนื่องจากเซิร์ฟเวอร์เป็นแบบมัลติคอร์ฉันจึงสามารถฆ่ากระบวนการนี้ได้
น่าเสียดายที่เซิร์ฟเวอร์ค้างและจาก บริษัท โฮสติ้งเริ่มต้นใหม่
มีวิธี จำกัด งานคู่ขนานใน bash หรือไม่?
เป็นไปได้หรือไม่ที่จะตั้งค่า PID ของกระบวนการหลักในตัวจับเวลาและหยุดงานย่อยทั้งหมด
แน่นอนฉันขอเพื่อจุดประสงค์ทางวิชาการเท่านั้น
แก้ไข
หาง recursionรุ่นทำงานเช่นเสน่ห์:
#!/usr/bin/bash
fibo() {
local n=$1 a=$2 b=$3
case $n in 0) echo "$a" ;;
1) echo "$b" ;; *) fibo $((n - 1)) "$b" $((a + b)) ;;
esac
}
fibo "${1:-10}" 0 1
เกณฑ์มาตรฐาน:
time ./fibotr.sh 100
3736710778780434371
real 0m0.078s
user 0m0.000s
sys 0m0.015s
คำตอบ
สำหรับวิธีการป้องกันดูเว็บไซต์น้องสาวของเราUnix และ Linux Stack แลกเปลี่ยน ท่ามกลางคำถามเฉพาะที่พวกเขามีในสถานที่ที่มีการดำเนินการระเบิดส้อมอย่างปลอดภัย
ในฐานะที่เป็นการนำอัลกอริทึมดั้งเดิมของคุณที่ไม่แยกส่วนย่อยใด ๆ ออกเลยดังนั้นจึงไม่สามารถเปลี่ยนเป็นระเบิดส้อมได้แม้ว่าจะส่งผ่านตัวเลขจำนวนมากโดยพลการ:
#!/usr/bin/env bash
declare -g _fibo_resA _fibo_resB # be explicit that it's intentional that these
# variables aren't locals, for future readers
fibo() {
local _fibo_outvar _fibo_inVal _fibo_locResA _fibo_locResB
_fibo_outvar=$1; _fibo_inVal=$2
if [ "$_fibo_inVal" -le 1 ]; then printf -v "$_fibo_outvar" 1
else
fibo _fibo_resA "$(( _fibo_inVal - 1 ))"; _fibo_locResA=$_fibo_resA
fibo _fibo_resB "$(( _fibo_inVal - 2 ))"; _fibo_locResB=$_fibo_resB
printf -v "$_fibo_outvar" "$(( _fibo_locResA + _fibo_locResB ))"
fi
}
# note that 100 is way too big; performance breaks down between 20 and 30
# but at least it won't act like a fork bomb!
fibo result "${1:-100}" # let user pass in a value they choose echo "$result"
เหตุใดจึงเขียนอย่างนี้?
- การเปลี่ยนแปลงที่สำคัญที่สุดคือการกำจัด
$( )แต่สิ่งนี้ไม่จำเป็นต้องส่งเอาต์พุตไปยังพาเรนต์บน stdout อีกต่อไป ดังนั้นโดยใช้การมอบหมายทางอ้อมเพื่อวัตถุประสงค์ - การใช้ชื่อตัวแปร namespaced (แม้กระทั่งสำหรับคนในพื้นที่!) ป้องกันความขัดแย้งเมื่อส่งคืนผลลัพธ์ตามชื่อ: หากผู้ใช้เลือกที่จะเขียนผลลัพธ์ไปยังชื่อตัวแปรที่ตรงกับท้องถิ่นของเราค่าของมันจะถูกเขียนลงในโลคัลไม่ใช่ ตัวแปรส่วนกลางที่ต้องการให้ผลลัพธ์วางไว้
locResAและlocResBถูกนำมาใช้เนื่องจากเราไม่ได้ใช้ส้อมและไม่สามารถสร้างได้resAและresBในท้องถิ่น ป้องกันไม่ให้สำเนาอื่น ๆfiboเขียนทับผลลัพธ์ก่อนที่เราจะสามารถส่งคืนได้funcname() {รูปแบบการประกาศไวยากรณ์ถูกนำมาใช้เพราะfunction funcname {เป็น ksh ลัทธิ อย่างไรก็ตามใน ksh รูปแบบนี้จะเปลี่ยนพฤติกรรมของโค้ดภายในบล็อก ทุบตีไม่เคารพเจตนานั้น ดังนั้นการใช้ที่สอดคล้องกับ POSIXfuncname() {จึงมีความประหลาดใจน้อยกว่าสำหรับผู้ที่มาจากพื้นหลัง ksh และปรับปรุงความเข้ากันได้ของโค้ดของคุณกับเชลล์ POSIX อื่น ๆ ดูhttps://wiki.bash-hackers.org/scripting/obsolete (โปรดทราบว่าไวยากรณ์การประกาศฟังก์ชันมีการกล่าวถึงมากกว่าหนึ่งแห่ง)