Create SysV-style linux daemon program

This article describes how to use the C language to create a SysV-style daemon program in a Linux system. Note: This is an old-style daemon program writing method. After entering the systemd era, there is no need to create a daemon program in this way. The demonstration environment of this article is ubuntu 18.04.

The flow of creating daemon program

Through the previous article " Linux session (session) " we learned that if you want the program to run in the background, you must handle the session of the process. Therefore, it is a very important step to deal with the session problem in the process of creating the daemon program. Of course, other steps are required. The following is the basic process of creating a SysV-style daemon in a Linux system:

  1. Fork a child process from the parent process
  2. Create a new session ID for the child process
  3. Fork again in the child process
  4. Modify umask
  5. Modify the current working directory of the process
  6. Close the file descriptor in the process

Next, we introduce the meaning of these operations through the code.

Create daemon program

Fork a child process from the parent process to
create a child process, if successful, let the parent process exit, the child process at this time has become a child process of the init process:

pid_t pid;

pid = fork();
if (pid < 0)
    exit(EXIT_FAILURE);
if (pid > 0)
    exit(EXIT_SUCCESS);

Create a new session ID for the child process. The process
running in the background needs to get rid of the constraints of the session terminal. You can do this by setting a new session ID for the process through the setsid () function:

pid_t pid;

pid = fork();
if (pid < 0)
    exit(EXIT_FAILURE);
if (pid > 0)
    exit(EXIT_SUCCESS);

if (setsid() < 0)
    exit(EXIT_FAILURE);

******************************** When
executed here, PID == PGID == SID

********************************

Fork again in the child process
The purpose of this fork is to prevent the process from acquiring the terminal again. Because only the session leader can get the terminal, and this fork makes the child process become a non-session leader:

pid_t pid;

pid = fork();
if (pid < 0)
    exit(EXIT_FAILURE);
if (pid > 0)
    exit(EXIT_SUCCESS);

if (setsid() < 0)
    exit(EXIT_FAILURE);
    
/* 第二次 fork */
pid = fork();
if (pid < 0)
    exit(EXIT_FAILURE);

if (pid > 0)
    exit(EXIT_SUCCESS);

******************************** When
executed here, PGID == SID but it is not equal to PID, indicating the process No longer a session leader

********************************

Modify the umask
In order to be able to write content (including logs) to any file created by the daemon process, you must reset the umask (file mode mask, umask) to ensure that these files can be written or read correctly:

umask(0);

Modifying the current working directory of a
process must ensure that the current working directory of the process exists. Because many of the Linux distributions do not fully comply with the standard file directory structure, it is best to set the current working directory of the process to /, so that it cannot be unmounted due to the setting of a directory:

chdir("/");

Close the file descriptor in the
process to close all open file descriptors in the process:

int x;
for (x = sysconf(_SC_OPEN_MAX); x>=0; x--)
{
    close (x);
}


It is very important to write the log to the log of the syslog
Daemon program. We can write the log content to syslog through the three functions of openlog, syslog and closelog:

openlog ("daemondemo", LOG_PID, LOG_DAEMON);
syslog (LOG_NOTICE, "Daemon demo is running, number: %d", count);
closelog();

The logs output by the demo in this article are as follows:

Complete code

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <syslog.h>

static void demo_daemon()
{
    pid_t pid;

    /* Fork off the parent process */
    pid = fork();

    /* An error occurred */
    if (pid < 0)
        exit(EXIT_FAILURE);

    /* Success: Let the parent terminate */
    if (pid > 0)
        exit(EXIT_SUCCESS);

    /* On success: The child process becomes session leader */
    if (setsid() < 0)
        exit(EXIT_FAILURE);

    /* Catch, ignore and handle signals */
    //TODO: Implement a working signal handler */
    //signal(SIGCHLD, SIG_IGN);
    //signal(SIGHUP, SIG_IGN);

    /* Fork off for the second time*/
    pid = fork();

    /* An error occurred */
    if (pid < 0)
        exit(EXIT_FAILURE);

    /* Success: Let the parent terminate */
    if (pid > 0)
        exit(EXIT_SUCCESS);

    /* Set new file permissions */
    umask(0);

    /* Change the working directory to the root directory */
    /* or another appropriated directory */
    chdir("/");

    /* Close all open file descriptors */
    int x;
    for (x = sysconf(_SC_OPEN_MAX); x>=0; x--)
    {
        close (x);
    }

    /* Open the log file */
    openlog ("daemondemo", LOG_PID, LOG_DAEMON);
}

int main()
{
    int count = 0;
    demo_daemon();

    while (1)
    {
        //TODO: Insert daemon code here.
        count ++;
        syslog (LOG_NOTICE, "Daemon demo is running, number: %d", count);
        sleep (5);
        if(count > 5)
        {
            break;
        }
    }

    syslog (LOG_NOTICE, "Daemon demo terminated.");
    closelog();

    return EXIT_SUCCESS;
}

Save the above code to the file daemondemo.c (you can also download the code from here ), and then execute the following command to compile to get the executable file daemondemo:

$ gcc -Wall daemondemo.c -o daemondemo

About fork twice

This is a very interesting topic. Some people say that you need to fork twice, and some people say that the second time is optional. What should I do? When we understand the purpose of the second fork, we can decide whether we need the second fork.
This also needs to start from the session control terminal. The control terminal is an attribute of the process, and the child process created by the fork system call will inherit the control terminal from the parent process. In this way, all processes in the session inherit the control terminal from the session leader process. As mentioned earlier, to turn a program into a daemon, you have to get the process out of the session terminal. This is done by calling the setsid () function after the first fork. So what if you accidentally add a terminal to the process next? The answer is not to let you add! This is what the second fork does. Only the session leader can get the terminal, and the second fork makes the child process become a non-session leader. If you want to make a mistake, you will not be given the opportunity.

像 nginx 和 gblic 的 daemon 函数的实现都是 fork 一次,所以说第二次 fork 是可选的,你可以根据自己的实际情况来决定。

参考:
Linux Daemon Writing HOWTO
Creating a daemon in Linux
daemon man page
daemon 函数
Unix Daemon Server Programming
glibc daemon.c

Guess you like

Origin www.cnblogs.com/sparkdev/p/12714790.html