ฉันจะแสดงสคริปต์โดยพลการใน unix ได้อย่างไร


94

ฉันต้องการ daemonizer ที่สามารถเปิดโดยพลสคริปต์ทั่วไปหรือสั่งเข้ามาเป็นภูต

มีสองกรณีทั่วไปที่ฉันต้องการจัดการ:

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

  2. ฉันมีสคริปต์หรือคำสั่งบรรทัดคำสั่งง่ายๆที่ฉันต้องการดำเนินการซ้ำ ๆ ตลอดไป (โดยหยุดชั่วคราวระหว่างการรัน) อีกครั้งอย่าปล่อยให้สคริปต์สองสำเนาทำงานพร้อมกัน

แน่นอนว่ามันเป็นเรื่องเล็กน้อยที่จะเขียน "while (true)" วนรอบสคริปต์ในกรณีที่ 2 จากนั้นใช้โซลูชันสำหรับกรณีที่ 1 แต่วิธีแก้ปัญหาทั่วไปจะแก้ปัญหากรณีที่ 2 โดยตรงเนื่องจากใช้กับสคริปต์ในกรณีที่ 1 เป็น ดี (คุณอาจต้องการหยุดชั่วคราวให้สั้นลงหรือไม่มีเลยหากสคริปต์ไม่ได้ตั้งใจที่จะตาย (แน่นอนว่าถ้าสคริปต์ไม่มีวันตายจริงๆการหยุดชั่วคราวก็ไม่สำคัญ))

โปรดทราบว่าโซลูชันไม่ควรเกี่ยวข้องกับการเพิ่มรหัสล็อกไฟล์หรือการบันทึก PID ลงในสคริปต์ที่มีอยู่

โดยเฉพาะอย่างยิ่งฉันต้องการโปรแกรม "daemonize" ที่สามารถเรียกใช้งานได้

% daemonize myscript arg1 arg2

หรือตัวอย่างเช่น

% daemonize 'echo `date` >> /tmp/times.txt'

ซึ่งจะทำให้รายการวันที่เพิ่มขึ้นต่อท้าย times.txt (โปรดสังเกตว่าถ้าอาร์กิวเมนต์ที่จะ daemonize เป็นสคริปต์ที่ทำงานตลอดไปเหมือนในกรณีที่ 1 ข้างต้น daemonize จะยังคงทำสิ่งที่ถูกต้องเริ่มต้นใหม่เมื่อจำเป็น) จากนั้นฉันสามารถใส่คำสั่งดังกล่าวข้างต้นใน. login ของฉันได้ และ / หรือ cron ทุกชั่วโมงหรือนาที (ขึ้นอยู่กับว่าฉันกังวลแค่ไหนที่มันจะตายโดยไม่คาดคิด)

หมายเหตุ: สคริปต์ daemonize จะต้องจำสตริงคำสั่งที่เป็น daemonizing ดังนั้นถ้าสตริงคำสั่งเดียวกันถูก daemonized อีกครั้งจะไม่เรียกใช้สำเนาที่สอง

นอกจากนี้โซลูชันควรใช้ได้กับทั้ง OS X และ linux แต่ยินดีต้อนรับโซลูชันสำหรับอย่างใดอย่างหนึ่ง

แก้ไข: sudo daemonize myscript myargsมันเป็นเรื่องดีถ้าคุณมีการเรียกมันด้วย

(ถ้าฉันคิดว่าทั้งหมดนี้ผิดหรือมีวิธีแก้ปัญหาบางส่วนที่รวดเร็วและสกปรกฉันก็ชอบที่จะได้ยินเช่นกัน)


PS: ในกรณีที่มีประโยชน์นี่เป็นคำถามที่คล้ายกันสำหรับ python

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


1
ดูserverfault.com/questions/311593/…สำหรับเวอร์ชันเพียวเชลล์
w00t

คำตอบ:


93

คุณสามารถแสดงผลปฏิบัติการใด ๆ ใน Unix โดยใช้ nohup และตัวดำเนินการ &:

nohup yourScript.sh script args&

คำสั่ง nohup ช่วยให้คุณสามารถปิดเซสชันเชลล์ของคุณได้โดยไม่ต้องฆ่าสคริปต์ของคุณในขณะที่ & วางสคริปต์ของคุณในพื้นหลังเพื่อให้คุณได้รับคำสั่งเชลล์เพื่อดำเนินการต่อเซสชันของคุณ ปัญหาเล็กน้อยเพียงอย่างเดียวของปัญหานี้คือ standard out และข้อผิดพลาดมาตรฐานทั้งสองจะถูกส่งไปที่. / nohup.out ดังนั้นหากคุณเริ่มสคริปต์หลาย ๆ สคริปต์ในคฤหาสน์นี้เอาต์พุตของมันจะถูกพันกัน คำสั่งที่ดีกว่าคือ:

nohup yourScript.sh script args >script.out 2>script.error&

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

nohup yourScript.sh script args >script.out 2>&1 &

2> & 1 บอกให้เชลล์เปลี่ยนเส้นทางข้อผิดพลาดมาตรฐาน (file descriptor 2) ไปยังไฟล์เดียวกันกับ standard out (file descriptor 1)

ในการรันคำสั่งเพียงครั้งเดียวและรีสตาร์ทหากตายคุณสามารถใช้สคริปต์นี้:

#!/bin/bash

if [[ $# < 1 ]]; then
    echo "Name of pid file not given."
    exit
fi

# Get the pid file's name.
PIDFILE=$1
shift

if [[ $# < 1 ]]; then
    echo "No command given."
    exit
fi

echo "Checking pid in file $PIDFILE."

#Check to see if process running.
PID=$(cat $PIDFILE 2>/dev/null)
if [[ $? = 0 ]]; then
    ps -p $PID >/dev/null 2>&1
    if [[ $? = 0 ]]; then
        echo "Command $1 already running."
        exit
    fi
fi

# Write our pid to file.
echo $$ >$PIDFILE

# Get command.
COMMAND=$1
shift

# Run command until we're killed.
while true; do
    $COMMAND "$@"
    sleep 10 # if command dies immediately, don't go into un-ctrl-c-able loop
done

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

หากคุณตั้งชื่อสคริปต์นี้ว่า restart.sh นี่คือวิธีที่คุณจะเรียกมัน:

nohup restart.sh pidFileName yourScript.sh script args >script.out 2>&1 &

น่ากลัว; ขอขอบคุณ. ฉันสงสัยว่ามันควรมีตัวเลือกสำหรับความล่าช้าในการรีสตาร์ทหรือไม่ หรืออาจจะดีกว่าถ้าใช้ร่วมกับสิ่งนี้ stackoverflow.com/questions/555116/…
dreeves

4
สิ่งนี้จัดการเฉพาะ SIGHUP มีสัญญาณร้ายแรงอื่น ๆ (ปกติ) ที่ควรได้รับการจัดการ
Tim Post

อีกวิธีหนึ่งในการปรับปรุงสคริปต์นี้ก็คือการสร้างตำแหน่งที่ดีในการวาง $ PIDFILE ด้วยตัวมันเองแทนที่จะกำหนดให้ระบุเป็นอาร์กิวเมนต์ มันไม่ได้ทำความสะอาดหลังจากตัวมันเอง! (ซึ่งควรจะตรงไปตรงมากับ a trap EXIT)
Steven Lu

นอกจากนี้ให้พิจารณาว่าการใช้<ในtestเป็นการเปรียบเทียบ ASCII ไม่ใช่การเปรียบเทียบจำนวนเต็ม อาจยังใช้งานได้ แต่อาจทำให้เกิดข้อบกพร่องได้
Steven Lu

ฉันโพสต์การแก้ไขสคริปต์นี้ที่นี่แล้ว
Steven Lu

34

ฉันขอโทษสำหรับคำตอบที่ยาว (โปรดดูความคิดเห็นเกี่ยวกับคำตอบของฉันว่าเป็นอย่างไร) ฉันพยายามพูดให้ครอบคลุมเพื่อให้คุณมีขาที่ดีที่สุดเท่าที่จะทำได้ :-)

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

มันเกี่ยวข้องกับการใช้daemontools ส่วนที่เหลือของโพสต์อธิบายวิธีตั้งค่าบริการโดยใช้ daemontools

ตั้งค่าเริ่มต้น

  1. ตามคำแนะนำในวิธีการติดตั้ง daemontools การแจกแจงบางอย่าง (เช่น Debian, Ubuntu) มีแพ็คเกจอยู่แล้วดังนั้นให้ใช้สิ่งนั้น
  2. /serviceทำให้ไดเรกทอรีที่เรียกว่า โปรแกรมติดตั้งควรดำเนินการไปแล้ว แต่เพียงแค่ตรวจสอบหรือหากติดตั้งด้วยตนเอง หากคุณไม่ชอบตำแหน่งนี้คุณสามารถเปลี่ยนได้ในsvscanbootสคริปต์ของคุณแม้ว่าผู้ใช้ daemontools ส่วนใหญ่จะคุ้นเคยกับการใช้งาน/serviceและจะสับสนหากคุณไม่ได้ใช้
  3. หากคุณใช้ Ubuntu หรือ distro อื่นที่ไม่ใช้มาตรฐานinit(เช่นไม่ได้ใช้/etc/inittab) คุณจะต้องใช้สิ่งที่ติดตั้งไว้ล่วงหน้าinittabเป็นฐานในการจัดเตรียมsvscanbootการเรียกinitใช้ ไม่ใช่เรื่องยาก แต่คุณต้องรู้วิธีกำหนดค่าinitที่ระบบปฏิบัติการของคุณใช้ svscanbootเป็นสคริปต์ที่โทรsvscanซึ่งทำหน้าที่หลักในการค้นหาบริการ มันถูกเรียกจากinitดังนั้นinitจะจัดการรีสตาร์ทหากมันตายไม่ว่าด้วยเหตุผลใดก็ตาม

การตั้งค่าต่อบริการ

  1. บริการแต่ละรายการต้องมีไดเรกทอรีบริการซึ่งเก็บข้อมูลการดูแลทำความสะอาดเกี่ยวกับบริการ นอกจากนี้คุณยังสามารถสร้างที่ตั้งเพื่อเก็บไดเรกทอรีบริการเหล่านี้เพื่อให้ทั้งหมดอยู่ในที่เดียว โดยปกติฉันจะใช้/var/lib/svscanแต่ตำแหน่งใหม่ใด ๆ ก็ใช้ได้
  2. ฉันมักจะใช้สคริปต์ในการตั้งค่าไดเรกทอรีบริการเพื่อประหยัดงานซ้ำ ๆ ด้วยตนเองจำนวนมาก เช่น,

    sudo mkservice -d /var/lib/svscan/some-service-name -l -u user -L loguser "command line here"
    

    some-service-nameชื่อที่คุณต้องการให้บริการของคุณอยู่ที่ไหนเป็นuserผู้ใช้ในการเรียกใช้บริการนั้นในฐานะและloguserเป็นผู้ใช้ที่จะเรียกใช้คนตัดไม้เป็น (มีการอธิบายการบันทึกเพียงเล็กน้อย)

  3. บริการของคุณได้ทำงานในเบื้องหน้า หากพื้นหลังโปรแกรมของคุณเป็นค่าเริ่มต้น แต่มีตัวเลือกในการปิดใช้งานให้ทำเช่นนั้น หากภูมิหลังของโปรแกรมของคุณไม่มีวิธีปิดใช้งานให้อ่านต่อfghackแม้ว่าสิ่งนี้จะมาพร้อมกับการแลกเปลี่ยน: คุณไม่สามารถควบคุมโปรแกรมโดยใช้svc.
  4. แก้ไขrunสคริปต์เพื่อให้แน่ใจว่าทำในสิ่งที่คุณต้องการ คุณอาจต้องsleepโทรออกที่ด้านบนหากคุณคาดว่าบริการของคุณจะออกบ่อย
  5. เมื่อตั้งค่าทุกอย่างถูกต้องแล้วให้สร้าง symlink /serviceเพื่อชี้ไปที่ไดเรกทอรีบริการของคุณ (อย่าใส่ไดเรกทอรีบริการโดยตรงภายใน/serviceจะทำให้ยากที่จะลบบริการออกจากsvscanนาฬิกา)

การบันทึก

  1. วิธีการบันทึก daemontools คือให้บริการเขียนข้อความบันทึกไปยังเอาต์พุตมาตรฐาน (หรือข้อผิดพลาดมาตรฐานหากคุณใช้สคริปต์ที่สร้างขึ้นด้วยmkservice) svscanดูแลการส่งข้อความบันทึกไปยังบริการบันทึก
  2. บริการบันทึกจะรับข้อความบันทึกจากอินพุตมาตรฐาน สคริปต์บริการบันทึกที่สร้างโดยmkserviceจะสร้างไฟล์บันทึกที่หมุนอัตโนมัติและประทับเวลาในlog/mainไดเร็กทอรี currentแฟ้มบันทึกปัจจุบันเรียกว่า
  3. บริการบันทึกสามารถเริ่มและหยุดได้โดยไม่ขึ้นกับบริการหลัก
  4. การวางไฟล์บันทึกผ่านtai64nlocalจะแปลการประทับเวลาเป็นรูปแบบที่มนุษย์อ่านได้ (TAI64N เป็นการประทับเวลาปรมาณู 64 บิตโดยมีจำนวนนาโนวินาที)

การควบคุมบริการ

  1. ใช้svstatเพื่อรับสถานะของบริการ โปรดทราบว่าบริการบันทึกเป็นอิสระและมีสถานะของตัวเอง
  2. คุณสามารถควบคุมบริการของคุณ (เริ่มหยุดการรีสตาร์ท ฯลฯ ) svcโดยใช้ ตัวอย่างเช่นในการเริ่มบริการของคุณใหม่ให้ใช้svc -t /service/some-service-name; -tหมายถึง "ส่งSIGTERM"
  3. สัญญาณอื่น ๆ ที่มี ได้แก่-h( SIGHUP), -a( SIGALRM), -1( SIGUSR1), -2( SIGUSR2) และ-k( SIGKILL)
  4. หากต้องการลงบริการให้ใช้-d. นอกจากนี้คุณยังสามารถป้องกันไม่ให้บริการเริ่มต้นโดยอัตโนมัติเมื่อเริ่มระบบโดยการสร้างไฟล์ที่มีชื่อdownในไดเรกทอรีบริการ
  5. -uในการเริ่มต้นการให้บริการการใช้งาน สิ่งนี้ไม่จำเป็นเว้นแต่คุณจะเคยกระดกไว้ก่อนหน้านี้ (หรือตั้งค่าไม่ให้เริ่มอัตโนมัติ)
  6. เพื่อขอให้หัวหน้างานออกให้ใช้-x; โดยปกติจะใช้-dเพื่อยุติบริการด้วย นี่เป็นวิธีปกติในการอนุญาตให้นำบริการออก แต่คุณต้องยกเลิกการเชื่อมโยงบริการตั้งแต่/serviceแรกมิฉะนั้นsvscanจะรีสตาร์ทหัวหน้างาน นอกจากนี้หากคุณสร้างบริการของคุณด้วยบริการบันทึก ( mkservice -l) อย่าลืมออกจากผู้ควบคุมการบันทึกด้วย (เช่นsvc -dx /var/lib/svscan/some-service-name/log) ก่อนที่จะลบไดเรกทอรีบริการ

สรุป

ข้อดี:

  1. daemontools มีวิธีป้องกันกระสุนในการสร้างและจัดการบริการ ฉันใช้สำหรับเซิร์ฟเวอร์ของฉันและฉันขอแนะนำเป็นอย่างยิ่ง
  2. ระบบบันทึกของมันมีประสิทธิภาพมากเช่นเดียวกับสิ่งอำนวยความสะดวกในการเริ่มบริการอัตโนมัติ
  3. เนื่องจากบริการเริ่มต้นด้วยเชลล์สคริปต์ที่คุณเขียน / ปรับแต่งคุณจึงสามารถปรับแต่งบริการของคุณได้ตามต้องการ
  4. เครื่องมือควบคุมบริการที่มีประสิทธิภาพ: คุณสามารถส่งสัญญาณส่วนใหญ่ไปยังบริการและนำบริการขึ้นและลงได้อย่างน่าเชื่อถือ
  5. บริการของคุณได้รับการรับรองสภาพแวดล้อมการดำเนินการที่สะอาด: พวกเขาจะดำเนินการในสภาพแวดล้อมเดียวกันขีด จำกัด ของกระบวนการ ฯลฯ ตามที่initมีให้

จุดด้อย:

  1. แต่ละบริการใช้เวลาในการตั้งค่าเล็กน้อย โชคดีที่ต้องทำเพียงครั้งเดียวต่อหนึ่งบริการ
  2. ต้องตั้งค่าบริการให้ทำงานในส่วนหน้า นอกจากนี้เพื่อให้ได้ผลลัพธ์ที่ดีที่สุดควรตั้งค่าให้ล็อกไปยังเอาต์พุตมาตรฐาน / ข้อผิดพลาดมาตรฐานแทนที่จะเป็น syslog หรือไฟล์อื่น ๆ
  3. เส้นโค้งการเรียนรู้ที่สูงชันหากคุณยังใหม่กับวิธีการ daemontools ในการทำสิ่งต่างๆ คุณต้องเริ่มบริการใหม่โดยใช้svcและไม่สามารถเรียกใช้สคริปต์รันโดยตรง (เนื่องจากจะไม่อยู่ภายใต้การควบคุมของหัวหน้างาน)
  4. ไฟล์ทำความสะอาดจำนวนมากและกระบวนการทำความสะอาดมากมาย แต่ละบริการต้องการไดเร็กทอรีบริการของตัวเองและแต่ละบริการจะใช้กระบวนการของผู้ดูแลคนเดียวเพื่อเริ่มบริการใหม่โดยอัตโนมัติหากบริการนั้นตาย (ถ้าคุณมีบริการจำนวนมากคุณจะเห็นจำนวนมากของsuperviseกระบวนการในตารางกระบวนการของคุณ.)

ในความสมดุลฉันคิดว่า daemontools เป็นระบบที่ยอดเยี่ยมสำหรับความต้องการของคุณ ฉันยินดีรับคำถามเกี่ยวกับวิธีการตั้งค่าและดูแลรักษา


คำตอบของฉันเน้นย้ำถึงข้อมูลจำเพาะอย่างไร: 1. คุณต้องตั้งค่าบริการตราบใดที่คุณไม่ได้ตั้งค่ารายการที่ซ้ำกัน (และตราบใดที่บริการของคุณไม่ได้อยู่เบื้องหลัง) ก็จะไม่มีการทำซ้ำ 2. superviseหัวหน้างานดูแลการเริ่มต้นบริการใด ๆ ที่ออกไป รอหนึ่งวินาทีระหว่างการรีสตาร์ท หากคุณมีเวลาไม่เพียงพอให้เข้าสู่โหมดสลีปที่ด้านบนของสคริปต์เรียกใช้บริการ
Chris Jester-Young

2a. superviseได้รับการสนับสนุนจากตัวมันเองsvscanดังนั้นหากหัวหน้างานเสียชีวิตก็จะเริ่มต้นใหม่ 2b. svscanได้รับการสนับสนุนinitซึ่งจะรีสตาร์ทอัตโนมัติsvscanตามความจำเป็น 2 ค. หากคุณinitเสียชีวิตด้วยเหตุผลใดก็ตามคุณก็เมาอยู่ดี :-P
Chris Jester-Young

เพื่อตอบคำถามอื่น ๆ เกี่ยวกับการดูแลทำความสะอาดระบบ daemontools จะไม่ใช้ไฟล์ PID เนื่องจากอาจมีการอัปเดต แต่ข้อมูลกระบวนการทั้งหมดจะถูกเก็บไว้โดยหัวหน้างานที่สนับสนุนบริการที่กำหนด หัวหน้างานดูแลรักษาไฟล์จำนวนมาก (และ FIFO) ในไดเรกทอรีบริการที่เครื่องมือต่างๆชอบsvstatและsvcสามารถทำงานได้
Chris Jester-Young

3
เราควรมีโพสต์แบบนี้มากขึ้นใน SO และในเน็ตโดยทั่วไป ไม่ใช่แค่สูตรอาหารเพื่อให้ได้เอฟเฟกต์ที่ต้องการเท่านั้น แต่ยังต้องมีปัญหาในการอธิบายสูตรอาหารอีกด้วย เหตุใดฉันจึงโหวตคะแนนมากกว่าหนึ่งครั้งไม่ได้ : |
skytreader

12

start-stop-daemon(8)ฉันคิดว่าคุณอาจต้องการที่จะลอง ดูสคริปต์ใน/etc/init.dLinux distro สำหรับตัวอย่าง สามารถค้นหากระบวนการที่เริ่มต้นโดยใช้บรรทัดคำสั่งหรือไฟล์ PID ดังนั้นจึงตรงกับความต้องการทั้งหมดของคุณยกเว้นการเป็นสุนัขเฝ้าบ้านสำหรับสคริปต์ของคุณ แต่คุณสามารถเริ่มสคริปต์สุนัขเฝ้าบ้าน daemon อื่นที่เพิ่งรีสตาร์ทสคริปต์ของคุณได้หากจำเป็น


ยังไม่มี start-stop-daemon ใน Fedora ดังนั้นสคริปต์ที่ขึ้นอยู่กับมันจึงไม่สามารถพกพาได้ ดู: fedoraproject.org/wiki/Features/start-stop-daemon
Bengt

เพียงแค่การแจ้งเตือนสำหรับผู้ใช้ OSX: ไม่มีstart-stop-daemonเช่นกัน (ณ วันที่ 10.9)
mklement0

@ mklement0 อืม ... มีการเปลี่ยนแปลงมากมายในรอบเกือบ 5 ปี
Alex B

ฉันเวลาผ่านไปแค่ไหน start-stop-daemonยังมีชีวิตอยู่และเตะบนลินุกซ์แม้ว่า; แต่หลังจากอ่านคำตอบstackoverflow.com/a/525406/45375ฉันตระหนักว่า OSX launchdไม่สิ่งของตัวเอง:
mklement0

12

คุณควรมีลักษณะที่daemonize อนุญาตให้ตรวจจับสำเนาที่สอง (แต่ใช้กลไกการล็อกไฟล์) นอกจากนี้ยังใช้งานได้กับ UNIX และ Linux ที่แตกต่างกัน

หากคุณต้องการเริ่มต้นแอปพลิเคชันของคุณโดยอัตโนมัติเป็น daemon คุณต้องสร้างสคริปต์เริ่มต้นที่เหมาะสม

คุณสามารถใช้เทมเพลตต่อไปนี้:

#!/bin/sh
#
# mydaemon     This shell script takes care of starting and stopping
#               the <mydaemon>
#

# Source function library
. /etc/rc.d/init.d/functions


# Do preliminary checks here, if any
#### START of preliminary checks #########


##### END of preliminary checks #######


# Handle manual control parameters like start, stop, status, restart, etc.

case "$1" in
  start)
    # Start daemons.

    echo -n $"Starting <mydaemon> daemon: "
    echo
    daemon <mydaemon>
    echo
    ;;

  stop)
    # Stop daemons.
    echo -n $"Shutting down <mydaemon>: "
    killproc <mydaemon>
    echo

    # Do clean-up works here like removing pid files from /var/run, etc.
    ;;
  status)
    status <mydaemon>

    ;;
  restart)
    $0 stop
    $0 start
    ;;

  *)
    echo $"Usage: $0 {start|stop|status|restart}"
    exit 1
esac

exit 0

1
ดูเหมือนผู้สมัครสำหรับคำตอบที่ถูกต้อง โดยเฉพาะอย่างยิ่งเมื่อพิจารณาถึง "การตรวจสอบอินสแตนซ์เดียว"
Martin Wickman

นี่อาจเป็นคำตอบที่ดีที่สุด - ฉันไม่แน่ใจ - แต่ถ้าคุณคิดว่าใช่คุณสามารถใส่คำอธิบายได้ไหมว่าทำไมสเป็คที่ฉันให้ไว้ในคำถามจึงผิดไป
เริ่ม

ฉันทำไม่ได้เช่นkillprocในส่วนหยุด: ถ้าคุณมีกระบวนการที่พูด, วิ่งjavaที่killprocจะทำให้เกิดกระบวนการ Java อื่น ๆ ทั้งหมดจะถูกฆ่าตายมากเกินไป
Chris Jester-Young

1
จาก /etc/rc.d/init.d/functions daemonize เพิ่งเริ่มไบนารีจากเชลล์ใหม่: $ cgroup $ nice / bin / bash -c $corelimit >/dev/null 2>&1 ; $*ดังนั้นฉันสงสัยว่ามันจะ daemonize อะไร ...
mbonnin

1
ฉันรู้ว่ามันเก่า แต่สำหรับใครก็ตามที่ค้นพบสิ่งนี้ในภายหลัง ... นี่ถูกต้อง "daemon" ตามที่กำหนดไว้ใน /etc/init.d/functions ไม่ได้ daemonize สำหรับคุณ เป็นเพียงเครื่องห่อหุ้มเพื่อทำ cgroups ตรวจสอบว่ากระบวนการทำงานอยู่แล้วหรือไม่ตั้งค่าผู้ใช้กำหนดค่า nice และ ulimit ฯลฯ มันไม่ได้แสดงให้เห็นกระบวนการสำหรับคุณ นั่นยังคงเป็นงานของคุณเอง :)
jakem

7

เป็นทางเลือกนอกเหนือจากที่กล่าวไปแล้วdaemonizeและdaemontoolsมีภูตคำสั่งของแพ็กเกจ libslack

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


5

หากคุณใช้ OS X โดยเฉพาะฉันขอแนะนำให้คุณดูว่า launchd ทำงานอย่างไร ระบบจะตรวจสอบโดยอัตโนมัติเพื่อให้แน่ใจว่าสคริปต์ของคุณกำลังทำงานอยู่และเปิดขึ้นมาใหม่หากจำเป็น นอกจากนี้ยังมีคุณสมบัติการตั้งเวลาทุกประเภท ฯลฯ ซึ่งควรเป็นไปตามข้อกำหนดทั้ง 1 และ 2

เพื่อให้แน่ใจว่าสคริปต์ของคุณสามารถรันได้เพียงสำเนาเดียวคุณต้องใช้ไฟล์ PID โดยทั่วไปฉันเขียนไฟล์ไปยัง /var/run/.pid ที่มี PID ของอินสแตนซ์ที่กำลังทำงานอยู่ในปัจจุบัน หากไฟล์มีอยู่เมื่อโปรแกรมทำงานจะตรวจสอบว่า PID ในไฟล์ทำงานจริงหรือไม่ (โปรแกรมอาจขัดข้องหรือลืมลบไฟล์ PID) ถ้าเป็นเช่นนั้นให้ยกเลิก หากไม่เป็นเช่นนั้นให้เริ่มรันและเขียนทับไฟล์ PID


5

Daemontools ( http://cr.yp.to/daemontools.html ) เป็นชุดของยูทิลิตี้ฮาร์ดคอร์ที่ใช้ในการทำสิ่งนี้ซึ่งเขียนโดย dj bernstein ฉันได้ใช้สิ่งนี้กับความสำเร็จบางอย่าง ส่วนที่น่ารำคาญเกี่ยวกับเรื่องนี้คือไม่มีสคริปต์ใดที่ส่งคืนผลลัพธ์ที่มองเห็นได้เมื่อคุณเรียกใช้ - เป็นเพียงรหัสส่งคืนที่มองไม่เห็น แต่เมื่อทำงานแล้วมันจะกันกระสุนได้


ใช่ฉันจะเขียนรายการที่ใช้ daemontools ด้วย ฉันจะเขียนโพสต์ของตัวเองเพราะฉันหวังว่าจะเข้าใจคำตอบของฉันได้ครอบคลุมมากขึ้นและหวังว่าจะได้รับรางวัลมากมาย เราจะเห็น :-)
Chris Jester-Young

3

รับครั้งแรกcreateDaemon()จากhttp://code.activestate.com/recipes/278731/

จากนั้นรหัสหลัก:

import subprocess
import sys
import time

createDaemon()

while True:
    subprocess.call(" ".join(sys.argv[1:]),shell=True)
    time.sleep(10)

โอขอบคุณ! ต้องการทำให้กว้างขึ้นเล็กน้อยเพื่อให้คุณสามารถทำ "daemonize foo arg1 arg2" และ "daemonize 'foo arg1 arg2'" ได้หรือไม่?
dreeves

ตกลงตอนนี้จะเข้าร่วมอาร์กิวเมนต์ - อย่างไรก็ตามคุณจะต้องเปลี่ยนหากคุณต้องการให้มีช่องว่างภายในอาร์กิวเมนต์ของคุณ
ดักลาสลีเดอร์

ขอบคุณดักลาส! แม้ว่าจะมีข้อบกพร่องใหญ่ ๆ : การเรียกใช้ "daemonize foo" สองครั้งจะเริ่มการทำงานของ foo สองสำเนา
dreeves

คุณสามารถเพิ่มรหัสบันทึก PID ได้ แต่อาจเป็นการดีที่สุดที่จะเรียกใช้สคริปต์เพียงครั้งเดียว ...
Douglas Leeder

ฉันคิดว่าเรื่องนี้เป็นพื้นฐานของแนวคิดเสื้อคลุม "daemonize" ทั้งหมด (เช่นฉันสามารถ cron ได้ทุกชั่วโมงหรืออย่างน้อยที่สุดเพื่อให้แน่ใจว่ามันทำงานตลอดเวลา) ฉันคิดผิดหรือเปล่า? createDaemon รับประกันอยู่แล้วหรือไม่? แล้วหลังจากรีบูตล่ะ?
อาหาร

1

นี่เป็นเวอร์ชันที่ใช้งานได้พร้อมตัวอย่างซึ่งคุณสามารถคัดลอกลงในไดเร็กทอรีว่างและทดลองใช้ (หลังจากติดตั้งการอ้างอิง CPAN ซึ่งก็คือ Getopt :: Long , File :: Spec , File :: PidและIPC :: System: : เรียบง่าย - เป็นมาตรฐานที่ดีและขอแนะนำอย่างยิ่งสำหรับแฮ็กเกอร์ทุกคน: คุณสามารถติดตั้งได้พร้อมกันด้วยcpan <modulename> <modulename> ...)


keepAlive.pl:

#!/usr/bin/perl

# Usage:
# 1. put this in your crontab, to run every minute:
#     keepAlive.pl --pidfile=<pidfile> --command=<executable> <arguments>
# 2. put this code somewhere near the beginning of your script,
#    where $pidfile is the same value as used in the cron job above:
#     use File::Pid;
#     File::Pid->new({file => $pidfile})->write;

# if you want to stop your program from restarting, you must first disable the
# cron job, then manually stop your script. There is no need to clean up the
# pidfile; it will be cleaned up automatically when you next call
# keepAlive.pl.

use strict;
use warnings;

use Getopt::Long;
use File::Spec;
use File::Pid;
use IPC::System::Simple qw(system);

my ($pid_file, $command);
GetOptions("pidfile=s"   => \$pid_file,
           "command=s"   => \$command)
    or print "Usage: $0 --pidfile=<pidfile> --command=<executable> <arguments>\n", exit;

my @arguments = @ARGV;

# check if process is still running
my $pid_obj = File::Pid->new({file => $pid_file});

if ($pid_obj->running())
{
    # process is still running; nothing to do!
    exit 0;
}

# no? restart it
print "Pid " . $pid_obj->pid . " no longer running; restarting $command @arguments\n";

system($command, @arguments);

example.pl:

#!/usr/bin/perl

use strict;
use warnings;

use File::Pid;
File::Pid->new({file => "pidfile"})->write;

print "$0 got arguments: @ARGV\n";

ตอนนี้คุณสามารถเรียกใช้ตัวอย่างด้านบนด้วย: ./keepAlive.pl --pidfile=pidfile --command=./example.pl 1 2 3และไฟล์pidfileจะถูกสร้างขึ้นและคุณจะเห็นผลลัพธ์:

Pid <random number here> no longer running; restarting ./example.pl 1 2 3
./example.pl got arguments: 1 2 3

ฉันเชื่อว่านี่ไม่ค่อยตรงกับสเป็คถ้าฉันเข้าใจถูกต้อง ในโซลูชันของคุณ (ขอบคุณ btw!) โปรแกรมที่คุณต้องการ daemonize จะต้องถูกแก้ไขเพื่อเขียน PID ไปยังไฟล์ PID ฉันหวังว่าจะมียูทิลิตี้ที่สามารถแสดงผลสคริปต์โดยพลการ
เริ่ม

@dreeves: ใช่ แต่มีสองวิธี: 1. สคริปต์ที่เรียกโดย keepAlive.pl (เช่น example.pl) อาจเป็นเพียง wrapper เพื่อดำเนินการโปรแกรมจริงหรือ 2. keepAlive.pl สามารถแยกวิเคราะห์ตารางของ กระบวนการของระบบที่ใช้งานอยู่ (ด้วย Proc :: ProcessTable ของ CPAN) เพื่อพยายามค้นหากระบวนการที่เกี่ยวข้องและ pid ของมัน)
Ether

1

คุณอาจลองMonit Monit เป็นบริการที่ตรวจสอบและรายงานเกี่ยวกับบริการอื่น ๆ แม้ว่าส่วนใหญ่จะใช้เป็นวิธีแจ้ง (ทางอีเมลและ SMS) เกี่ยวกับปัญหารันไทม์ แต่ก็สามารถทำตามคำแนะนำอื่น ๆ ส่วนใหญ่ที่นี่ได้ โดยอัตโนมัติสามารถเริ่มและหยุดโปรแกรมส่งอีเมลเริ่มสคริปต์อื่น ๆ และรักษาบันทึกผลลัพธ์ที่คุณสามารถรับได้ นอกจากนี้ฉันพบว่าง่ายต่อการติดตั้งและบำรุงรักษาเนื่องจากมีเอกสารที่มั่นคง


1

คุณสามารถลองเป็นอมตะมันเป็น * nix cross-platform (OS ไม่เชื่อเรื่องพระเจ้า) หัวหน้างาน

สำหรับการลองใช้งาน macOS อย่างรวดเร็ว:

brew install immortal

ในกรณีที่คุณใช้FreeBSDจากพอร์ตหรือโดยใช้ pkg:

pkg install immortal

สำหรับLinuxโดยดาวน์โหลดไบนารีที่คอมไพล์ไว้ล่วงหน้าหรือจากแหล่งที่มา: https://immortal.run/source/

คุณสามารถใช้มันได้ดังนี้:

immortal -l /var/log/date.log date

หรือโดยไฟล์YAML การกำหนดค่าซึ่งให้ตัวเลือกเพิ่มเติมแก่คุณตัวอย่างเช่น:

cmd: date
log:
    file: /var/log/date.log
    age: 86400 # seconds
    num: 7     # int
    size: 1    # MegaBytes
    timestamp: true # will add timesamp to log

หากคุณต้องการเก็บเอาต์พุตข้อผิดพลาดมาตรฐานไว้ในไฟล์แยกต่างหากคุณสามารถใช้สิ่งต่อไปนี้:

cmd: date
log:
    file: /var/log/date.log
    age: 86400 # seconds
    num: 7     # int
    size: 1    # MegaBytes
stderr:
    file: /var/log/date-error.log
    age: 86400 # seconds
    num: 7     # int
    size: 1    # MegaBytes
    timestamp: true # will add timesamp to log

0

ผมได้ทำชุดการปรับปรุงในคำตอบอื่น

  1. stdout ออกจากสคริปต์นี้สร้างขึ้นจาก stdout ที่มาจากลูกของมันอย่างหมดจด UNLESS มันออกเนื่องจากตรวจพบว่าคำสั่งนั้นถูกรันอยู่แล้ว
  2. ทำความสะอาดหลังจาก pidfile เมื่อยกเลิก
  3. ระยะหมดเวลาที่กำหนดเพิ่มเติมได้ (ยอมรับอาร์กิวเมนต์ตัวเลขบวกส่งไปยังsleep)
  4. พรอมต์การใช้งานบน -h
  5. การดำเนินการคำสั่งโดยพลการแทนที่จะดำเนินการคำสั่งเดียว อาร์กิวเมนต์สุดท้ายหรืออาร์กิวเมนต์ที่เหลืออยู่ (ถ้ามีมากกว่าหนึ่งอาร์กิวเมนต์สุดท้าย) จะถูกส่งไปยังevalดังนั้นคุณสามารถสร้างเชลล์สคริปต์ประเภทใดก็ได้เป็นสตริงเพื่อส่งไปยังสคริปต์นี้เป็นอาร์กิวเมนต์สุดท้าย (หรืออาร์เรย์ต่อท้าย) เพื่อให้สามารถแสดงผลได้
  6. การเปรียบเทียบจำนวนอาร์กิวเมนต์ทำด้วย-ltแทนที่จะเป็น<

นี่คือสคริปต์:

#!/bin/sh

# this script builds a mini-daemon, which isn't a real daemon because it
# should die when the owning terminal dies, but what makes it useful is
# that it will restart the command given to it when it completes, with a
# configurable timeout period elapsing before doing so.

if [ "$1" = '-h' ]; then
    echo "timeout defaults to 1 sec.\nUsage: $(basename "$0") sentinel-pidfile [timeout] command [command arg [more command args...]]"
    exit
fi

if [ $# -lt 2 ]; then
    echo "No command given."
    exit
fi

PIDFILE=$1
shift

TIMEOUT=1
if [[ $1 =~ ^[0-9]+(\.[0-9]+)?$ ]]; then
        TIMEOUT=$1
        [ $# -lt 2 ] && echo "No command given (timeout was given)." && exit
        shift
fi

echo "Checking pid in file ${PIDFILE}." >&2

#Check to see if process running.
if [ -f "$PIDFILE" ]; then
    PID=$(< $PIDFILE)
    if [ $? = 0 ]; then
        ps -p $PID >/dev/null 2>&1
        if [ $? = 0 ]; then
            echo "This script is (probably) already running as PID ${PID}."
            exit
        fi
    fi
fi

# Write our pid to file.
echo $$ >$PIDFILE

cleanup() {
        rm $PIDFILE
}
trap cleanup EXIT

# Run command until we're killed.
while true; do
    eval "$@"
    echo "I am $$ and my child has exited; restart in ${TIMEOUT}s" >&2
    sleep $TIMEOUT
done

การใช้งาน:

$ term-daemonize.sh pidfilefortesting 0.5 'echo abcd | sed s/b/zzz/'
Checking pid in file pidfilefortesting.
azzzcd
I am 79281 and my child has exited; restart in 0.5s
azzzcd
I am 79281 and my child has exited; restart in 0.5s
azzzcd
I am 79281 and my child has exited; restart in 0.5s
^C

$ term-daemonize.sh pidfilefortesting 0.5 'echo abcd | sed s/b/zzz/' 2>/dev/null
azzzcd
azzzcd
azzzcd
^C

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

นอกจากนี้เพื่อให้มันทำงานเป็น daemon ที่เหมาะสมคุณต้องใช้ (อย่างน้อยที่สุด) nohup ตามที่คำตอบอื่นกล่าวถึง ฉันไม่ได้พยายามที่จะให้ความยืดหยุ่นใด ๆ ต่อสัญญาณที่กระบวนการอาจได้รับ

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.