以下代码实测可用,两次fork第一次生成守护进程pid,第二次生成GPID,同时SID继承GPID(进程组组长),时间读写可换其他文件操作
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <time.h>
#include <stdio.h>
static bool flag = true;
void create_daemon();
void handler(int);
void handler(int sig)
{
printf("I got a signal %d\nI'm quitting.\n", sig);
flag = false;
}
int main()
{
time_t t;
int fd;
create_daemon();
struct sigaction act;//这里是一个信号处理的结构体
act.sa_handler = handler;//后面调用handler函数
sigemptyset(&act.sa_mask);//将sa_mask初始化为空集
act.sa_flags = 0;//不设置特殊标志,意味着在 handler 函数执行期间,不会自动阻塞任何信号。
if(sigaction(SIGQUIT, &act, NULL))//SIGQUIT是系统中用于请求程序立即终止并可能生成core dump文件的信号,说明如果core dump或者立刻推出则执行handler函数
{
printf("sigaction error.\n");
exit(0);
}
//操作文件写入时间
while(flag)
{
fd = open("/home/daemon.log",
O_WRONLY | O_CREAT | O_APPEND, 0644);
if(fd == -1)
{
printf("open error\n");
}
t = time(0);
char *buf = asctime(localtime(&t));//获取时间
write(fd, buf, strlen(buf));
close(fd);
sleep(60);//每分钟写入一次
}
return 0;
}
void create_daemon()
{
pid_t pid;
pid = fork();
if(pid == -1)
{
printf("fork error\n");
exit(1);
}
else if(pid)//父进程退出
{
exit(0);
}
if(-1 == setsid())//创建新对话,该会话的会话ID是调用进程的PID,调用进程成为新会话的会话首领。调用setsid()的进程将成为新的进程组的组长,并且脱离原来的控制终端
{
printf("setsid error\n");
exit(1);
}
pid = fork();
if(pid == -1)
{
printf("fork error\n");
exit(1);
}
else if(pid)
{
exit(0);
}
chdir("/");
int i;
for(i = 0; i < 3; ++i)
{
close(i);
}
umask(0);
return;
}
- 本例中的守护进程仍然会尝试在控制台上打印消息,但这在实际环境中是不可见的,因为标准输出和标准错误已被关闭。你可以通过重定向或日志记录来捕获这些输出。
- 守护进程通常用于在后台运行,执行长期任务,如监控服务、定时任务等。
- 守护进程在Linux系统中非常常见,但在其他操作系统上可能有所不同。
实际案例中最后将会关闭标准文件描述符
// 关闭标准文件描述符
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
// 可以在这里重定向标准输出和标准错误到日志文件等