Linux 实现守护进程只能执行一次

问题描述
守护进程是一个脱离中断运行在后台为系统提供某种服务的特殊进程;但这样的服务一般不需要两个,因而需要赋予他只能执行一次的属性。

解决方案
使用最普通的方法:在执行时创建一个特殊的标志文件,结束时关闭并删除文件,执行过程中如果再次执行,创建文件是会报错并退出。如此即可实现只能执行一次的属性;

方案细节

  1. 创建daemon
    (1) fork子进程,退出父进程;
    (2)使用setsid()创建会话期,脱离控制台;
    (3)chdir()工作目录设为 “/” ;
    (4)umask()设置umask=0;
    (5)遍历fd并关闭所有文件;
    (6)绑定标准输入、输出、错误为“”/dev/null“;

  2. 自定义型号SIGUSR1处理函数并设置为结束前删除标志文件

  3. 注意前后顺序
    (1)创建守护进程在程序最前面;
    (2)创建标志文件;
    (3)设置信号处理;
    (4)以上过程都要在daemon死循环之前;

  4. 所有输出信息全部写在日志文件里

示例代码

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <syslog.h>



void creat_daemon(void);
void func(int sig);


int main(void)
{
    
    

	int cnt = 0;
	int fd = -1;
	
	creat_daemon();
	
	fd = open("b.txt", O_RDWR | O_CREAT | O_EXCL, 0644);
	if (EEXIST == errno)
	{
    
    
		openlog("daemon", LOG_CONS | LOG_PID, LOG_USER);		
		syslog(LOG_INFO, "This process is running and can not be run again.\n");
		closelog();	
		
		exit(-1);
	}
	
	struct sigaction act = {
    
    0};
	act.sa_handler = func;		
	sigaction(SIGUSR1, &act, NULL);

	openlog("daemon", LOG_CONS | LOG_PID, LOG_USER);		
	syslog(LOG_INFO, "The daemon has been run successfully\n");
	closelog();	

	
	while (1)
	{
    
    
		openlog("daemon", LOG_CONS | LOG_PID, LOG_USER);
		
		syslog(LOG_INFO, "cnt = %d.\n", cnt++);
		closelog();
		sleep(1);
	}
	

	
	
	return 0;
}



void func(int sig)
{
    
    

	if (sig == SIGUSR1)
	{
    
    
		remove("./b.txt");
		
		openlog("daemon", LOG_CONS | LOG_PID, LOG_USER);		
		syslog(LOG_INFO, "The daemon has been terminated.\n");
		closelog();				
		exit(0);
	}
}

void creat_daemon(void)
{
    
    
	pid_t pid = -1;
	pid = fork();
	if (pid < 0)
	{
    
    
		perror("fork");
		exit(-1);
	}
	else if (pid > 0)
	{
    
    
		exit(0);
	}
	
	pid = setsid();
	if (pid < 0)
	{
    
    
		perror("setsid");
		exit(-1);
	}
	
	chdir("/");
	
	umask(0);
	
	int cnt = sysconf(_SC_OPEN_MAX);
	int i = 0;
	for (i=0; i<cnt; i++)
	{
    
    
		close(i);
	}
	
	open("/dev/null", O_RDWR);
	open("/dev/null", O_RDWR);
	open("/dev/null", O_RDWR);
	
}


程序测试
查看目录: ls 结果是b.txt不存在
首次运行: ./b.out
控制台: NULL
查看目录: ls 结果是b.txt存在
查看进程: ps -x

 3715 ?        S      0:00 [kworker/u16:3]
 3719 ?        S      0:00 [kworker/u16:1]
 3742 ?        Ss     0:00 ./b.out
 3743 pts/16   R+     0:00 ps -x

查看日志:vi /var/log/syslog

2004 Sep  1 08:51:53 ubuntu daemon[3742]: The daemon has been run successfully
2005 Sep  1 08:51:53 ubuntu daemon[3742]: cnt = 0.
2006 Sep  1 08:51:54 ubuntu daemon[3742]: cnt = 1.
2007 Sep  1 08:51:55 ubuntu daemon[3742]: cnt = 2.
2008 Sep  1 08:51:56 ubuntu daemon[3742]: cnt = 3.
2009 Sep  1 08:51:57 ubuntu daemon[3742]: cnt = 4.
2010 Sep  1 08:51:58 ubuntu daemon[3742]: cnt = 5.
2011 Sep  1 08:51:59 ubuntu daemon[3742]: cnt = 6.

再次执行:./b.out

 3657 ?        S      0:00 [kworker/u16:0]
 3715 ?        S      0:00 [kworker/u16:3]
 3742 ?        Ss     0:00 ./b.out
 3745 ?        S      0:00 [kworker/u16:1]
 3750 pts/16   R+     0:00 ps -x

只有一个b.out进程
查看日志:

2258 Sep  1 08:56:06 ubuntu daemon[3742]: cnt = 253.
2259 Sep  1 08:56:07 ubuntu daemon[3749]: This process is running and can not be run again.
2260 Sep  1 08:56:07 ubuntu daemon[3742]: cnt = 254.
2261 Sep  1 08:56:08 ubuntu daemon[3742]: cnt = 255.

结束进程:kill -10 3742
控制台:NULL
查看目录:ls j结果是b.txt不存在
查看进程:ps -x

 3745 ?        S      0:00 [kworker/u16:1]
 3755 ?        S      0:00 [kworker/u16:0]
 3762 pts/16   R+     0:00 ps -x

查看日志:

3179 Sep  1 09:11:28 ubuntu daemon[3742]: cnt = 1173.
3180 Sep  1 09:11:29 ubuntu daemon[3742]: cnt = 1174.
3181 Sep  1 09:11:29 ubuntu daemon[3742]: The daemon has been terminated.   

总结
以上示例便是实现了一个只能执行一次的守护进程

猜你喜欢

转载自blog.csdn.net/m0_50399735/article/details/108333248