c++ - 启动停止守护进程不发送 SIGTERM

标签 c++ linux signals daemon start-stop-daemon

我有一个简单的守护进程可以归结为

#include <unistd.h>
#include <signal.h>
#include <sys/stat.h>

#include <fstream>
#include <iostream>

#include <boost/thread.hpp>

bool running = true;
std::ofstream log_output;

void close_log_output()
{
    log_output.close();
}

void signal_handler(int)
{
    running = false;
}

int detach_service()
{
    if(pid_t pid = fork()) exit(pid < 0);
    umask(0);
    close(STDIN_FILENO);
    close(STDOUT_FILENO);
    close(STDERR_FILENO);
    chdir("/");
    log_output.open("/var/log/mydaemon.log");
    std::cout.rdbuf(log_output.rdbuf());
    std::cerr.rdbuf(log_output.rdbuf());
    std::clog.rdbuf(log_output.rdbuf());
    atexit(&close_log_output);
    signal(SIGTERM, &signal_handler);
    return (setsid() < 0);
}

int main(int argc, char **argv)
{
    if(int err = detach_service()) return err;
    while(running) boost::this_thread::sleep(boost::posix_time::milliseconds(30));
    std::cout << "terminated\n";
    return 0;
}

还有一个 init.d 脚本,它只是调整了 $NAME$DAEMON$DAEMON_ARGS 的原始框架。如果我通过它的 PID 终止进程,它会收到 SIGTERM 并正确终止,但是如果我尝试停止服务(默认情况下设置为先发送 TERM),命令会挂起并且进程会在没有收到 SIGTERM 的情况下被终止。

我必须做什么/更改才能正确终止进程(在 shell 端或 C++ 端)

修改后的框架(与原始 Ubuntu init.d 框架相比,只有 NAME 和 DAEMON_ARGS 发生了变化):

#! /bin/sh
### BEGIN INIT INFO
# Provides:          skeleton
# Required-Start:    $remote_fs $syslog
# Required-Stop:     $remote_fs $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Example initscript
# Description:       This file should be used to construct scripts to be
#                    placed in /etc/init.d.
### END INIT INFO

# Author: Foo Bar <foobar@baz.org>
#
# Please remove the "Author" lines above and replace them
# with your own name if you copy and modify this script.

# Do NOT "set -e"

# PATH should only include /usr/* if it runs after the mountnfs.sh script
PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC="Description of the service"
NAME=mydaemon
DAEMON=/usr/sbin/$NAME
DAEMON_ARGS=""
PIDFILE=/var/run/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME

# Exit if the package is not installed
[ -x "$DAEMON" ] || exit 0

# Read configuration variable file if it is present
[ -r /etc/default/$NAME ] && . /etc/default/$NAME

# Load the VERBOSE setting and other rcS variables
. /lib/init/vars.sh

# Define LSB log_* functions.
# Depend on lsb-base (>= 3.2-14) to ensure that this file is present
# and status_of_proc is working.
. /lib/lsb/init-functions

#
# Function that starts the daemon/service
#
do_start()
{
        # Return
        #   0 if daemon has been started
        #   1 if daemon was already running
        #   2 if daemon could not be started
        start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
                || return 1
        start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \
                $DAEMON_ARGS \
                || return 2
        # Add code here, if necessary, that waits for the process to be ready
        # to handle requests from services started subsequently which depend
        # on this one.  As a last resort, sleep for some time.
}

#
# Function that stops the daemon/service
#
do_stop()
{
        # Return
        #   0 if daemon has been stopped
        #   1 if daemon was already stopped
        #   2 if daemon could not be stopped
        #   other if a failure occurred
        start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME
        RETVAL="$?"
        [ "$RETVAL" = 2 ] && return 2
        # Wait for children to finish too if this is a daemon that forks
        # and if the daemon is only ever run from this initscript.
        # If the above conditions are not satisfied then add some other code
        # that waits for the process to drop all resources that could be
        # needed by services started subsequently.  A last resort is to
        # sleep for some time.
        start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
        [ "$?" = 2 ] && return 2
        # Many daemons don't delete their pidfiles when they exit.
        rm -f $PIDFILE
        return "$RETVAL"
}

#
# Function that sends a SIGHUP to the daemon/service
#
do_reload() {
        #
        # If the daemon can reload its configuration without
        # restarting (for example, when it is sent a SIGHUP),
        # then implement that here.
        #
        start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME
        return 0
}

case "$1" in
  start)
        [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
        do_start
        case "$?" in
                0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
                2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
        esac
        ;;
  stop)
        [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
        do_stop
        case "$?" in
                0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
                2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
        esac
        ;;
  status)
        status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
        ;;
  #reload|force-reload)
        #
        # If do_reload() is not implemented then leave this commented out
        # and leave 'force-reload' as an alias for 'restart'.
        #
        #log_daemon_msg "Reloading $DESC" "$NAME"
        #do_reload
        #log_end_msg $?
        #;;
  restart|force-reload)
        #
        # If the "reload" option is implemented then remove the
        # 'force-reload' alias
        #
        log_daemon_msg "Restarting $DESC" "$NAME"
        do_stop
        case "$?" in
          0|1)
                do_start
                case "$?" in
                        0) log_end_msg 0 ;;
                        1) log_end_msg 1 ;; # Old process is still running
                        *) log_end_msg 1 ;; # Failed to start
                esac
                ;;
          *)
                # Failed to stop
                log_end_msg 1
                ;;
        esac
        ;;
  *)
        #echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2
        echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
        exit 3
        ;;
esac

:

最佳答案

您的守护进程不会创建 pid 文件,start-stop-daemon 也不会。我猜第一行

start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME

什么都不做,因为永远不会满足--pidfile进程选择标准,但是第二行

start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON

SIGKILL 杀死守护进程,因为这里不包括 --pidfile 选项。

您应该修复您的守护进程以创建一个 pid 文件。

关于c++ - 启动停止守护进程不发送 SIGTERM,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22224528/

相关文章:

linux - 如何发送USR2信号?

C++:如何将字符串中的每个字符转换为相应的字符?

android - 无法转换为 c\c++ 项目 eclipse ndk cocos2dx

c++ - MPI 中变量的可见性

c++ - TerminateThread 中的无效句柄错误

android - 如何在android中找到另一个设备的wifi信号方向?

c++ - 为什么更改包含 psapi.h 的顺序会产生编译错误?(标识符 BOOL 未定义)

linux - ec2 - 4 ubuntu 12.04 t2.micro 相同实例上的 Hadoop 2.2.0 多节点集群设置

linux - 在 bash 中不工作循环

c++ - 在 Qt 中创建 C++ 库