守护进程(
Daemon
)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。它不需要用户输入就能运行而且提供某种服务,不是对整个系统就是对某个用户程序提供服务。Linux系统的大多数服务器就是通过守护进程实现的。常见的守护进程包括系统日志进程syslogd、 web服务器httpd、邮件服务器sendmail和数据库服务器mysqld等。
守护进程一般在系统启动时开始运行,除非强行终止,否则直到系统关机都保持运行。守护进程经常以超级用户(root)权限运行,因为它们要使用特殊的端口(1-1024)或访问某些特殊的资源。
一个守护进程的父进程是init进程,因为它真正的父进程在fork出子进程后就先于子进程exit退出了,所以它是一个由init继承的孤儿进程。守护进程是非交互式程序,没有控制终端,所以任何输出,无论是向标准输出设备stdout还是标准出错设备stderr的输出都需要特殊处理。
守护进程的名称通常以d结尾,比如sshd、xinetd、crond等
由对守护进程的介绍我们可以得到创建守护进程的步骤:
- 创建子进程,父进程退出,让子进程的父进程为
init
进程 - 在子进程中创建会话(使用
setsid()
函数),让子进程创建新的进程组,并且让该进程组成为一个新的会话(简单来讲,就是子进程的进程id=进程组id=会话id),并脱离终端控制 - 改变当前目录(为根目录,或者为其他目录),防止占用可卸载的文件系统
- 重设文件权限掩码,防止继承的文件创建屏蔽字拒绝某些权限
- 关闭文件描述符,将标准输入、标准输出、标准错误重定向到
/dev/null
- 开始执行守护进程核心工作
- 退出守护进程
创建守护进程代码:这里的守护进程每隔两秒将系统当前时间写入文件中
std::string getCurrentSystemTime()
{
auto tt = std::chrono::system_clock::to_time_t
(std::chrono::system_clock::now());
struct tm* ptm = localtime(&tt);
char date[60] = { 0 };
sprintf(date, "%d-%02d-%02d-%02d.%02d.%02d",
(int)ptm->tm_year + 1900, (int)ptm->tm_mon + 1, (int)ptm->tm_mday,
(int)ptm->tm_hour, (int)ptm->tm_min, (int)ptm->tm_sec);
return std::string(date) + "\n";
}
int test_daemon() {
pid_t pid = fork();
if (pid > 0) {
//父进程退出
return 0;
}
//创建一个新会话,并让子进程成为组长和会长
setsid();
//改变当前目录,防止占用可卸载的文件系统
chdir("/home/edward");
//重设文件权限掩码,防止继承的文件创建屏蔽字拒绝某些权限,增加守护进程的灵活性
umask(0002);
//关闭文件描述符,将标准输入、标准输出、标准错误重定向到/dev/null中
close(STDIN_FILENO);
open("/dev/null", O_RDWR);
dup2(STDIN_FILENO, STDOUT_FILENO);
dup2(STDIN_FILENO, STDERR_FILENO);
//开始守护进程的工作
int fd = open("test_daemon.txt", O_RDWR | O_CREAT | O_TRUNC, 0666);
check_error(fd, "open error");
constexpr int BUFFER_SIZE = 1024;
char buffer[BUFFER_SIZE];
for (int i = 0; i < 10; ++i) {
my_sleep(2, 0);
strcpy(buffer, getCurrentSystemTime().c_str());
write(fd, buffer, strlen(buffer));
}
//退出守护进程的工作
check_error(close(fd), "close error");
}
其中my_sleep
函数是我自己写的一个sleep
函数,check_error
是检查返回值是否是-1
,如果是则通过perror
输出错误信息,并退出程序,getCurrentSystemTime
用于获取系统当前时间