我想知道是否有更好的方法来制作一个只使用sh等待某事的守护进程:
#! /bin/sh
trap processUserSig SIGUSR1
processUserSig() {
echo "doing stuff"
}
while true; do
sleep 1000
done
特别是,我想知道是否有任何方法可以摆脱循环,仍然可以监听信号。
答案 0 :(得分:107)
只需后台编写脚本(./myscript &
)就不会对其进行守护。请参阅http://www.faqs.org/faqs/unix-faq/programmer/faq/,第1.7节,其中描述了成为守护进程所需的内容。您必须将其与终端断开连接,以便SIGHUP
不会将其终止。您可以使用快捷方式使脚本看起来像守护进程;
nohup ./myscript 0<&- &>/dev/null &
将完成这项工作。或者,将stderr和stdout捕获到文件中:
nohup ./myscript 0<&- &> my.admin.log.file &
但是,您可能还需要考虑其他重要方面。例如:
chdir("/")
(或脚本中的cd /
)和fork,以便父进程退出,从而关闭原始描述符。umask 0
。您可能不想依赖守护程序调用者的umask。有关考虑所有这些方面的脚本示例,请参阅Mike S' answer。
答案 1 :(得分:64)
这里的一些最热门的答案缺少使守护进程成为守护进程的一些重要部分,而不仅仅是后台进程,或者是从shell分离的后台进程。
这个http://www.faqs.org/faqs/unix-faq/programmer/faq/描述了成为守护进程的必要条件。这个Run bash script as daemon实现了setsid,虽然它错过了chdir to root。
原始海报的问题实际上比“我如何使用bash创建守护进程?”更具体,但由于主题和答案一般讨论守护进程的shell脚本,我认为重要的是指出它(对于像我这样的闯入者)查看创建守护进程的详细信息。)
这是我根据常见问题解答执行的shell脚本的再现。将DEBUG设置为true
以查看漂亮的输出(但它也会立即退出而不是无休止地循环):
#!/bin/bash
DEBUG=false
# This part is for fun, if you consider shell scripts fun- and I do.
trap process_USR1 SIGUSR1
process_USR1() {
echo 'Got signal USR1'
echo 'Did you notice that the signal was acted upon only after the sleep was done'
echo 'in the while loop? Interesting, yes? Yes.'
exit 0
}
# End of fun. Now on to the business end of things.
print_debug() {
whatiam="$1"; tty="$2"
[[ "$tty" != "not a tty" ]] && {
echo "" >$tty
echo "$whatiam, PID $$" >$tty
ps -o pid,sess,pgid -p $$ >$tty
tty >$tty
}
}
me_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
me_FILE=$(basename $0)
cd /
#### CHILD HERE --------------------------------------------------------------------->
if [ "$1" = "child" ] ; then # 2. We are the child. We need to fork again.
shift; tty="$1"; shift
$DEBUG && print_debug "*** CHILD, NEW SESSION, NEW PGID" "$tty"
umask 0
$me_DIR/$me_FILE XXrefork_daemonXX "$tty" "$@" </dev/null >/dev/null 2>/dev/null &
$DEBUG && [[ "$tty" != "not a tty" ]] && echo "CHILD OUT" >$tty
exit 0
fi
##### ENTRY POINT HERE -------------------------------------------------------------->
if [ "$1" != "XXrefork_daemonXX" ] ; then # 1. This is where the original call starts.
tty=$(tty)
$DEBUG && print_debug "*** PARENT" "$tty"
setsid $me_DIR/$me_FILE child "$tty" "$@" &
$DEBUG && [[ "$tty" != "not a tty" ]] && echo "PARENT OUT" >$tty
exit 0
fi
##### RUNS AFTER CHILD FORKS (actually, on Linux, clone()s. See strace -------------->
# 3. We have been reforked. Go to work.
exec >/tmp/outfile
exec 2>/tmp/errfile
exec 0</dev/null
shift; tty="$1"; shift
$DEBUG && print_debug "*** DAEMON" "$tty"
# The real stuff goes here. To exit, see fun (above)
$DEBUG && [[ "$tty" != "not a tty" ]] && echo NOT A REAL DAEMON. NOT RUNNING WHILE LOOP. >$tty
$DEBUG || {
while true; do
echo "Change this loop, so this silly no-op goes away." >/dev/null
echo "Do something useful with your life, young man." >/dev/null
sleep 10
done
}
$DEBUG && [[ "$tty" != "not a tty" ]] && sleep 3 && echo "DAEMON OUT" >$tty
exit # This may never run. Why is it here then? It's pretty.
# Kind of like, "The End" at the end of a movie that you
# already know is over. It's always nice.
DEBUG
设置为true
时输出如下所示。请注意会话和进程组ID(SESS,PGID)编号如何更改:
<shell_prompt>$ bash blahd
*** PARENT, PID 5180
PID SESS PGID
5180 1708 5180
/dev/pts/6
PARENT OUT
<shell_prompt>$
*** CHILD, NEW SESSION, NEW PGID, PID 5188
PID SESS PGID
5188 5188 5188
not a tty
CHILD OUT
*** DAEMON, PID 5198
PID SESS PGID
5198 5188 5188
not a tty
NOT A REAL DAEMON. NOT RUNNING WHILE LOOP.
DAEMON OUT
答案 2 :(得分:61)
# double background your script to have it detach from the tty
# cf. http://www.linux-mag.com/id/5981
(./program.sh &) &
答案 3 :(得分:43)
使用系统的守护程序工具,例如start-stop-daemon。
否则,是的,某处必须有一个循环。
答案 4 :(得分:4)
这实际上取决于二进制文件本身要做什么。
例如,我想创建一些监听器。
启动守护进程是一项简单的任务:
lis_deamon:
#!/bin/bash
# We will start the listener as Deamon process
#
LISTENER_BIN=/tmp/deamon_test/listener
test -x $LISTENER_BIN || exit 5
PIDFILE=/tmp/deamon_test/listener.pid
case "$1" in
start)
echo -n "Starting Listener Deamon .... "
startproc -f -p $PIDFILE $LISTENER_BIN
echo "running"
;;
*)
echo "Usage: $0 start"
exit 1
;;
esac
这就是我们启动守护进程的方式(所有/etc/init.d/员工的通用方式)
现在对于听众而言, 它必须是某种循环/警报,否则将触发脚本 做你想做的事。例如,如果你想让你的脚本睡10分钟 然后醒来并问你是怎么做的,你会用
做到这一点while true ; do sleep 600 ; echo "How are u ? " ; done
这是一个简单的听众,你可以做的就是听你的 来自远程机器的命令并在本地执行:
听众:
#!/bin/bash
# Starting listener on some port
# we will run it as deamon and we will send commands to it.
#
IP=$(hostname --ip-address)
PORT=1024
FILE=/tmp/backpipe
count=0
while [ -a $FILE ] ; do #If file exis I assume that it used by other program
FILE=$FILE.$count
count=$(($count + 1))
done
# Now we know that such file do not exist,
# U can write down in deamon it self the remove for those files
# or in different part of program
mknod $FILE p
while true ; do
netcat -l -s $IP -p $PORT < $FILE |/bin/bash > $FILE
done
rm $FILE
所以要启动它:/ tmp / deamon_test / listener start
并从shell发送命令(或将其包装到脚本中):
test_host#netcat 10.184.200.22 1024
uptime
20:01pm up 21 days 5:10, 44 users, load average: 0.62, 0.61, 0.60
date
Tue Jan 28 20:02:00 IST 2014
punt! (Cntrl+C)
希望这会有所帮助。
答案 5 :(得分:1)
查看libslack包中的守护进程工具:
在Mac OS X上,为shell守护程序使用launchd脚本。
答案 6 :(得分:1)
如果我有一个script.sh
并且我想从bash执行它并让它保持运行,即使我想关闭我的bash会话,那么我会将nohup
和&
组合在一起端。
示例:nohup ./script.sh < inputFile.txt > ./logFile 2>&1 &
inputFile.txt
可以是任何文件。如果您的文件没有输入,那么我们通常使用/dev/null
。所以命令是:
nohup ./script.sh < /dev/null > ./logFile 2>&1 &
在关闭bash会话之后,打开另一个终端并执行:ps -aux | egrep "script.sh"
,您将看到您的脚本仍在后台运行。对于cource,如果要停止它,则执行相同的命令(ps)和kill -9 <PID-OF-YOUR-SCRIPT>
答案 7 :(得分:0)
请参阅 Bash服务管理器项目:https://github.com/reduardo7/bash-service-manager
#!/usr/bin/env bash
export PID_FILE_PATH="/tmp/my-service.pid"
export LOG_FILE_PATH="/tmp/my-service.log"
export LOG_ERROR_FILE_PATH="/tmp/my-service.error.log"
. ./services.sh
run-script() {
local action="$1" # Action
while true; do
echo "@@@ Running action '${action}'"
echo foo
echo bar >&2
[ "$action" = "run" ] && return 0
sleep 5
[ "$action" = "debug" ] && exit 25
done
}
before-start() {
local action="$1" # Action
echo "* Starting with $action"
}
after-finish() {
local action="$1" # Action
local serviceExitCode=$2 # Service exit code
echo "* Finish with $action. Exit code: $serviceExitCode"
}
action="$1"
serviceName="Example Service"
serviceMenu "$action" "$serviceName" run-script "$workDir" before-start after-finish
$ ./example-service
# Actions: [start|stop|restart|status|run|debug|tail(-[log|error])]
$ ./example-service start
# Starting Example Service service...
$ ./example-service status
# Serive Example Service is runnig with PID 5599
$ ./example-service stop
# Stopping Example Service...
$ ./example-service status
# Service Example Service is not running
答案 8 :(得分:0)
$ ( cd /; umask 0; setsid your_script.sh </dev/null &>/dev/null & ) &
答案 9 :(得分:0)
像许多答案一样,这不是“真正的”守护进程,而是http://username:password@example.com
方法的替代方法。
nohup
使用echo "script.sh" | at now
显然有区别。对于一个人,首先没有与父母分离。同样,“ script.sh”不会继承父级的环境。
绝不是更好的选择。这只是在后台启动进程的另一种方式(并且有些懒惰)。
P.S。我个人赞成carlo的回答,因为它似乎是最优雅的,并且可以在终端脚本和内部脚本中使用
答案 10 :(得分:0)
这是在Bourne shell(或Bash)中创建有效守护程序的原始提案的最小更改:
#!/bin/sh
if [ "$1" != "__forked__" ]; then
setsid "$0" __forked__ "$@" &
exit
else
shift
fi
trap 'siguser1=true' SIGUSR1
trap 'echo "Clean up and exit"; kill $sleep_pid; exit' SIGTERM
exec > outfile
exec 2> errfile
exec 0< /dev/null
while true; do
(sleep 30000000 &>/dev/null) &
sleep_pid=$!
wait
kill $sleep_pid &>/dev/null
if [ -n "$siguser1" ]; then
siguser1=''
echo "Wait was interrupted by SIGUSR1, do things here."
fi
done
说明:
猜猜它没有比这更简单的了。
答案 11 :(得分:-3)
尝试使用&amp; 如果您将此文件另存为program.sh
你可以使用
$. program.sh &