平时经常性会需要写一些daemon服务,于是就写了一个简单的脚本,利用模板文件,可以自动生成一个简单的daemon进程,包括源文件和makefile,启停脚本,只需要直接添上业务逻辑即可。使用方式:./generate_daemon.sh <app name>。
工具的下载地址在这里: http://download.csdn.net/source/3242136
执行./generate_daemon.sh test,生成的目录结构如下,包括src,bin和etc目录和一个Makefile。
下面简单介绍一下生成的代码,大概有几点说明一下:
(1)daemon进程的上下文数据结构中,包括自己的pid,以及pid文件的path;另外有一个stop标志来控制进程的起停;
(2)每一个daemon进程都应该有自己的id号。编译之后的启停方式,应该类似这样:
./test --id=192.0.0.1 --daemon start
./test --id=192.0.0.1 stop
(2)version参数可以看到版本号,这里也就是编译的时间;help可以显示使用说明。
(3)进程第一次启动时,会将自己的pid写入一个文件,目前这个文件在tmp目录下,为进程名字+id号,写完之后会加锁。在stop进程时,会去读这个文件,并到/proc/pid目录下验证进程名字;如果start进程时,发现这个pid文件已经存在,会提示错误。-->如果想起多个进程,必须用不同的id来控制;
(4)stop进程,是用发送kill信号的方式来实现的,目前捕捉的信号包括SIGINT,SIGUSR1,SIGKILL,SIGQUIT;
(5)正常情况下,进程将死循环执行process函数,加上需要的业务逻辑即可。
(6)生成一个test进程,make之后启动脚本,可以看到进程正常运行ing:
最后把部分代码贴出来:
/* * @author: auther <auther mail> * @date: 2011-05-02 * @function: test daemon server header */ .... // test daemon server contxt typedef struct TESTContext { int pid; char pid_file[256]; char id[16]; bool is_stop; bool is_daemon; time_t current_time; }TESTCtx; int test_init(); int test_finish(); int test_daemonize(); int test_signal_init(); void test_signal_handler(int); int test_process(); int test_tick(); int test_help(); int test_write_pid(const char*); int test_read_pid(const char*); int test_is_exist(); int test_kill(); ....
/* * @author: auther <auther mail> * @date: 2011-05-02 * @function: test daemon server source */ #include "test.h" TESTCtx* gSvrdCtx = NULL; int test_init() { gSvrdCtx = (TESTCtx*)malloc(sizeof(TESTCtx)); if(!gSvrdCtx) { fprintf(stderr, "malloc server context fail, no free memory../n"); return -1; } memset(gSvrdCtx, 0, sizeof(TESTCtx)); gSvrdCtx->current_time = time(NULL); gSvrdCtx->is_stop = false; gSvrdCtx->is_daemon = false; // TODO: return 0; } int test_finish() { // TODO: free(gSvrdCtx); return 0; } int test_process() { // TODO: return -1; } int test_tick() { // TODO: return -1; } int test_daemonize() { signal(SIGTTOU, SIG_IGN); signal(SIGTTIN, SIG_IGN); signal(SIGTSTP, SIG_IGN); int i,pid; if((pid = fork())) { exit(0); } else if(pid < 0) { exit(1); } setsid(); signal( SIGHUP, SIG_IGN ); if((pid = fork())) { exit(0); } else if(pid < 0) { exit(1); } for(i =0; i < 255; i++) { close(i); } umask(0); return 0; } int test_signal_init() { struct sigaction act; act.sa_handler = SIG_IGN; sigaction(SIGTERM, &act, NULL); sigaction(SIGPIPE, &act, NULL); sigaction(SIGHUP, &act, NULL); sigaction(SIGCHLD, &act, NULL); sigaction(SIGALRM, &act, NULL); act.sa_handler = test_signal_handler; sigemptyset(&act.sa_mask); act.sa_flags = 0; sigaction(SIGINT, &act, NULL); return 0; } void test_signal_handler(int signal) { if(SIGINT == signal //ctrl + c, kill -2 || SIGUSR1 == signal // quit, kill -10 || SIGKILL == signal // kill -9 || SIGQUIT == signal) // kill -3 { fprintf(stderr, "catch signal SIGINT, server shutdown/n"); gSvrdCtx->is_stop = true; } } int test_help() { const char* szHelpInfo = "test start-up parameters: /n"/ " --id /n"/ " --daemaon /n"/ " --help /n"/ " --version /n"/ " [start | stop] /n"; fprintf(stderr, szHelpInfo); return 0; } int test_write_pid(const char* szPidFile) { if(!szPidFile) { fprintf(stderr, "no pid file to write!/n"); return -1; } int fd = open(szPidFile, O_CREAT | O_TRUNC | O_RDWR, 0666); if(fd < 0) { fprintf(stderr, "open(create) pid file[%s] fail!/n", szPidFile); return -1; } // lock pid file if(0 != flock(fd, LOCK_EX | LOCK_NB)) { fprintf(stderr, "lock to pid file[%s] fail!/n", szPidFile); close(fd); return -1; } char szPid[16]; memset(szPid, 0, 16); snprintf(szPid, 16, "%d", gSvrdCtx->pid); int len = strlen(szPid); if(len != write(fd, szPid, len)) { fprintf(stderr, "write pid file[%s] fail!/n", szPidFile); close(fd); return -1; } close(fd); return 0; } int test_read_pid(const char* szPidFile) { if(!szPidFile) { fprintf(stderr, "no pid file to read!/n"); return -1; } int fd = open(szPidFile, O_RDONLY, 0644); if(fd < 0) { return -1; } char szPid[16]; memset(szPid, 0, 16); if(-1 == read(fd, szPid, sizeof(szPid))) { fprintf(stderr, "read pid file[%s] fail!/n", szPidFile); close(fd); return -1; } int pid = atoi(szPid); close(fd); return pid; } // return 1, exist // return 0, not exist // return -1, check fail int test_is_exist() { if(!gSvrdCtx || !gSvrdCtx->pid_file) { fprintf(stderr, "invalid test context!/n"); return -1; } int pid = test_read_pid(gSvrdCtx->pid_file); // no pid read, then not existed if(pid < 0) { return 0; } char szProcDir[256]; memset(szProcDir, 0, sizeof(szProcDir)); snprintf(szProcDir, 256, "/proc/%d", pid); // check whether process existed in /proc struct stat stStat; int ret = stat(szProcDir, &stStat); if(ret < 0 || !S_ISDIR(stStat.st_mode)) { return 0; } // check process name whether equals to test snprintf(szProcDir, 256, "/proc/%d/status", pid); FILE* fd = fopen(szProcDir, "r"); if(fd) { char buff[64]; char name_str[64]; memset(name_str, 0, sizeof(name_str)); memset(buff, 0, sizeof(buff)); // read first line("Name") if(NULL != fgets(buff, sizeof(buff), fd)) { // dest name found sscanf(buff, "%*s %s", name_str); if (0 == strcmp(name_str, "test")) { fclose(fd); return 1; } } fclose(fd); } return 0; } int test_kill() { if(!gSvrdCtx || !gSvrdCtx->pid_file) { fprintf(stderr, "invalid test context!/n"); return -1; } int ret = -1; if(test_is_exist() > 0) { int pid = test_read_pid(gSvrdCtx->pid_file); ret = kill(pid, SIGINT); } // delete pid file unlink(gSvrdCtx->pid_file); return ret; } int main(int argc, char* argv[]) { if(test_init() < 0) { fprintf(stderr, "test server init fail/n"); return -1; } // load main parameters static struct option szLongOptions[] = { {"id", 1, NULL, 'd'}, {"daemon", 0, NULL, 'D'}, {"help", 0, NULL, 'H'}, {"version", 0, NULL, 'V'}, {0, 0, 0, 0} }; int ch; while((ch = getopt_long(argc, argv, "d:h:v", szLongOptions, NULL)) != EOF) { switch(ch) { case 'd': snprintf(gSvrdCtx->id, 16, "%s", optarg); snprintf(gSvrdCtx->pid_file, 256, "/tmp/test_%s.pid", optarg); break; case 'D': gSvrdCtx->is_daemon = true; break; case 'H': test_help(); free(gSvrdCtx); exit(0); case 'V': fprintf(stderr,"test compile at %s %s/n", __DATE__, __TIME__); free(gSvrdCtx); exit(0); } } if(optind >= argc) { test_help(); free(gSvrdCtx); exit(0); } for (int opt = optind; opt < argc; opt++) { if (0 == strcasecmp(argv[opt], "start")) { // if process already exsit, then warning & exit if(test_is_exist() > 0) { fprintf(stderr, "test server %s already started. /n", gSvrdCtx->id); free(gSvrdCtx); exit(0); } fprintf(stderr, "test server start. /n"); break; } else if (0 == strcasecmp(argv[opt], "stop")) { fprintf(stderr, "test server stop. /n"); // if process not exsit, then warning & exit if(test_is_exist() <= 0) { fprintf(stderr, "test server %s not started. /n", gSvrdCtx->id); } // kill previous process else if(test_kill() < 0) { fprintf(stderr, "kill previous test server %s fail. /n", gSvrdCtx->id); } else { fprintf(stderr, "kill previous test server %s success. /n", gSvrdCtx->id); } free(gSvrdCtx); exit(0); } else { test_help(); free(gSvrdCtx); exit(0); } } fprintf(stderr, "init test server success!/n"); if(gSvrdCtx->is_daemon) { test_daemonize(); } gSvrdCtx->pid = getpid(); if(test_write_pid(gSvrdCtx->pid_file) < 0) { fprintf(stderr, "test server %s init fail. /n", gSvrdCtx->id); free(gSvrdCtx); exit(0); } while(!gSvrdCtx->is_stop) { gSvrdCtx->current_time = time(NULL); if(test_process() < 0) { usleep(100); } test_tick(); } test_finish(); fprintf(stderr, "test server is exit/n"); return 0; }