linux守护进程示例-linux daemon and singal handler

17 篇文章 1 订阅
13 篇文章 0 订阅

Introduction

 为什么要用守护进程,参考博客[1]。

Code

 Show you my fucking source code. Code is copied from here and there, referece form nginx [2], netdata [3], mosquitto [4].
turtle.c

/*
Project:turtle
Author:zsy
Create:2021/03/09
To test daemon and signal
I raise a brazil turtle
*/
/*
Reference: http://www.luwenpeng.cn/2018/11/24/nginx%E4%BF%A1%E5%8F%B7%E5%A4%84%E7%90%86%E5%8E%9F%E7%90%86%E5%8F%8A%E5%BA%94%E7%94%A8/
ngx_signal_process
*/
#include <chrono>
#include <iostream>
#include <fstream>
#include <unistd.h>
#include <signal.h>
#include <string>
#include <stdio.h>
#include <assert.h>
#include <sys/file.h>
#include <string.h>
#include <errno.h>
#include <memory.h>
#include "cmdline.h"
typedef struct {
    int     signo;
    char   *signame;
    char   *name;
    void  (*handler)(int signo, siginfo_t *siginfo, void *ucontext);
}turtle_signal_t;
#define turtle_signal_helper(n)     SIG##n
#define turtle_signal_value(n)      turtle_signal_helper(n)
#define turtle_str_value_helper(n)   #n
#define turtle_str_value(n)          turtle_str_value_helper(n)
#define TURTLE_SHUTDOWN_SIGNAL      QUIT
#define TURTLE_TERMINATE_SIGNAL     TERM
inline int64_t WallTimeNowInUsec(){
    std::chrono::system_clock::duration d = std::chrono::system_clock::now().time_since_epoch();    
    std::chrono::microseconds mic = std::chrono::duration_cast<std::chrono::microseconds>(d);
    return mic.count(); 
}
inline int64_t TimeMillis(){
    return WallTimeNowInUsec()/1000;
}
//https://github.com/enki/gvpe/blob/master/lib/pidfile.c
int write_pid (const char *pidfile)
{
  FILE *f;
  int fd;
  int pid;

  if ( ((fd = open(pidfile, O_RDWR|O_CREAT, 0644)) == -1)
       || ((f = fdopen(fd, "r+")) == NULL) ) {
      fprintf(stderr, "Can't open or create %s.\n", pidfile ? pidfile : "(null)");
      return 0;
  }
  
#ifdef HAVE_FLOCK
  if (flock(fd, LOCK_EX|LOCK_NB) == -1) {
      fscanf(f, "%d", &pid);
      fclose(f);
      printf("Can't lock, lock is held by pid %d.\n", pid);
      return 0;
  }
#endif

  pid = getpid();
  if (!fprintf(f,"%d\n", pid)) {
      printf("Can't write pid , %s.\n", strerror(errno));
      close(fd);
      return 0;
  }
  fflush(f);

#ifdef HAVE_FLOCK
  if (flock(fd, LOCK_UN) == -1) {
      printf("Can't unlock pidfile %s, %s.\n", pidfile, strerror(errno));
      close(fd);
      return 0;
  }
#endif
  close(fd);

  return pid;
}
int read_pid (const char *pidfile)
{
  FILE *f;
  int pid;

  if (!(f=fopen(pidfile,"r")))
    return 0;
  fscanf(f,"%d", &pid);
  fclose(f);
  return pid;
}
int remove_pid (const char *pidfile)
{
  return unlink (pidfile);
}
void turtle_daemonise(void)
{
    char *err;
    pid_t pid;
    
    pid = fork();
    if(pid < 0){
        err = strerror(errno);
        std::cout<<"error in fork "<<err<<std::endl;
        exit(1);
    }
    if(pid > 0){
        exit(0);
    }
    if(setsid() < 0){
        err = strerror(errno);
        std::cout<<"Error in setsid "<<err<<std::endl;
        exit(1);
    }
    
    assert(freopen("/dev/null", "r", stdin));
    assert(freopen("/dev/null", "w", stdout));
    assert(freopen("/dev/null", "w", stderr));
}
static volatile bool g_running=true;
static void turtle_signal_handler(int signo, siginfo_t *siginfo, void *ucontext){
    switch(signo){
        case turtle_signal_value(TURTLE_TERMINATE_SIGNAL):
        case turtle_signal_value(TURTLE_SHUTDOWN_SIGNAL):
        case SIGINT:
        case SIGHUP:
        case SIGTSTP:
            g_running=false;
            std::cout<<"signal "<<signo<<std::endl;
            break;
        default:
            break;
    }
}
turtle_signal_t  signals[] ={
    { turtle_signal_value(TURTLE_TERMINATE_SIGNAL),
      "SIG" turtle_str_value(TURTLE_TERMINATE_SIGNAL),
      "stop",
      turtle_signal_handler},
    {turtle_signal_value(TURTLE_SHUTDOWN_SIGNAL),
      "SIG" turtle_str_value(TURTLE_SHUTDOWN_SIGNAL),
      "quit",
      turtle_signal_handler},
    { SIGINT, "SIGINT", "", turtle_signal_handler },
    { SIGHUP, "SIGHUP", "", turtle_signal_handler },
    { SIGTSTP, "SIGTSTP", "", turtle_signal_handler },
    { 0, NULL, "", NULL}
};
int init_signals()
{
    turtle_signal_t      *sig;
    struct sigaction   sa;

    for (sig = signals; sig->signo != 0; sig++) {
        memset(&sa, 0,sizeof(struct sigaction));

        if (sig->handler) {
            sa.sa_sigaction = sig->handler;
            sa.sa_flags = SA_SIGINFO;
        } else {
            sa.sa_handler = SIG_IGN;
        }
        sigemptyset(&sa.sa_mask);
        if (sigaction(sig->signo, &sa, NULL) == -1) {
            std::cout<<"error init signal "<<sig->name<<std::endl;
        }
    }
    return 0;
}
int process_signal(const char *name,int pid){
    turtle_signal_t  *sig;
    for (sig = signals; sig->signo != 0; sig++) {
        if (strcmp(name, sig->name) == 0) {
            if (kill(pid, sig->signo) != -1) {
                return 0;
            }
            std::cout<<"error fire signal "<<sig->name<<std::endl;
        }
    }
    return 1;
}
int main(int argc, char *argv[]){
    cmdline::parser a;
    a.add<std::string>("signal", 's', "signal", false, "none");
    a.add<std::string>("config", '\0', "configure file", false, "temp.conf");
    a.add<std::string>("logfile", '\0', "log file", false, "temp.log");
    a.add<std::string>("pidfile", '\0', "log file", false, "temp.pid");
    a.parse_check(argc, argv);
    std::string action=a.get<std::string>("signal");
    std::string config_file=a.get<std::string>("config");
    std::string log_file=a.get<std::string>("logfile");
    std::string pid_file=a.get<std::string>("pidfile");
    std::cout<<config_file<<" "<<log_file<<std::endl;
    int count=0;
    if(0==action.compare("stop")){
        int pid=read_pid(pid_file.c_str());
        if(pid>0){
           process_signal(action.c_str(),pid);
        }
        return 1;
    }
    std::cout<<"parameter"<<std::endl;
    for(int i=0;i<argc;i++){
        std::cout<<argv[i]<<std::endl;
    }
    init_signals();
    turtle_daemonise();
    if(0==write_pid(pid_file.c_str())){
        return 1;
    }
    std::cout<<getpid()<<" "<<action<<std::endl;
    int pid=read_pid(pid_file.c_str());
    std::cout<<pid<<std::endl;
    std::fstream f_log;
    f_log.open(log_file.c_str(),std::fstream::out);
    while(g_running){
        int64_t last=TimeMillis();
        sleep(5);
        int delta=TimeMillis()-last;
        count++;
        f_log<<count<<"\t"<<delta<<std::endl;
        f_log.flush();
    }
    f_log<<"stop"<<std::endl;
    f_log.close();
    remove_pid(pid_file.c_str());
    return 0;
}

 It depends on cmd line parser, download the header cmdline.h first from https://github.com/tanakh/cmdline.
 CMakeLists.txt.

PROJECT(project)
cmake_minimum_required(VERSION 2.6)
SET(CMAKE_BUILD_TYPE "Debug")
include_directories(${CMAKE_SOURCE_DIR}/)
set(EXECUTABLE_NAME "myturtled")
add_executable(${EXECUTABLE_NAME} ${CMAKE_SOURCE_DIR}/turtle.cc)

Build

mkdir build && cd build
cmake ..
make

Run

 run

./myturtled

 kill an existing process by signal.

./myturtled -s stop

starting the program with init.d script on startup

start-stop-turtle.sh

#!/bin/sh

### BEGIN INIT INFO
# Provides:          myturtle
# Required-Start:    $network $local_fs $remote_fs
# Required-Stop:     $network $local_fs $remote_fs
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Should-Start:      slapd cups
# Should-Stop:       slapd cups
# Short-Description: Frpc keep service
# Description: server to provide Frpc
### END INIT INFO

# Quick start-stop-daemon example, derived from Debian /etc/init.d/ssh
set -e

# Must be a valid filename
NAME=turtle
PIDFILE=/home/zsy/myturtle/$NAME.pid
#This is the command to be run, give the full pathname
DAEMON=/home/zsy/myturtle/build/myturtled
log_file="/home/zsy/myturtle/build/$NAME.log"
conf_file="/home/zsy/myturtle/turtle.conf"
DAEMON_OPTS="--config $conf_file --logfile $log_file --pidfile $PIDFILE"

#export PATH="${PATH:+$PATH:}/usr/sbin:/sbin"
#. /lib/lsb/init-functions
case "$1" in
  start)
    echo -n "Starting daemon: "$NAME
    start-stop-daemon --start  --pidfile $PIDFILE --exec $DAEMON -- $DAEMON_OPTS
    echo "."
    ;;
  stop)
    echo -n "Stopping daemon: "$NAME
    start-stop-daemon --stop --pidfile $PIDFILE --retry=TERM/30/KILL/5
    echo "."
	;;
  restart)
    echo -n "Restarting daemon: "$NAME
    start-stop-daemon --stop --quiet --oknodo --retry 30 --pidfile $PIDFILE
    start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- $DAEMON_OPTS
    echo "."
    ;;

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

exit 0

 The path “/home/zsy/myturtle” (PIDFILE, DAEMON, log_file, conf_file) should be changed accoding to where you build myturtled project.

PIDFILE=/home/zsy/myturtle/$NAME.pid
#This is the command to be run, give the full pathname
DAEMON=/home/zsy/myturtle/build/myturtled
log_file="/home/zsy/myturtle/build/$NAME.log"
conf_file="/home/zsy/myturtle/turtle.conf"

 Set Up:

sudo cp start-stop-turtle.sh /etc/init.d/myturtle
sudo chmod 755 /etc/init.d/myturtle
cd /etc/init.d/
sudo update-rc.d myturtle defaults 90

 Of course, the script can be removed, if you do not want it running on startup.

sudo update-rc.d -f  myturtle remove

[1]深入理解Linux操作系统守护进程的意义
[2] ngx_init_signals
[3] netdata signals_init
[4] mosquitto
[5] pid write and read
[6] Example of Linux Daemon
[7] start-stop-daemon 守护进程管理
[8] Ubuntu 设置系统环境变量和开机自启动
[9] Ubuntu 利用 update-rc.d命令 添加开机启动服务

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值