由于java是独立于操作系统之外的,使用java写的程序不能很容易地写成一个普通Unix 守护进程的样子。

尽管如此我们可以知道Linux中的守护进程一般会怎么做(这句我没看懂。。。)。在这篇文章里我会告诉你如何使用“start-stop-daemon”方法来使得无界面的java程序运行得像其他一般守护进程一样。

首先,你必须将你的程序打成jar包。这是因为我们必须从其他路径调用这个文件,这会导致 ClassNotFoundException,如果我们不使用jar文件的话(入口必须定义在Manifest文件里)。

接着我们可以使用如下命令来运行守护进程
[codesyntax lang="bash"]

# start-stop-daemon --start --exec /usr/bin/java -- -jar myservice.jar

[/codesyntax]

start-stop-daemon(以下简称ssd)会在进程表中检查是否java已经在运行了。这意味着,如果在系统中,在当前时刻,任何用户在使用/usr/bin/java,那么你的服务将启动失败。此外,当你使用/etc/init.d/yourservice stop命令来结束你的服务的时候,所有的java虚拟机将会被终止。这显然不是我们希望的,不是么?

为了防止这种情况我们必须告诉ssd不要去检查进程表,而是要去检查pid文件。问题是:哪个pid文件?你根本不会为了这样一个原因去改写你的java应用程序,只是为了添加这样一个功能。没问题,我们可以使用一个假的pid文件来骗过ssd:
[codesyntax lang="bash"]

# start-stop-daemon --start --make-pidfile --pidfile /var/run/myservice.pid --exec /usr/bin/java -- -jar myservice.jar

[/codesyntax]

根据ssd的man解释页面,参数--pidfile /var/run/myservice.pid会通知ssd去检查/var/run/myservice.pid文件是否存在,同时,参数--make-pidfile会创建这个文件(在检查是否存在之前)!

此外,为了使得这段代码运行,我们还必须使用额外的两个参数:--chuid,这个参数会改变运行你的java应用程序的用户,--background,这个参数会强制应用程序呢运行在后台。

最后,我们可以使用以下命令来结束我们的java守护进程:
[codesyntax lang="bash"]

# start-stop-daemon --stop --quiet --oknodo --pidfile /var/run/myservice.pid

[/codesyntax]

不要忘记删除这个假pid文件(虽然不是必须的,但是我们不想留下无用的文件):
[codesyntax lang="bash"]

# rm -f /var/run/myservice.pid

[/codesyntax]

总结下,以下就是能使得java应用程序以守护进程方式启动和停止的命令:

[codesyntax lang="bash"]

# start-stop-daemon --start --quiet -b -m -p /var/run/myservice.pid --chuid <username> --exec /usr/bin/java -- -jar myservice.jar

# start-stop-daemon --stop  --quiet -p /var/run/myservice.pid

# rm -f /var/run/myservice.pid

[/codesyntax]

文章翻译自:http://mydebian.blogdns.org/?p=172
这篇文章很久远了。。。2007年10月8号的文章,不过还是很有参考价值的。

接下去留一份我自己开发程序中使用过的/etc/init.d/启动脚本:

[codesyntax lang="bash"]

#!/bin/bash

# PARAMS
USER=root
PID_PATH=/var/run/
PROJECT_ROOT=/opt/JSS
JAR_FILE=/opt/JSS/dist/jss.jar
MODE=""
EXCEPTION="Usage: jss {start|stop|restart} {M1|M2|M3}"

M1=HTTP_REQUEST_TIMER
M2=HTTP_REDIRECT_LISTENER
M3=MEMCACHED_LISTENER_TIMER

RETVAL=0

# PREPARE PARAMS
case "$2" in
    M1)
      MODE=$M1
  ;;
    M2)
      MODE=$M2
  ;;
    M3)
      MODE=$M3
  ;;
    *)
      echo $EXCEPTION
      exit 1
  ;;
esac
PID_FILE=$PID_PATH$MODE.pid

# PREPARE JAR FILE
if [ ! -e $JAR_FILE ]
then
    cd $PROJECT_ROOT
    ant
fi

# DEFINE STARTUP & ENDUP
start() {
      echo -n "Starting JSS, MODE: $MODE"
      start-stop-daemon --start --quiet -b -m -p $PID_FILE --chuid $USER --exec /usr/bin/java -- -jar $JAR_FILE $MODE
      RETVAL=$?
      echo "DONE."
}
stop() {
      echo -n "Stopping JSS, MODE: $MODE"
      start-stop-daemon --stop --quiet -p $PID_FILE
      rm -f $PID_FILE
      RETVAL=$?
      echo "DONE."
}

# COMMANDS
case "$1" in
    start)
      start
  ;;
    stop)
      stop
  ;;
    restart)
      stop
        sleep 2
      start
  ;;
    *)
      echo $EXCEPTION
      exit 1
  ;;
esac

exit $RETVAL

[/codesyntax]