Bash - จำกัด subshells ในการเรียกซ้ำ

Sep 22 2020

ฉันตัดสินใจทดสอบการเรียกซ้ำใน 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

คำตอบ

1 CharlesDuffy Sep 22 2020 at 11:51

สำหรับวิธีการป้องกันดูเว็บไซต์น้องสาวของเรา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 รูปแบบนี้จะเปลี่ยนพฤติกรรมของโค้ดภายในบล็อก ทุบตีไม่เคารพเจตนานั้น ดังนั้นการใช้ที่สอดคล้องกับ POSIX funcname() {จึงมีความประหลาดใจน้อยกว่าสำหรับผู้ที่มาจากพื้นหลัง ksh และปรับปรุงความเข้ากันได้ของโค้ดของคุณกับเชลล์ POSIX อื่น ๆ ดูhttps://wiki.bash-hackers.org/scripting/obsolete (โปรดทราบว่าไวยากรณ์การประกาศฟังก์ชันมีการกล่าวถึงมากกว่าหนึ่งแห่ง)