这篇博客简单介绍了守护进程(daemon process)
什么是守护进程
守护进程一般提供一个服务,比如ssh登录服务,ftp文件服务,他们长时间运行,等待客户
端来调用这个服务
一般的编码规则
守护线程初始时,一般有以下行为
- 完全控制生成文件的权限,因此一般会调用umask(0),完全去掉系统默认的权限掩码
- 调用fork,生成一个子线程,父线程退出,这样做的目的是:
a.如果守护线程是一个简单的shell命令,父线程退出后,shell会认为该指令已经执行完毕
b. 新生成的子线程继承了父进程的线程组ID,保证了子线程不是线程组的领导者,这是执
行第三步的必要条件 - 调用setid创建一个新的会话,这样守护线程就从当前的控制终端中独立出来,不再和
当前关联 - 更改当前工作目录,一般更改到根目录或者是某个特定目录。
这样做是因为守护线程长期运行,如果不更改目录,会影响当前目录所在磁盘的卸载和重新
挂载 - 关闭不使用的文件描述符
- 将标准输入流、输出流、错误流都定向到/dev/null,因为守护线程不关联任何控制终端
标准输入、输出、错误流本身就是无意义的
日志
守护进程是后台进程,为了便于管理,系统提供了标准的日志输出函数,直接调用即可
单例守护进程
有些守护进程要求在同一时刻,只能有一个实例在运行。要实现此功能,可以利用file和
record-locking机制
例子
#include <stdio.h>
#include <errno.h>
#include <syslog.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/resource.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
void
daemonize(const char *cmd)
{
int fd0, fd1, fd2;
pid_t pid;
struct rlimit rl;
struct sigaction sa;
/**
* clear file creation mask
*/
umask(0);
/**
* Get maximum number of file descriptors
*/
if (getrlimit(RLIMIT_NOFILE, &rl) < 0)
{
perror("can't get file limit");
exit(1);
}
/**
* Became a session leader to lose controlling TTY
*/
if ((pid = fork()) < 0)
{
perror("fail to fork");
exit(0);
} else if (pid != 0) {
/*parent*/
exit(0);
}
setsid();
/*
* Initialize the log file.
*/
openlog(cmd, LOG_CONS, LOG_DAEMON);
/**
* Change the current working directory to the root so
* we won’t prevent file systems from being unmounted.
*/
if (chdir("/") < 0)
{
syslog(LOG_ERR, "fail to change dir");
exit(1);
}
/**
* Close all open file descriptors
*/
if (rl.rlim_max == RLIM_INFINITY)
{
rl.rlim_max = 1024;
}
for (int i = 0; i < rl.rlim_max; i++)
close(i);
/**
* Attach file descriptor 0, 1, 2 to /dev/null
*/
fd0 = open("/dev/null", O_RDWR);
fd1 = dup(fd0);
fd2 = dup(fd0);
if (fd0 != 0 || fd1 != 1 || fd2 != 2) {
syslog(LOG_ERR, "unexpected file descriptors %d %d %d",
fd0, fd1, fd2);
exit(1);
}
/**
* write pid to log
*/
syslog(LOG_INFO, "pid is %d", getpid());
}
int
main(int argc, char *argv[])
{
daemonize(argv[0]);
sleep(60);
syslog(LOG_INFO, "exit");
}
上述程序运行后,就会自动进入后台运行,运行过程中记录了进程的pid,这些日志记录到
了系统日志里,在使用systemd管理守护进程的系统,可以试用systemctl查看日志。