我想知道是否有比使用sh更好的方法来制作仅使用sh进行等待的守护程序:
#! /bin/sh
trap processUserSig SIGUSR1
processUserSig() {
echo "doing stuff"
}
while true; do
sleep 1000
done
特别是,我想知道是否有任何方法可以摆脱循环并仍然让事物监听信号。
我想知道是否有比使用sh更好的方法来制作仅使用sh进行等待的守护程序:
#! /bin/sh
trap processUserSig SIGUSR1
processUserSig() {
echo "doing stuff"
}
while true; do
sleep 1000
done
特别是,我想知道是否有任何方法可以摆脱循环并仍然让事物监听信号。
Answers:
仅将脚本(./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 /
在脚本内)并进行分叉,以使父项退出,从而关闭原始描述符。umask 0
。您可能不想依赖于守护程序调用者的umask。有关将所有这些方面都考虑在内的脚本示例,请参见Mike S的答案。
0<&-
办?字符序列完成的功能尚不清楚。
0<&-
该做什么。我找到了解释它的链接。
0<&-
关闭stdin(fd 0)。这样,如果您的进程意外地从stdin读取(易于执行),它将得到一个错误,而不是永远挂起等待数据显示。
这里的一些最重要的答案都缺少使守护程序成为守护程序的某些重要部分,这与后台进程或从shell分离的后台进程相反。
此http://www.faqs.org/faqs/unix-faq/programmer/faq/描述了成为守护程序的必要条件。而且,该运行bash脚本作为守护程序实现了setid,尽管它会丢失chdir到根目录。
原始发布者的问题实际上比“我如何使用bash创建守护进程?”更具体,但是由于主题和答案都讨论了一般情况下如何守护shell脚本,因此我指出这一点很重要(对于像我这样的闯入者,创建守护程序的详细信息)。
这是我根据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
。请注意会话和进程组标识(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
fork()' so the parent can exit, this returns control to the command line or shell invoking your program. ... 2.
setsid()'成为进程组和会话组负责人...我们的进程现在没有控制终端,即守护进程的好东西... 3.再次使用“ fork()”,以便父代...可以退出。这意味着作为非会话组长,我们永远无法重新获得控制终端。”
# double background your script to have it detach from the tty
# cf. http://www.linux-mag.com/id/5981
(./program.sh &) &
stdin
,stdout
,stderr
。至少没有sh
。
这实际上取决于二进制文件本身要做什么。
例如,我想创建一些监听器。
启动守护程序很简单:
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)
希望这会有所帮助。
看一下libslack软件包中的守护程序工具:
在Mac OS X上,将启动的脚本用于shell守护程序。
如果我有一个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"
您将看到您的脚本仍在后台运行。当然,如果要停止它,请执行相同的命令(ps)并kill -9 <PID-OF-YOUR-SCRIPT>
请参阅Bash Service Manager项目: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