首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >没有PID文件争用条件的Linux守护

问没有PID文件争用条件的Linux守护
EN

Stack Overflow用户
提问于 2016-04-08 00:31:05
回答 2查看 1.3K关注 0票数 2

我已经做过几次工作,让一个程序在Linux下作为守护进程运行。

  • 在一种情况下,我刚刚使用了daemon()。
  • 还有一次,我编写了自己的守护进程代码(基于这样的),因为我想对STDIN、STDOUT等进行更复杂的重定向。
  • 我还使用Busybox start-stop-daemon作为守护进程启动C# Mono程序,并使用-m选项生成PID文件。

问题是,所有这些解决方案在PID文件的创建上都有一个竞争条件,即PID文件是由程序通过后台进程编写的,在前景进程退出后有一些不确定的时间。这是一个问题,例如,在嵌入式Linux中,如果程序是由initscript启动的,然后启动一个监视进程,它通过检查它的PID文件来监视程序的运行。在使用C# Mono的情况下,我曾让监督狗在启动时偶尔重新启动这样的系统,因为在监视狗进程开始监视时,程序的PID文件还没有编写(这可能会在实际场景中发生)。

在没有PID文件争用条件的情况下,程序如何被去守护?也就是说,这样可以保证在前景进程退出时,PID文件被完全创建并用有效的PID值写入。

注意,对于Linux守护进程叉-setsid-叉成语(防止守护进程获得一个控制的tty)来说,这有点困难,因为父进程不能很容易地获得孙辈的PID。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-04-08 05:22:58

我正在尝试下面的代码。要点是:

  • 第一个叉的父级等待到子级退出。
  • 第一个叉的子程序执行各种守护进程的设置,然后执行第二个守护进程。第二个叉的父级(它获取其子块的PID )将PID写入PID文件,然后退出。

因此,在编写后台进程PID之前,前台进程是不会退出的。

(注意exit()和_exit()之间的区别。其思想是exit()进行正常关闭,这可以包括C++析构函数或C atexit()函数对PID文件的解锁和删除。但是_exit()跳过了所有这些。这允许后台进程保持PID文件的打开和锁定(例如使用flock()),这允许“单例”守护进程。因此,程序在调用此函数之前,应该打开PID文件并对其进行flock()。如果它是一个C程序,它应该注册一个atexit()函数,它将关闭并删除PID文件。如果它是一个C++程序,它应该使用RAII样式的类来创建PID文件,并在退出时关闭/删除它。)

代码语言:javascript
复制
int daemon_with_pid(int pid_fd)
{
    int         fd;
    pid_t       pid;
    pid_t       pid_wait;
    int         stat;
    int         file_bytes;
    char        pidfile_buffer[32];

    pid = fork();
    if (pid < 0) {
        perror("daemon fork");
        exit(20);
    }
    if (pid > 0) {
        /* We are the parent.
         * Wait for child to exit. The child will do a second fork,
         * write the PID of the grandchild to the pidfile, then exit.
         * We wait for this to avoid race condition on pidfile writing.
         * I.e. when we exit, pidfile contents are guaranteed valid. */
        for (;;) {
            pid_wait = waitpid(pid, &stat, 0);
            if (pid_wait == -1 && errno == EINTR)
                continue;
            if (WIFSTOPPED(stat) || WIFCONTINUED(stat))
                continue;
            break;
        }
        if (WIFEXITED(stat)) {
            if (WEXITSTATUS(stat) != 0) {
                fprintf(stderr, "Error in child process\n");
                exit(WEXITSTATUS(stat));
            }
            _exit(0);
        }
        _exit(21);
    }

    /* We are the child. Set up for daemon and then do second fork. */
    /* Set current directory to / */
    chdir("/");

    /* Redirect STDIN, STDOUT, STDERR to /dev/null */
    fd = open("/dev/null", O_RDWR);
    if (fd < 0)
        _exit(22);
    stat = dup2(fd, STDIN_FILENO);
    if (stat < 0)
        _exit(23);
    stat = dup2(fd, STDOUT_FILENO);
    if (stat < 0)
        _exit(23);
    stat = dup2(fd, STDERR_FILENO);
    if (stat < 0)
        _exit(23);

    /* Start a new session for the daemon. */
    setsid();

    /* Do a second fork */
    pid = fork();
    if (pid < 0) {
        _exit(24);
    }
    if (pid > 0) {
        /* We are the parent in this second fork; child of the first fork.
         * Write the PID to the pidfile, then exit. */
        if (pid_fd >= 0) {
            file_bytes = snprintf(pidfile_buffer, sizeof(pidfile_buffer), "%d\n", pid);
            if (file_bytes <= 0)
                _exit(25);
            stat = ftruncate(pid_fd, 0);
            if (stat < 0)
                _exit(26);
            stat = lseek(pid_fd, 0, SEEK_SET);
            if (stat < 0)
                _exit(27);
            stat = write(pid_fd, pidfile_buffer, file_bytes);
            if (stat < file_bytes)
                _exit(28);
        }
        _exit(0);

    }
    /* We are the child of the second fork; grandchild of the first fork. */
    return 0;
}
票数 1
EN

Stack Overflow用户

发布于 2016-04-08 07:52:37

正如您所发现的,管理自己的pid文件的守护进程本质上是动态的。解决方案不是去守护,而是在前台运行流程,然后使用流程管理器来管理它。例如runit、监督d和systemd对“新样式守护进程”的支持。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/36489529

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档