How to Daemonize in Linux

本文详细阐述了Linux守护进程的正确初始化流程,包括使用fork()、setsid()等关键步骤,以及如何避免常见的错误行为,如终端挂起、目录锁定等问题。并通过提供示例代码,展示了一个基本和更实用的守护进程实现方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

One of the things I keep running across is Linux daemons that don’t properly daemonize themselves. To properly daemonize, the following steps must be followed.

  • The fork() call is used to create a separate process.
  • The setsid() call is used to detach the process from the parent (normally a shell).
  • The file mask should be reset.
  • The current directory should be changed to something benign.
  • The standard files (stdin,stdout and stderr) need to be reopened.

Failure to do any of these steps will lead to a daemon process that can misbehave. The typical symptoms are as follows.

  • Starting the daemon and then logging out will cause the terminal to hang. This is particularly nasty with ssh.
  • The directory from which the daemon was launched remains locked.
  • Spurious output appears in the shell from which the daemon was started.

Simple Example

The following example program performs the bare minimum steps required to launch a daemon process.

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <sys/types.h>

#include <sys/stat.h>

 

#define EXIT_SUCCESS 0

#define EXIT_FAILURE 1

 

static void daemonize(void)

{

    pid_t pid, sid;

 

    /* already a daemon */

    if ( getppid() == 1 ) return;

 

    /* Fork off the parent process */

    pid = fork();

    if (pid < 0) {

        exit(EXIT_FAILURE);

    }

    /* If we got a good PID, then we can exit the parent process. */

    if (pid > 0) {

        exit(EXIT_SUCCESS);

    }

 

    /* At this point we are executing as the child process */

 

    /* Change the file mode mask */

    umask(0);

 

    /* Create a new SID for the child process */

    sid = setsid();

    if (sid < 0) {

        exit(EXIT_FAILURE);

    }

 

    /* Change the current working directory.  This prevents the current

       directory from being locked; hence not being able to remove it. */

    if ((chdir("/")) < 0) {

        exit(EXIT_FAILURE);

    }

 

    /* Redirect standard files to /dev/null */

    freopen( "/dev/null", "r", stdin);

    freopen( "/dev/null", "w", stdout);

    freopen( "/dev/null", "w", stderr);

}

 

int main( int argc, char *argv[] ) {

    daemonize();

 

    /* Now we are a daemon -- do the work for which we were paid */

 

 

    return 0;

}

It has been brought to my attention that a second call to fork() may be required to fully detach the process from the controller terminal (in other words: fork, setsid, fork). This does not seem to be required in Linux. A second fork would not cause any problems, although it would complicate the child/parent signalling below.

A More Useful Example

The following program extends the basic daemon by adding the following features.

  • Logs messages to the system log (via syslog).
  • Creates a lock file to prevent the daemon from being run twice.
  • Changes the effective user (drops privileges).
  • Startup errors are reported to the main process.

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <unistd.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <syslog.h>

#include <errno.h>

#include <pwd.h>

#include <signal.h>

 

/* Change this to whatever your daemon is called */

#define DAEMON_NAME "mydaemon"

 

/* Change this to the user under which to run */

#define RUN_AS_USER "daemon"

 

#define EXIT_SUCCESS 0

#define EXIT_FAILURE 1

 

 

static void child_handler(int signum)

{

    switch(signum) {

    case SIGALRM: exit(EXIT_FAILURE); break;

    case SIGUSR1: exit(EXIT_SUCCESS); break;

    case SIGCHLD: exit(EXIT_FAILURE); break;

    }

}

 

static void daemonize( const char *lockfile )

{

    pid_t pid, sid, parent;

    int lfp = -1;

 

    /* already a daemon */

    if ( getppid() == 1 ) return;

 

    /* Create the lock file as the current user */

    if ( lockfile && lockfile[0] ) {

        lfp = open(lockfile,O_RDWR|O_CREAT,0640);

        if ( lfp < 0 ) {

            syslog( LOG_ERR, "unable to create lock file %s, code=%d (%s)",

                    lockfile, errno, strerror(errno) );

            exit(EXIT_FAILURE);

        }

    }

 

    /* Drop user if there is one, and we were run as root */

    if ( getuid() == 0 || geteuid() == 0 ) {

        struct passwd *pw = getpwnam(RUN_AS_USER);

        if ( pw ) {

            syslog( LOG_NOTICE, "setting user to " RUN_AS_USER );

            setuid( pw->pw_uid );

        }

    }

 

    /* Trap signals that we expect to recieve */

    signal(SIGCHLD,child_handler);

    signal(SIGUSR1,child_handler);

    signal(SIGALRM,child_handler);

 

    /* Fork off the parent process */

    pid = fork();

    if (pid < 0) {

        syslog( LOG_ERR, "unable to fork daemon, code=%d (%s)",

                errno, strerror(errno) );

        exit(EXIT_FAILURE);

    }

    /* If we got a good PID, then we can exit the parent process. */

    if (pid > 0) {

 

        /* Wait for confirmation from the child via SIGTERM or SIGCHLD, or

           for two seconds to elapse (SIGALRM).  pause() should not return. */

        alarm(2);

        pause();

 

        exit(EXIT_FAILURE);

    }

 

    /* At this point we are executing as the child process */

    parent = getppid();

 

    /* Cancel certain signals */

    signal(SIGCHLD,SIG_DFL); /* A child process dies */

    signal(SIGTSTP,SIG_IGN); /* Various TTY signals */

    signal(SIGTTOU,SIG_IGN);

    signal(SIGTTIN,SIG_IGN);

    signal(SIGHUP, SIG_IGN); /* Ignore hangup signal */

    signal(SIGTERM,SIG_DFL); /* Die on SIGTERM */

 

    /* Change the file mode mask */

    umask(0);

 

    /* Create a new SID for the child process */

    sid = setsid();

    if (sid < 0) {

        syslog( LOG_ERR, "unable to create a new session, code %d (%s)",

                errno, strerror(errno) );

        exit(EXIT_FAILURE);

    }

 

    /* Change the current working directory.  This prevents the current

       directory from being locked; hence not being able to remove it. */

    if ((chdir("/")) < 0) {

        syslog( LOG_ERR, "unable to change directory to %s, code %d (%s)",

                "/", errno, strerror(errno) );

        exit(EXIT_FAILURE);

    }

 

    /* Redirect standard files to /dev/null */

    freopen( "/dev/null", "r", stdin);

    freopen( "/dev/null", "w", stdout);

    freopen( "/dev/null", "w", stderr);

 

    /* Tell the parent process that we are A-okay */

    kill( parent, SIGUSR1 );

}

 

int main( int argc, char *argv[] ) {

    /* Initialize the logging interface */

    openlog( DAEMON_NAME, LOG_PID, LOG_LOCAL5 );

    syslog( LOG_INFO, "starting" );

 

    /* One may wish to process command line arguments here */

 

    /* Daemonize */

    daemonize( "/var/lock/subsys/" DAEMON_NAME );

 

    /* Now we are a daemon -- do the work for which we were paid */

 

 

    /* Finish up */

    syslog( LOG_NOTICE, "terminated" );

    closelog();

    return 0;

}

 

<think>好的,用户想了解如何在Linux中实现daemonize或者安装相关的守护进程工具。首先,我需要整理现有的知识库里的引用内容,看看有哪些相关的信息可以引用。 根据引用[3],daemon进程是后台守护进程,像httpd、sshd这些服务都是daemon进程。如果用户想自己编写一个后台运行的程序,可以使用头文件<unistd.h>中的daemon函数,调用daemon(1, 0)即可。这可能是一个直接的方法,不需要额外安装工具,适合开发者自己实现。 然后,引用[1]、[2]、[5]提到了daemonize这个命令。引用[1]说明daemonize会根据系统是否有daemon库来选择实现方式,如果没有则用自己的实现。安装方法可能需要从官网下载源码编译安装,因为引用[2]给出了官网地址。而引用[5]中提到的修改redis.conf中的daemonize no为yes,可能涉及到Redis的配置,但用户的问题是关于daemonize工具的安装和使用,所以需要区分开。 接下来,用户可能需要具体的安装步骤。对于daemonize工具,引用中没有详细步骤,但可以推断出可能需要从源码编译。比如,下载源码包,解压,然后执行configure、make、make install。这部分需要结合常见的Linux软件安装流程来补充。 另外,系统自带的daemon命令可能也是一个选项,比如在FreeBSD中有daemon命令,但功能较少。对于Linux系统,可能需要安装其他工具或者使用自带的systemd来管理守护进程。例如,使用systemd的service文件来配置守护进程,这也是常见的方法,但引用中没有提到,可能需要作为补充内容。 用户的问题可能还涉及到如何将普通程序转换为守护进程,这时候需要分步骤说明,比如fork()、创建新会话、改变工作目录、关闭文件描述符等。这部分在引用[3]的daemon函数中有所体现,但可能需要更详细的解释,比如手动实现daemonize的步骤。 需要确保回答结构清晰,分点说明不同的方法,包括使用系统函数、使用daemonize工具、使用systemd等。同时,引用相关的引用内容,比如引用[3]中的daemon函数,引用[1]、[2]中的daemonize工具,以及引用[5]中的配置示例。 另外,用户可能需要安装步骤的具体命令,例如如何下载和编译daemonize,这可能涉及到安装gcc等编译工具,如引用[4]中提到的安装gcc来编译Redis,类似的步骤可能适用于编译daemonize。 最后,生成相关问题需要考虑用户可能的后续疑问,比如性能分析、不同方法的优缺点、常见错误处理等。</think>### Linux中实现daemonize的两种核心方法 #### 一、使用系统函数daemon() 通过$<unistd.h>$头文件提供的系统级函数实现,适合开发者在代码层面直接创建守护进程。具体步骤如下: ```c #include <unistd.h> int main() { // 参数1: 是否切换工作目录到根目录(0=切换) // 参数2: 是否关闭标准输入输出(0=关闭) if (daemon(1, 0) == -1) { perror("daemon创建失败"); exit(EXIT_FAILURE); } // 守护进程业务逻辑 while(1) { /* ... */ } return 0; } ``` 此方法直接利用Linux内核提供的API,无需额外安装工具,是最高效的实现方式[^3]。 #### 二、安装daemonize工具 对于需要将现有普通进程转换为守护进程的场景,可以使用第三方工具: 1. **源码编译安装** ```bash wget http://software.clapper.org/daemonize/daemonize-1.7.8.tar.gz tar -zxvf daemonize-1.7.8.tar.gz cd daemonize-1.7.8 ./configure make && sudo make install # 需要gcc编译器,安装方法见[^4] ``` 2. **基本使用语法** ```bash daemonize [-v] [-a] [-l lockfile] [-p pidfile] [-u user] [-o outfile] [-e errfile] command ``` 示例将python脚本转为守护进程: ```bash daemonize -p /var/run/mydaemon.pid /usr/bin/python3 /path/to/script.py ``` 该工具提供完善的日志重定向、PID文件管理等特性,比系统函数更易维护[^1][^2]。 #### 三、systemd服务配置(现代Linux推荐) 对于系统服务级别的守护进程,推荐使用systemd管理: 1. 创建服务文件 ```bash sudo vim /etc/systemd/system/myservice.service ``` 2. 写入配置模板 ```ini [Unit] Description=My Daemon Service [Service] Type=simple ExecStart=/path/to/executable Restart=always User=daemonuser [Install] WantedBy=multi-user.target ``` 3. 启用服务 ```bash sudo systemctl daemon-reload sudo systemctl start myservice sudo systemctl enable myservice # 设置开机自启 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值