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命令 添加开机启动服务