本文共 3430 字,大约阅读时间需要 11 分钟。
守护进程(Daemon)是Linux系统中运行于后台的一种特殊进程。它独立于控制终端,能够周期性地执行特定任务或等待处理,通常不依赖用户输入即可提供服务。常见的守护进程包括syslogd、httpd、mysqld等,其命名习惯约定以"d"结尾。
通过代码示例可以快速体验守护进程的特性。以下是一个简单的守护进程示例:
#include#include #include int main() { if (daemon(0, 0)) { perror("daemon error"); exit(EXIT_FAILURE); } FILE *txt = fopen("demo.log", "w"); if (txt) { fprintf(txt, "%ld, hello, 世界", time(NULL)); fclose(txt); } exit(EXIT_SUCCESS);}
运行该程序后,程序会成为守护进程,日志文件demo.log会记录当前时间和消息。
为了全面了解daemon()函数的使用方法,建议查阅其man手册:
man daemon
手册内容摘录如下:
daemon()函数用于让程序脱离控制终端,作为系统守护进程运行。int daemon(int nochdir, int noclose);nochdir为0,进程的工作目录会被更改为根目录/。noclose为0,标准输入、输出和错误会被重定向到/dev/null。在glibc 2.2版本中,daemon()函数存在一个潜在问题:它不使用double-fork技术,导致生成的守护进程可能成为会话负责人。为了避免这种情况,可以采用以下改进方法:
void daemon_service(void) { // 第一次fork,确保父进程退出 switch (fork()) { case -1: exit(EXIT_FAILURE); case 0: break; default: exit(EXIT_SUCCESS); } // 设置新会话 if (setsid() == -1) { exit(EXIT_FAILURE); } // 第二次fork,避免成为会话负责人 switch (fork()) { case -1: exit(EXIT_FAILURE); case 0: break; default: exit(EXIT_SUCCESS); } // 设置工作目录为根目录 if (chdir("/")) {} // 重置文件权限 umask(0); // 处理标准输入输出 int fd = sysconf(_SC_OPEN_MAX); if (fd == -1) { fd = INT_MAX_CLOSE; } while (fd >= 0) { close(fd--); } // 重定向到/dev/null fd = open(_PATH_DEVNULL, O_RDWR); if (fd == STDIN_FILENO) { dup2(fd, STDOUT_FILENO); dup2(fd, STDERR_FILENO); }} glibc的daemon.c实现如下:
#include#include #include #include #include int daemon(int nochdir, int noclose) { int fd; switch (__fork()) { case -1: return (-1); case 0: break; default: _exit(0); } if (__setsid() == -1) { return (-1); } if (!nochdir) { (void) chdir("/"); } if (!noclose) { struct stat64 st; if ((fd = __open_nocancel(_PATH_DEVNULL, O_RDWR, 0)) != -1 && (__builtin_expect(__fstat64(fd, &st), 0) == 0)) { if (__builtin_expect(S_ISCHR(st.st_mode), 1) != 070 || (!defined(DEV_NULL_MAJOR) && !defined(DEV_NULL_MINOR) || st.st_rdev != makedev(DEV_NULL_MAJOR, DEV_NULL_MINOR))) { (void) dup2(fd, STDIN_FILENO); (void) dup2(fd, STDOUT_FILENO); (void) dup2(fd, STDERR_FILENO); if (fd > 2) { (void) close(fd); } } else { (void) __close_nocancel_nostatus(fd); __set_errno(ENODEV); return (-1); } } else { (void) __close_nocancel_nostatus(fd); __set_errno(ENODEV); return (-1); } } else { (void) __close_nocancel_nostatus(fd); return (-1); } return (0);}
/dev/null的属性,确保它是字符设备。/dev/null。原glibc实现缺乏double-fork技术,导致守护进程可能成为会话负责人。修改后的实现通过double-fork确保守护进程不再是会话负责人。
欢迎交流和指正。如果您对守护进程有更深入的需求,或者发现其他问题,请随时联系。感谢您的阅读与支持!
转载地址:http://ljiyz.baihongyu.com/