linux实现守护进程demo
程序员文章站
2022-03-07 22:42:07
...
工作中因为业务需要,被要求实现守护进程的功能。
对守护进程简单进行理解,从网络获取到一些有用的demo代码,以及自己进行测试,这里做笔记汇总。
1:守护进程理解
守护进程是一个在后台运行并且不受任何终端控制的进程。
个人广义理解:守护进程需要做到,脱离当前启动的终端,重定位输入输出相关信息。
代码实现一个守护进程的流程:(最终参考百科)
1:脱离终端:使用创建子进程,终止父进程,僵尸进程的形式实现。
2:在子进程中创建新会话:setsid函数能够使进程完全独立出来,从而脱离fork函数父进程的一些控制。
调用setsid的三个作用:让进程摆脱原会话的控制、让进程摆脱原进程组的控制,让进程摆脱原控制终端的控制。
3:改变工作目录:继承了父进程的工作目录,使用chdir定义该进程的工作目录(进程运行期间,目录无法删除)
4:重设文件创建掩码:文件创建掩码是指屏蔽掉文件创建时的对应位,使用umask(0)把文件创建掩码设置为0。
5:关闭文件描述符:关闭从父进程那里继承一些已经打开了的文件。
2:守护进程demo
按照上述的流程,大概知道守护进行的实现流程,整理到感觉比较好的两个demo:
demo1:设置守护进程,监控子进程如果终止做日志记录(注意代码中的目录):
#include <unistd.h>
#include <signal.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <time.h>
#include <fcntl.h>
#include <wait.h>
#include <stdio.h>
#include <stdlib.h>
//提升控制 设置守护进程目录 以及输出重定向
int updateSTD()
{
int fd;
umask(0);
chdir("/tmp");
long maxfd;
if ((maxfd = sysconf(_SC_OPEN_MAX)) != -1)
{
for (fd = 0; fd < maxfd; fd++)
{
close(fd);
}
}
fd = open("/dev/null", O_RDWR);
if (fd == -1) {
return -1;
}
if (dup2(fd, STDIN_FILENO) == -1) {
return -1;
}
if (dup2(fd, STDOUT_FILENO) == -1) {
return -1;
}
if (dup2(fd, STDERR_FILENO) == -1) {
return -1;
}
if (fd > STDERR_FILENO) {
if (close(fd) == -1) {
return -1;
}
}
return 0;
}
int start_daemon(int monitorChild)
{
switch (fork())
{
case -1: //fork失败 退出
return -1;
case 0: //创建了一个子进程 继续运行
break;
default: //是父进程 退出
exit(0);
}
//是第一子进程,后台继续执行
if (setsid() == -1) //第一子进程成为新的会话组长和进程组长
{
return -1;
}
//Step2: 父进程监控子进程运行
pid_t pid;
int status, i;
FILE *fp;
time_t t;
while(1)
{
switch (fork())
{
case -1: //fork失败 退出
return -1;
case 0: //创建了一个子进程 提升权限 作为监控
updateSTD();
return 0;
default: //父进程 监控子进程的运行
if(monitorChild == 0) //只是记录日志?
{
pid = wait(&status);
i = WEXITSTATUS(status);
if((fp=fopen("/home/hlp/0922/test.log","a")) >=0)
{
t=time(0);
fprintf(fp,"[%s] child's pid = %d . exit status=%d \n",asctime(localtime(&t)),pid, i);
fclose(fp);
}
}
else
{
exit(0);
}
}
}
return 0;
}
//那么如何实现一个 应用进程挂了 使用守护进程监控启动
int main()
{
time_t t;
start_daemon(0);
FILE *fp1;
while(1)//每隔一分钟向test.log报告运行状态
{
sleep(10);//睡眠一分钟
if((fp1=fopen("/home/hlp/0922/test_exec.log","a")) >=0){
t=time(0);
fprintf(fp1,"Im here at %s \n",asctime(localtime(&t)) );
fclose(fp1);
}
}
return 0;
}
demo2: 守护进程可以实现一些监控功能:
看到一个实例,这里做整理:
监控进程可以实现监控我们的运行进程情况,做记录或者重启。
这里可以衍生,实现达到系统监控的一些功能。
thisisatest.c:守护进程,脱离终端,在后台运行。 实现我们的业务功能:
#include<unistd.h>
#include<signal.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/param.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<time.h>
void init_daemon()
{
int pid;
int i;
pid=fork();
if(pid<0)
exit(1); //创建错误,退出
else if(pid>0) //父进程退出
exit(0);
setsid(); //使子进程成为组长
pid=fork();
if(pid>0)
exit(0); //再次退出,使进程不是组长,这样进程就不会打开控制终端
else if(pid<0)
exit(1);
//关闭进程打开的文件句柄
for(i=0;i<NOFILE;i++)
close(i);
chdir("/root/test"); //改变目录
umask(0);//重设文件创建的掩码
return;
}
void main()
{
FILE *fp;
time_t t;
init_daemon();
while(1)
{
sleep(60); //等待一分钟再写入
fp=fopen("testfork2.log","a");
if(fp>=0)
{
time(&t);
fprintf(fp,"current time is:%s\n",asctime(localtime(&t))); //转换为本地时间输出
fclose(fp);
}
}
return;
}
监控进程monitor.c: 同样是守护进程,但是代码功能实现了监控的功能:
#include<unistd.h>
#include<signal.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/param.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<time.h>
#include<sys/wait.h>
#include<fcntl.h>
#include<limits.h>
#define BUFSZ 150
void init_daemon()
{
int pid;
int i;
pid=fork();
if(pid<0)
exit(1); //创建错误,退出
else if(pid>0) //父进程退出
exit(0);
setsid(); //使子进程成为组长
pid=fork();
if(pid>0)
exit(0); //再次退出,使进程不是组长,这样进程就不会打开控制终端
else if(pid<0)
exit(1);
//关闭进程打开的文件句柄
for(i=0;i<NOFILE;i++)
close(i);
chdir("/root/test"); //改变目录
umask(0);//重设文件创建的掩码
return;
}
void err_quit(char *msg)
{
perror(msg);
exit(EXIT_FAILURE);
}
// 判断程序是否在运行
int does_service_work()
{
FILE* fp;
int count;
char buf[BUFSZ];
char command[150];
sprintf(command, "ps -ef | grep thisisatest | grep -v grep | wc -l" );
if((fp = popen(command,"r")) == NULL)
err_quit("popen");
if( (fgets(buf,BUFSZ,fp))!= NULL )
{
count = atoi(buf);
}
pclose(fp);
return count;
// exit(EXIT_SUCCESS);
}
void main()
{
FILE *fp;
time_t t;
int count;
init_daemon();
while(1)
{
sleep(10); //等待一分钟再写入
fp=fopen("testfork3.log","a");
if(fp>=0)
{
count = does_service_work();
time(&t);
if(count>0)
fprintf(fp,"current time is:%s and the process exists, the count is %d\n",asctime(localtime(&t)), count); //转换为本地时间输出
else
{
fprintf(fp,"current time is:%s and the process does not exist, restart it!\n",asctime(localtime(&t))); //转换为本地时间输出
system("/home/user/daemon/thisisatest"); //启动服务 注意自己可执行程序的目录
}
fclose(fp);
}
}
return;
}
该测试程序,如果我终止thisisatest可执行程序,监控程序就会自动重启。
注意:内部涉及一些工作目录,以及日志目录,在测试的时候要关注。
下一篇: (6-03)字符串切割