linux api daemon,后台运行进程

主流程:

  1. 调用fork()函数,创建一个子进程,然后使父进程退出,这样就能保证子进程不再有控制终端。

  2. 调用setsid()函数,创建一个新的会话期(session),并使当前进程成为该会话期的首进程和组长进程,这样就能摆脱之前的控制终端、会话期和进程组。

  3. 修改工作目录和文件权限掩码,确保守护进程不占用任何挂载的文件系统,并且对文件的访问权限受到适当的限制。

  4. 关闭标准输入、标准输出和标准错误文件描述符,以避免在后台运行时受到这些文件描述符的影响。

在Linux系统中,确实有一个名为daemon的函数。daemon函数是一个标准的Unix/Linux系统调用,用于将当前进程转变为守护进程。

daemon函数的原型如下:

#include  
int daemon(int nochdir, int noclose); 

其中,nochdirnoclose是两个参数,用于控制守护进程的行为:

  • 如果nochdir设置为0,daemon函数会将当前工作目录切换到根目录(/);
  • 如果nochdir设置为非0,daemon函数会保持当前工作目录不变。
  • 如果noclose设置为0,daemon函数会关闭所有的已打开文件描述符(除了标准输入、标准输出和标准错误);
  • 如果noclose设置为非0,daemon函数不会关闭任何文件描述符。

daemon函数的返回值为0表示成功,返回-1表示失败。

使用daemon函数可以简化将进程转变为守护进程的过程。例如,下面的代码演示了如何使用daemon函数创建一个守护进程:


#include 
#include 
#include 

int main() {
    // 创建守护进程
    if (daemon(0, 0) == -1) {
        perror("daemon");
        return -1;
    }

    // 在守护进程中执行任务
    printf("This is a daemon process.\n");
    sleep(10);
    printf("Daemon process finished.\n");

    return 0;
}

上述代码中,调用daemon(0, 0)函数将当前进程转变为守护进程。在守护进程中,可以执行需要在后台运行的任务。在示例中,守护进程会打印一条消息,然后睡眠10秒,最后打印另一条消息。

需要注意的是,daemon函数仅适用于Unix/Linux系统,不适用于所有操作系统。在其他操作系统上实现守护进程可能需要使用不同的方法和函数。

源码是https://github.com/lattera/glibc/blob/master/misc/daemon.c

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 (__fxstat64 (_STAT_VER, fd, &st), 0)
			== 0)) {
			if (__builtin_expect (S_ISCHR (st.st_mode), 1) != 0
#if defined DEV_NULL_MAJOR && defined DEV_NULL_MINOR
			    && (st.st_rdev
				== makedev (DEV_NULL_MAJOR, DEV_NULL_MINOR))
#endif
			    ) {
				(void)__dup2(fd, STDIN_FILENO);
				(void)__dup2(fd, STDOUT_FILENO);
				(void)__dup2(fd, STDERR_FILENO);
				if (fd > 2)
					(void)__close (fd);
			} else {
				/* We must set an errno value since no
				   function call actually failed.  */
				__close_nocancel_nostatus (fd);
				__set_errno (ENODEV);
				return -1;
			}
		} else {
			__close_nocancel_nostatus (fd);
			return -1;
		}
	}
	return (0);
}

nginx 的实现方式

ngx_int_t
ngx_daemon(ngx_log_t *log)
{
    int  fd;

    switch (fork()) {
    case -1:
        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "fork() failed");
        return NGX_ERROR;

    case 0:
        break;

    default:
        exit(0);
    }

    ngx_parent = ngx_pid;
    ngx_pid = ngx_getpid();

    if (setsid() == -1) {
        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "setsid() failed");
        return NGX_ERROR;
    }

    umask(0);

    fd = open("/dev/null", O_RDWR);
    if (fd == -1) {
        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
                      "open(\"/dev/null\") failed");
        return NGX_ERROR;
    }

    if (dup2(fd, STDIN_FILENO) == -1) {
        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "dup2(STDIN) failed");
        return NGX_ERROR;
    }

    if (dup2(fd, STDOUT_FILENO) == -1) {
        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "dup2(STDOUT) failed");
        return NGX_ERROR;
    }

#if 0
    if (dup2(fd, STDERR_FILENO) == -1) {
        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "dup2(STDERR) failed");
        return NGX_ERROR;
    }
#endif

    if (fd > STDERR_FILENO) {
        if (close(fd) == -1) {
            ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "close() failed");
            return NGX_ERROR;
        }
    }

    return NGX_OK;
}

你可能感兴趣的:(linux,运维,服务器)