แก้ไขการล็อคในเชลล์สคริปต์หรือไม่


66

บางครั้งคุณต้องตรวจสอบให้แน่ใจว่ามีเพียงหนึ่งอินสแตนซ์ของเชลล์สคริปต์เท่านั้นที่ทำงานพร้อมกัน

ตัวอย่างเช่นงาน cron ซึ่งดำเนินการผ่าน crond ที่ไม่มีการล็อคด้วยตนเอง (เช่น Solaris crond เริ่มต้น)

รูปแบบทั่วไปที่จะใช้การล็อคคือรหัสดังนี้:

#!/bin/sh
LOCK=/var/tmp/mylock
if [ -f $LOCK ]; then            # 'test' -> race begin
  echo Job is already running\!
  exit 6
fi
touch $LOCK                      # 'set'  -> race end
# do some work
rm $LOCK

แน่นอนรหัสดังกล่าวมีสภาพการแข่งขัน มีหน้าต่างเวลาที่การประมวลผลของทั้งสองอินสแตนซ์สามารถเลื่อนไปข้างหลังบรรทัดที่ 3 ก่อนที่จะสามารถแตะ$LOCKไฟล์ได้

สำหรับงาน cron สิ่งนี้ไม่ใช่ปัญหาเพราะคุณมีช่วงเวลาเป็นนาทีระหว่างการเรียกใช้สองครั้ง

แต่สิ่งต่าง ๆ อาจผิดพลาดได้ - ตัวอย่างเช่นเมื่อ lockfile อยู่บนเซิร์ฟเวอร์ NFS - ที่ค้าง ในกรณีนั้นงาน cron หลายงานสามารถบล็อกบนบรรทัดที่ 3 และจัดคิว หากเซิร์ฟเวอร์ NFS มีการใช้งานอีกครั้งแล้วคุณมีฝูงร้ายกาจของงานการทำงานแบบคู่ขนาน

การค้นหาบนเว็บฉันพบเครื่องมือล็อกรันซึ่งดูเหมือนว่าเป็นทางออกที่ดีสำหรับปัญหานั้น ด้วยคุณใช้สคริปต์ที่ต้องล็อคเช่นนี้

$ lockrun --lockfile=/var/tmp/mylock myscript.sh

คุณสามารถใส่สิ่งนี้ลงใน wrapper หรือใช้จาก crontab ของคุณ

มันใช้lockf()(POSIX) ถ้ามีและกลับไปที่flock()(BSD) และlockf()การสนับสนุน NFS ควรค่อนข้างแพร่หลาย

มีทางเลือกอื่นอีกlockrunหรือเปล่า?

แล้ว cron daemons อื่นล่ะ? crond ที่พบได้ทั่วไปนั้นรองรับการล็อคอย่างมีเหตุผลหรือไม่? การดูอย่างรวดเร็วในหน้า man ของ Vixie Crond (ค่าเริ่มต้นในระบบ Debian / Ubuntu) ไม่แสดงอะไรเกี่ยวกับการล็อค

มันจะเป็นความคิดที่ดีที่จะรวมถึงเครื่องมือที่เหมือนlockrunเข้าไปcoreutils ?

ในความคิดของฉันมันใช้รูปแบบคล้ายกับtimeout, niceและเพื่อน ๆ


4
เป็นรูปธรรมและเพื่อประโยชน์ของผู้อื่นที่อาจพิจารณารูปแบบเริ่มต้นของคุณดีพอ (tm) รหัสเชลล์นั้นอาจเป็นไปได้ที่จะดักจับ TERM เพื่อที่จะลบ lockfile เมื่อkillแก้ไข และดูเหมือนว่าเป็นวิธีปฏิบัติที่ดีในการจัดเก็บ pid ของตัวเองใน lockfile แทนที่จะแค่แตะมัน
Ulrich Schwarz


@Shawn ไม่ได้พูดถึง crond และ NFS
maxschlepzig

คำถามที่เกี่ยวข้องกับ SO: stackoverflow.com/questions/185451/…
maxschlepzig

1
@Ulrich ล่าช้ามากการจัดเก็บ PID ใน NFS lockfile เพิ่มค่าน้อยมาก แม้การเพิ่มชื่อโฮสต์ยังคงไม่ได้จริงๆช่วยให้มีการตรวจสอบกระบวนการสด
roaima

คำตอบ:


45

ต่อไปนี้เป็นอีกวิธีในการล็อกในเชลล์สคริปต์ที่สามารถป้องกันสภาวะการแข่งขันที่คุณอธิบายไว้ข้างต้นซึ่งทั้งสองงานอาจส่งผ่านบรรทัดที่ 3 noclobberตัวเลือกจะทำงานใน ksh และ bash อย่าใช้set noclobberเพราะคุณไม่ควรใช้สคริปต์ใน csh / tcsh ;)

lockfile=/var/tmp/mylock

if ( set -o noclobber; echo "$$" > "$lockfile") 2> /dev/null; then

        trap 'rm -f "$lockfile"; exit $?' INT TERM EXIT

        # do stuff here

        # clean up after yourself, and release your trap
        rm -f "$lockfile"
        trap - INT TERM EXIT
else
        echo "Lock Exists: $lockfile owned by $(cat $lockfile)"
fi

YMMV ที่มีการล็อก NFS (คุณรู้ว่าเมื่อเซิร์ฟเวอร์ NFS ไม่สามารถเข้าถึงได้) แต่โดยทั่วไปแล้วจะมีประสิทธิภาพมากกว่าที่เคยเป็น (10 ปีที่แล้ว)

หากคุณมีงาน cron ที่ทำสิ่งเดียวกันในเวลาเดียวกันจากเซิร์ฟเวอร์หลายเครื่อง แต่คุณต้องการเพียง 1 อินสแตนซ์เท่านั้นที่จะทำงานได้จริงสิ่งที่อาจเป็นไปได้สำหรับคุณ

ฉันไม่มีประสบการณ์กับการใช้งาน lockrun แต่การมีสภาพแวดล้อมการล็อกที่กำหนดไว้ล่วงหน้าก่อนที่สคริปต์จะทำงานจริงอาจช่วยได้ หรืออาจจะไม่ คุณแค่ตั้งค่าการทดสอบสำหรับ lockfile นอกสคริปต์ของคุณใน wrapper และในทางทฤษฎีคุณไม่สามารถตีเงื่อนไขการแข่งขันเดียวกันได้หรือไม่หากงานสองงานถูกเรียกโดย lockrun ในเวลาเดียวกันเหมือนกับ 'ภายใน - แก้ปัญหาสคริปต์ '?

การล็อคไฟล์เป็นสิ่งที่น่ายกย่องมากสำหรับพฤติกรรมของระบบและสคริปต์ใด ๆ ที่ไม่ตรวจสอบการมีอยู่ของ lockfile ก่อนที่จะทำงานจะทำสิ่งที่พวกเขากำลังจะทำ เพียงแค่ใส่การทดสอบล็อคไฟล์และพฤติกรรมที่เหมาะสมคุณจะสามารถแก้ไขปัญหาที่อาจเกิดขึ้นได้ 99% หากไม่ใช่ 100%

หากคุณพบปัญหาการแย่งชิงไฟล์มากอาจเป็นตัวบ่งชี้ปัญหาที่ใหญ่กว่าเช่นไม่มีงานของคุณหมดเวลาหรือบางทีถ้าช่วงเวลาไม่สำคัญเท่ากับงานที่ทำเสร็จงานของคุณอาจจะดีกว่า .


แก้ไขด้านล่าง - 2016-05-06 (หากคุณใช้ KSH88)


ฐานในความคิดเห็น @Clint Pachl ด้านล่างถ้าคุณใช้ ksh88 ใช้แทนmkdir noclobberสิ่งนี้ส่วนใหญ่จะลดสภาพการแข่งขันที่อาจเกิดขึ้น แต่ไม่ได้ จำกัด ทั้งหมด (แม้ว่าความเสี่ยงจะน้อยกว่า) สำหรับข้อมูลเพิ่มเติมอ่านลิงค์ที่คลินต์โพสต์ด้านล่าง

lockdir=/var/tmp/mylock
pidfile=/var/tmp/mylock/pid

if ( mkdir ${lockdir} ) 2> /dev/null; then
        echo $$ > $pidfile
        trap 'rm -rf "$lockdir"; exit $?' INT TERM EXIT
        # do stuff here

        # clean up after yourself, and release your trap
        rm -rf "$lockdir"
        trap - INT TERM EXIT
else
        echo "Lock Exists: $lockdir owned by $(cat $pidfile)"
fi

และในฐานะที่เป็นข้อได้เปรียบที่เพิ่มเข้ามาหากคุณต้องการสร้าง tmpfiles ในสคริปต์ของคุณคุณสามารถใช้lockdirไดเรกทอรีสำหรับพวกเขาได้เพราะพวกเขารู้ว่าพวกเขาจะได้รับการทำความสะอาดเมื่อสคริปต์ออก

สำหรับทุบตีที่ทันสมัยกว่าวิธี noclobber ที่ด้านบนควรมีความเหมาะสม


1
ไม่ได้ด้วย lockrun คุณไม่มีปัญหา - เมื่อเซิร์ฟเวอร์ NFS แฮงค์การเรียกใช้ lockrun ทั้งหมดจะหยุดทำงาน (อย่างน้อย) ในการlockf()เรียกของระบบ - เมื่อสำรองข้อมูลกระบวนการทั้งหมดจะกลับมาทำงานต่อ แต่กระบวนการเดียวเท่านั้นที่จะชนะการล็อค ไม่มีสภาพการแข่งขัน ฉันไม่พบปัญหากับ cronjobs มาก - ตรงข้ามเป็นกรณี - แต่นี่เป็นปัญหาเมื่อมันกระทบคุณมันมีศักยภาพที่จะสร้างความเจ็บปวดมากมาย
maxschlepzig

1
ฉันยอมรับคำตอบนี้เพราะวิธีการนี้ปลอดภัยและทันสมัยที่สุด ฉันขอแนะนำตัวแปรย่อย: set -o noclobber && echo "$$" > "$lockfile"เพื่อให้ได้ทางเลือกที่ปลอดภัยเมื่อเชลล์ไม่สนับสนุนตัวเลือก noclobber
maxschlepzig

3
คำตอบที่ดี แต่คุณควร 'kill -0' ค่าใน lockfile เพื่อให้แน่ใจว่ากระบวนการที่สร้างการล็อคยังคงมีอยู่
Nigel Horne

1
noclobberตัวเลือกที่อาจจะมีแนวโน้มที่จะแข่งเงื่อนไข ดูmywiki.wooledge.org/BashFAQ/045สำหรับอาหารสำหรับความคิด
Clint Pachl

2
หมายเหตุ: การใช้noclobber(หรือ-C) ใน ksh88 ไม่ได้เพราะ ksh88 ไม่ได้ใช้สำหรับO_EXCL noclobberหากคุณกำลังใช้เชลล์รุ่นใหม่คุณอาจจะตกลง ...
jrw32982

14

ฉันชอบที่จะใช้ฮาร์ดลิงก์

lockfile=/var/lock/mylock
tmpfile=${lockfile}.$$
echo $$ > $tmpfile
if ln $tmpfile $lockfile 2>&-; then
    echo locked
else
    echo locked by $(<$lockfile)
    rm $tmpfile
    exit
fi
trap "rm ${tmpfile} ${lockfile}" 0 1 2 3 15
# do what you need to

เชื่อมโยงอย่างหนักเป็นอะตอมผ่าน NFSและส่วนใหญ่ที่mkdir เป็นเช่นกัน การใช้mkdir(2)หรือlink(2)ใกล้เคียงกันในระดับการปฏิบัติ; ฉันชอบที่จะใช้ฮาร์ดลิงก์เพราะการใช้ NFS ที่อนุญาตให้ฮาร์ดลิงก์อะตอมมิกมากกว่าอะตอมmkdirมิก ด้วย NFS รุ่นใหม่คุณไม่ต้องกังวลเรื่องการใช้งาน


12

ฉันเข้าใจว่าmkdirเป็นอะตอมดังนั้นอาจจะ:

lockdir=/var/tmp/myapp
if mkdir $lockdir; then
  # this is a new instance, store the pid
  echo $$ > $lockdir/PID
else
  echo Job is already running, pid $(<$lockdir/PID) >&2
  exit 6
fi

# then set traps to cleanup upon script termination 
# ref http://www.shelldorado.com/goodcoding/tempfiles.html
trap 'rm -r "$lockdir" >/dev/null 2>&1' 0
trap "exit 2" 1 2 3 13 15

ตกลง แต่ฉันไม่สามารถหาข้อมูลได้ว่าmkdir()เกิน NFS (> = 3) เป็นมาตรฐานที่เป็นอะตอม
maxschlepzig

2
@maxschlepzig RFC 1813ไม่ได้เรียกร้องอย่างชัดเจนว่าmkdirเป็นอะตอมมิก (มันทำเพื่อrename) ในทางปฏิบัติมันเป็นที่รู้กันว่าการใช้งานบางอย่างไม่ได้ ที่เกี่ยวข้อง: หัวข้อที่น่าสนใจรวมทั้งผลงานโดยผู้เขียนของ GNU โค้งที่
Gilles

8

วิธีที่ง่ายคือใช้งานได้ตามlockfileปกติกับprocmailแพ็คเกจ

LOCKFILE="/tmp/mylockfile.lock"
# try once to get the lock else exit
lockfile -r 0 "$LOCKFILE" || exit 0

# here the actual job

rm -f "$LOCKFILE"

5

semซึ่งมาเป็นส่วนหนึ่งของparallelเครื่องมือGNU อาจเป็นสิ่งที่คุณกำลังมองหา:

sem [--fg] [--id <id>] [--semaphoretimeout <secs>] [-j <num>] [--wait] command

ในขณะที่:

sem --id my_semaphore --fg "echo 1 ; date ; sleep 3" &
sem --id my_semaphore --fg "echo 2 ; date ; sleep 3" &
sem --id my_semaphore --fg "echo 3 ; date ; sleep 3" &

outputting:

1
Thu 10 Nov 00:26:21 UTC 2016
2
Thu 10 Nov 00:26:24 UTC 2016
3
Thu 10 Nov 00:26:28 UTC 2016

โปรดทราบว่าไม่รับประกันการสั่งซื้อ นอกจากนี้เอาต์พุตยังไม่ปรากฏขึ้นจนกว่าจะเสร็จสิ้น (เกิดการระคายเคือง!) แต่ถึงกระนั้นก็เป็นวิธีที่รัดกุมที่สุดที่ฉันรู้เพื่อป้องกันการดำเนินการพร้อมกันโดยไม่ต้องกังวลเกี่ยวกับ lockfiles และลองใหม่และการทำความสะอาด


การล็อคที่เสนอโดยsemจุดจับถูกลงกลางคันหรือไม่?
roaima


1

ฉันจะใช้เครื่องมือบรรทัดคำสั่ง "แห่" การจัดการล็อคในสคริปต์ทุบตีฉันตามที่อธิบายไว้ที่นี่และที่นี่ ฉันใช้วิธีการง่าย ๆ จาก flock manpage เพื่อเรียกใช้คำสั่งใน subshell ...

   (
     flock -n 9
     # ... commands executed under lock ...
   ) 9>/var/lock/mylockfile

ในตัวอย่างนั้นมันล้มเหลวด้วยรหัสออก 1 ถ้ามันไม่สามารถรับ lockfile แต่ฝูงก็ยังสามารถใช้ในรูปแบบที่ไม่ต้องการคำสั่งให้ทำงานใน sub-shell :-)


3
flock()เรียกระบบไม่ทำงานมากกว่า NFS
maxschlepzig

1
BSD มีเครื่องมือที่คล้ายกัน "lockf"
dubiousjim

2
@dubiousjim, BSD lockfยังเรียกใช้flock()และทำให้เกิดปัญหากับ NFS Btw ในระหว่างนี้ flock () บน Linux จะกลับมาอีกครั้งfcntl()เมื่อไฟล์นั้นอยู่บน NFS mount ดังนั้นในสภาพแวดล้อม NFS ที่ใช้ Linux เท่านั้นflock()จะทำงานได้บน NFS
maxschlepzig

1

อย่าใช้ไฟล์

หากสคริปต์ของคุณถูกดำเนินการเช่นนี้เช่น:

bash my_script

คุณสามารถตรวจสอบได้ว่ามันใช้งานอยู่หรือไม่:

running_proc=$(ps -C bash -o pid=,cmd= | grep my_script);
if [[ "$running_proc" != "$$ bash my_script" ]]; do 
  echo Already locked
  exit 6
fi

หืมรหัสการตรวจสอบ ps ทำงานจากภายในmy_script? ในกรณีที่อินสแตนซ์อื่นกำลังทำงานอยู่ - ไม่มีrunning_procสองบรรทัดที่ตรงกันหรือไม่ ฉันชอบความคิด แต่แน่นอน - คุณจะได้รับผลลัพธ์ที่ผิดพลาดเมื่อผู้ใช้รายอื่นกำลังเรียกใช้สคริปต์ด้วยชื่อเดียวกัน ...
maxschlepzig

3
นอกจากนี้ยังรวมถึงสภาพการแข่งขัน: หาก 2 อินสแตนซ์ดำเนินการบรรทัดแรกในแบบคู่ขนานไม่มีใครได้รับ 'ล็อค' และทั้งสองออกจากสถานะ 6 นี้จะเป็นชนิดของความอดอยากร่วมกันรอบเดียว Btw ฉันไม่แน่ใจว่าทำไมคุณใช้$!แทน$$ตัวอย่างของคุณ
maxschlepzig

@maxschlepzig ขอโทษจริง ๆ เกี่ยวกับ $ ไม่ถูกต้อง! vs. $$
frogstarr78

@maxschlepzig เพื่อจัดการผู้ใช้หลายคนที่เรียกใช้สคริปต์เพิ่ม euser = ไปยังอาร์กิวเมนต์ -o
frogstarr78

@maxschlepzig เพื่อป้องกันไม่ให้หลายบรรทัดคุณสามารถเปลี่ยนอาร์กิวเมนต์เป็น grep หรือ "ตัวกรอง" เพิ่มเติม (เช่นgrep -v $$) โดยทั่วไปฉันพยายามที่จะให้แนวทางที่แตกต่างกับปัญหา
frogstarr78

1

สำหรับการใช้งานจริงคุณควรใช้คำตอบที่ได้รับคะแนนสูงสุด

อย่างไรก็ตามฉันต้องการพูดคุยเกี่ยวกับวิธีการที่แตกต่างกันและกึ่งใช้งานได้และการใช้psคำเตือนที่พวกเขามีเพราะฉันเห็นคนใช้พวกเขา

คำตอบนี้เป็นคำตอบของ"ทำไมไม่ใช้psและgrepจัดการกับการล็อกในเชลล์"

วิธีการแตก # 1

ขั้นแรกให้แนวทางในอีกคำตอบหนึ่งที่มี upvotes ไม่กี่คนแม้ว่าจะไม่ได้ทำงาน (และไม่สามารถทำได้) และไม่เคยทดสอบอย่างชัดเจน:

running_proc=$(ps -C bash -o pid=,cmd= | grep my_script);
if [[ "$running_proc" != "$$ bash my_script" ]]; do 
  echo Already locked
  exit 6
fi

มาแก้ไขข้อผิดพลาดทางไวยากรณ์และpsข้อโต้แย้งที่ขาดและได้รับ:

running_proc=$(ps -C bash -o pid,cmd | grep "$0");
echo "$running_proc"
if [[ "$running_proc" != "$$ bash $0" ]]; then
  echo Already locked
  exit 6
fi

สคริปต์นี้จะมักจะออกจาก 6 ทุกครั้งไม่ว่าคุณจะใช้มัน

ถ้าคุณเรียกมันด้วย./myscriptแล้วpsการส่งออกจะเป็นเพียง12345 -bashซึ่งไม่ตรงกับสตริงที่จำเป็น12345 bash ./myscriptเพื่อที่จะล้มเหลว

ถ้าคุณใช้มันbash myscriptสิ่งต่าง ๆ น่าสนใจยิ่งขึ้น กระบวนการทุบตีส้อมที่จะเรียกใช้ท่อและเด็กเปลือกทำงานและps grepทั้งเปลือกดั้งเดิมและเปลือกลูกจะปรากฏขึ้นในpsผลลัพธ์บางอย่างเช่นนี้:

25793 bash myscript
25795 bash myscript

นั่นไม่ใช่เอาต์พุตที่คาดไว้$$ bash $0ดังนั้นสคริปต์ของคุณจะออก

วิธีการแตก # 2

ในความเป็นธรรมกับผู้ใช้ที่เขียนวิธีการที่ไม่เหมาะสม # 1 ฉันทำสิ่งที่คล้ายกันเมื่อฉันลองทำสิ่งนี้เป็นครั้งแรก:

if otherpids="$(pgrep -f "$0" | grep -vFx "$$")" ; then
  echo >&2 "There are other copies of the script running; exiting."
  ps >&2 -fq "${otherpids//$'\n'/ }" # -q takes about a tenth the time as -p
  exit 1
fi

นี้เกือบงาน แต่ความจริงแล้วการฟอร์กเพื่อให้ท่อวิ่งออกไป ดังนั้นอันนี้ก็จะออกเช่นกัน

แนวทางที่ไม่น่าเชื่อถือ # 3

pids_this_script="$(pgrep -f "$0")"
if not_this_process="$(echo "$pids_this_script" | grep -vFx "$$")"; then
  echo >&2 "There are other copies of this script running; exiting."
  ps -fq "${not_this_process//$'\n'/ }"
  exit 1
fi

รุ่นนี้หลีกเลี่ยงการฟอร์กปัญหาในวิธี # 2 โดยรับ PID ทั้งหมดที่มีสคริปต์ปัจจุบันในอาร์กิวเมนต์บรรทัดคำสั่งของพวกเขาแล้วกรอง pidlist นั้นแยกจากกันเพื่อละเว้น PID ของสคริปต์ปัจจุบัน

สิ่งนี้อาจใช้งานได้ ... หากไม่มีกระบวนการอื่นใดที่มีบรรทัดคำสั่งที่ตรงกับ$0และการให้สคริปต์จะเรียกว่าวิธีเดียวกันเสมอ (เช่นถ้ามันถูกเรียกด้วยเส้นทางสัมพัทธ์แล้วเส้นทางสัมบูรณ์อินสแตนซ์หลังจะไม่สังเกตเห็นอดีต )

แนวทางที่ไม่น่าเชื่อถือ # 4

ดังนั้นถ้าเราข้ามการตรวจสอบบรรทัดคำสั่งแบบเต็มเนื่องจากอาจไม่ได้ระบุว่าสคริปต์กำลังทำงานจริงและตรวจสอบlsofแทนเพื่อค้นหากระบวนการทั้งหมดที่มีสคริปต์นี้เปิดอยู่

ใช่วิธีนี้ไม่เลวจริง ๆ :

if otherpids="$(lsof -t "$0" | grep -vFx "$$")"; then
  echo >&2 "Error: There are other processes that have this script open - most likely other copies of the script running.  Exiting to avoid conflicts."
  ps >&2 -fq "${otherpids//$'\n'/ }"
  exit 1
fi

แน่นอนหากสำเนาของสคริปต์ทำงานอยู่อินสแตนซ์ใหม่จะเริ่มต้นได้ดีและคุณจะมีสองชุดที่ทำงานอยู่

หรือหากสคริปต์ที่ใช้งานได้รับการแก้ไข (เช่นกับ Vim หรือ a git checkout) ดังนั้นเวอร์ชั่น "ใหม่" ของสคริปต์จะเริ่มต้นโดยไม่มีปัญหาเนื่องจากทั้ง Vim และgit checkoutผลลัพธ์เป็นไฟล์ใหม่ (inode ใหม่) แทนที่ อันเก่า.

อย่างไรก็ตามหากสคริปต์ไม่เคยถูกแก้ไขและไม่เคยคัดลอกดังนั้นเวอร์ชั่นนี้ค่อนข้างดี ไม่มีเงื่อนไขการแข่งขันเนื่องจากไฟล์สคริปต์ต้องเปิดอยู่ก่อนที่จะถึงการตรวจสอบ

อาจยังมีผลบวกปลอมหากกระบวนการอื่นเปิดไฟล์สคริปต์ แต่โปรดทราบว่าแม้ว่าจะเปิดให้แก้ไขใน Vim ได้ แต่ vim ไม่ได้เปิดไฟล์สคริปต์ไว้จริง ๆ ดังนั้นจะไม่มีผลบวกปลอม

แต่จำไว้ว่าอย่าใช้วิธีนี้หากสคริปต์อาจถูกแก้ไขหรือคัดลอกเนื่องจากคุณจะได้รับเนกาทีฟที่ผิดพลาดเช่นมีหลายอินสแตนซ์ที่ทำงานพร้อมกันดังนั้นความจริงที่ว่าการแก้ไขด้วย Vim นั้นไม่ได้ให้ผลเชิงบวกที่ผิด ถึงคุณ. ผมพูดถึงมัน แต่เนื่องจากวิธีการ # 3 ไม่ให้ผลบวกปลอม (เช่นปฏิเสธที่จะเริ่มต้น) ถ้าคุณมีสคริปต์ที่เปิดกับกลุ่ม

แล้วจะทำอย่างไรดี?

คำตอบด้านบนลงมติให้คำถามนี้ให้เป็นวิธีการที่ดีที่เป็นของแข็ง

บางทีคุณสามารถเขียนดีกว่า ... แต่ถ้าคุณไม่เข้าใจปัญหาและข้อแม้ทั้งหมดด้วยวิธีการข้างต้นคุณไม่น่าจะเขียนวิธีการล็อคที่หลีกเลี่ยงพวกเขาทั้งหมด


0

การใช้เครื่องมือ FLOM (Free Lock Manager) การทำให้คำสั่งเป็นอนุกรมกลายเป็นเรื่องง่ายเหมือนการทำงาน

flom -- command_to_serialize

FLOM ช่วยให้คุณสามารถใช้กรณีการใช้งานที่ซับซ้อนมากขึ้น (การล็อกแบบกระจายผู้อ่าน / ผู้เขียนทรัพยากรตัวเลข ฯลฯ ... ) ตามที่อธิบายไว้ที่นี่: http://sourceforge.net/p/flom/wiki/FLOM%20by%20examples/


0

นี่คือสิ่งที่ฉันเพิ่มในเซิร์ฟเวอร์บางครั้งเพื่อจัดการกับสภาพการแข่งขันสำหรับงานใด ๆ บนเครื่อง มันเป็นสิ่งที่คล้ายกับโพสต์ของ Tim Kennedy แต่วิธีนี้คุณจะได้รับการจัดการการแข่งขันโดยเพิ่มเพียงหนึ่งแถวในสคริปต์ทุบตีที่ต้องการ

ใส่เนื้อหาด้านล่างใน / opt / racechecker / racechecker:

ZPROGRAMNAME=$(readlink -f $0)
EZPROGRAMNAME=`echo $ZPROGRAMNAME | sed 's/\//_/g'`
EZMAIL="/usr/bin/mail"
EZCAT="/bin/cat"

if  [ -n "$EZPROGRAMNAME" ] ;then
        EZPIDFILE=/tmp/$EZPROGRAMNAME.pid
        if [ -e "$EZPIDFILE" ] ;then
                EZPID=$($EZCAT $EZPIDFILE)
                echo "" | $EZMAIL -s "$ZPROGRAMNAME already running with pid $EZPID"  alarms@someemail.com >>/dev/null
                exit -1
        fi
        echo $$ >> $EZPIDFILE
        function finish {
          rm  $EZPIDFILE
        }
        trap finish EXIT
fi

นี่คือวิธีการใช้งาน สังเกตแถวหลัง shebang:

     #/bin/bash
     . /opt/racechecker/racechecker
     echo "script are running"
     sleep 120

วิธีการทำงานคือการระบุชื่อไฟล์ bashscript หลักและสร้าง pidfile ภายใต้ "/ tmp" นอกจากนี้ยังเพิ่มฟังให้สัญญาณเสร็จ ผู้ฟังจะลบ pidfile เมื่อสคริปต์หลักเสร็จสิ้นอย่างเหมาะสม

แต่ถ้า pidfile นั้นมีอยู่เมื่อเปิดอินสแตนซ์ดังนั้นคำสั่ง if ที่มีรหัสอยู่ภายใน if-second จะถูกดำเนินการ ในกรณีนี้ฉันได้ตัดสินใจเปิดจดหมายเตือนภัยเมื่อเกิดเหตุการณ์นี้

เกิดอะไรขึ้นถ้าสคริปต์ขัดข้อง

การออกกำลังกายต่อไปคือการจัดการกับความผิดพลาด เป็นการดีที่ควรลบ pidfile แม้ว่าสคริปต์หลักขัดข้องไม่ว่าด้วยเหตุผลใดก็ตามสิ่งนี้ไม่ได้ทำในเวอร์ชันของฉันด้านบน ซึ่งหมายความว่าหากสคริปต์ขัดข้อง pidfile จะต้องถูกลบออกด้วยตนเองเพื่อเรียกคืนการทำงาน

ในกรณีที่ระบบขัดข้อง

เป็นความคิดที่ดีที่จะเก็บไฟล์ pidfile / lockfile ไว้ใต้ / tmp วิธีนี้สคริปต์ของคุณจะดำเนินการต่อไปอย่างแน่นอนหลังจากระบบล่มเนื่องจาก pidfiles จะถูกลบในการบูต


ซึ่งแตกต่างจาก ansatz ของ Tim Kennedy สคริปต์ของคุณมีเงื่อนไขการแข่งขัน นี่เป็นเพราะคุณตรวจสอบสถานะของ PIDFILE และการสร้างแบบมีเงื่อนไขไม่ได้ทำในการดำเนินการแบบอะตอมมิก
maxschlepzig

+1 บนนั้น! ฉันจะพิจารณาเรื่องนี้และแก้ไขสคริปต์ของฉัน
ziggestardust

-2

ตรวจสอบสคริปต์ของฉัน ...

คุณอาจรักมัน ....

[rambabu@Server01 ~]$ sh Prevent_cron-OR-Script_against_parallel_run.sh
Parallel RUN Enabled
Now running
Task completed in Parallel RUN...
[rambabu@Server01 ~]$ cat Prevent_cron-OR-Script_against_parallel_run.sh
#!/bin/bash
#Created by RambabuKella
#Date : 12-12-2013

#LOCK file name
Parallel_RUN="yes"
#Parallel_RUN="no"
PS_GREP=0
LOCK=/var/tmp/mylock_`whoami`_"$0"
#Checking for the process
PS_GREP=`ps -ef |grep "sh $0" |grep -v grep|wc -l`
if [ "$Parallel_RUN" == "no" ] ;then
echo "Parallel RUN Disabled"

 if [ -f $LOCK ] || [ $PS_GREP -gt 2   ] ;then
        echo -e "\nJob is already running OR LOCK file exists. "
        echo -e "\nDetail are : "
        ps -ef |grep  "$0" |grep -v grep
        cat "$LOCK"
  exit 6
 fi
echo -e "LOCK file \" $LOCK \" created on : `date +%F-%H-%M` ." &> $LOCK
# do some work
echo "Now running"
echo "Task completed on with single RUN ..."
#done

rm -v $LOCK 2>/dev/null
exit 0
else

echo "Parallel RUN Enabled"

# do some work
echo "Now running"
echo "Task completed in Parallel RUN..."
#done

exit 0
fi
echo "some thing wrong"
exit 2
[rambabu@Server01 ~]$

-3

ฉันเสนอวิธีแก้ไขปัญหาต่อไปนี้ในสคริปต์ชื่อ 'flocktest'

#!/bin/bash
export LOGFILE=`basename $0`.logfile
logit () {
echo "$1" >>$LOGFILE
}
PROGPATH=$0
(
flock -x -n 257
(($?)) && logit "'$PROGPATH' is already running!" && exit 0
logit "'$PROGPATH', proc($$): sleeping 30 seconds"
sleep 30
)257<$PROGPATH
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.