First, the daemon process overview

The Linux Daemon is a special process that runs in the background. It is independent of the controlling terminal and periodically performs certain tasks or waits for certain events to occur. It does not require user input to run and provides a service, either for the entire system or for a user program. Most servers on Linux systems are implemented through daemons. Common daemons include the system log process syslogd, web server httpd, mail server sendmail, and database server mysqld.

The daemon usually starts running at system startup, and unless it is forcibly terminated, it will remain running until the system is shut down. Daemons often run with superuser privileges because they use special ports (1-1024) or access certain special resources.

The parent process of a daemon is the init process, because its real parent process exits before the child process exits before the child process exit, so it is an orphan process inherited by init. The daemon is a non-interactive program with no control terminals, so any output, whether it is to the standard output device stdout or the standard error device stderr output requires special handling.

The name of the daemon usually ends with d, such as sshd, xinetd, crond, etc.

Second, create a daemon step

First we need to understand some basic concepts:

Process group:

  • Each process also belongs to a process group
  • Each process master has a process group number, which is equal to the PID number of the process group leader.
  • A process can only set the process group ID number for itself or a child process

Session period:

A session is a collection of one or more process groups.

The setsid() function can establish a session:

If the process calling setsid is not the leader of a process group, this function creates a new session .

(1) This process becomes the first process of the dialogue period

(2) This process becomes the leader process of a new process group.

(3) This process has no control terminal. If the process has a control terminal before calling setsid, the contact with the terminal is released. This function returns an error if the process is the leader of a process group.

(4) To ensure this, we first call fork() and then exit(), at which point only the child process is running.

Now let’s walk through the steps required to create a daemon:

General steps to write a daemon:

(1) Execute fork and exit in the parent process;

(2) call the setsid function in the child process to create a new session;

(3) call the chdir function in the child process, so that the root directory “/” becomes the working directory of the child process;

(4) call the umask function in the child process, set the umask of the process to 0;

(5) Close any unwanted file descriptors in the child process

Description:

1. Run in the background. 
In order to avoid suspending the control terminal, Daemon is put into the background. The method is to call fork in the process to terminate the parent process, and let Daemon execute in the background in the child process. 
If(pid=fork()) 
exit(0);// is the parent process, the parent process ends, the child process continues 
2. From the control terminal, the login session and the process group
need to first introduce the process and control terminal in Linux. Relationship between the login session and the process group: The process belongs to a process group, and the process group number (GID) is the process ID (PID) of the process leader. A login session can contain multiple process groups. These process groups share a single control terminal. This control terminal is usually the login terminal that creates the process. 
Control terminals, login sessions and process groups are usually inherited from the parent process. Our goal is to get rid of them and protect them from them. The method is to call setsid() on the basis of the first point to make the process become the session leader: 
setsid(); 
Description: The setsid() call fails when the process is the session leader. But the first point has been to ensure that the process is not the session leader. After the setsid() call succeeds, the process becomes the new session leader and the new process leader, and is detached from the original login session and process group. Due to the exclusiveness of the session process to the control terminal, the process is simultaneously disconnected from the control terminal. 
3. Prevent the process from reopening the control terminal.
Now, the process has become the session leader of the terminalless. But it can reapply to open a control terminal. You can prevent the process from reopening the control terminal by making the process no longer the session leader: 
if(pid=fork()) 
exit(0);//End the first child process, the second child process continues (the second child process does not Then the session leader) 
4. Close the open file descriptor
The process inherits the open file descriptor from the parent process that created it. Failure to shut down will waste system resources, causing the file system in which the process resides to fail to unload and cause unforeseen errors. Close them as follows: 
for(i=0;i Close the open file descriptor close(i);> 
5. When changing the current working directory
process activity, the file system where the working directory is located cannot be removed. Generally it will work. change directory to the root directory for the process needs to dump core, log write operation to change the working directory to a specific directory such as / tmpchdir ( “/”) 
6. Reset the file creation mask
process from the parent process that created it inherited the File creation mask. It may modify the access bits of the file created by the daemon. To prevent this, the file creation mask is cleared: umask(0); 
7. Processing the SIGCHLD signal
processing SIGCHLD signal is not necessary. For some processes, especially the server process often generates a child process to process the request when the request comes. If the parent process does not wait for the child process to end, the child process will become a zombie process (zombie) and occupy system resources. If the parent process waits for the child process to end Will increase the burden on the parent process and affect the concurrent performance of the server process. Under Linux, you can simply set the operation of the SIGCHLD signal to SIG_IGN. 
signal (SIGCHLD, SIG_IGN); 
In this way, the kernel does not produce at the end of the child process with BSD4 zombie process that is different under BSD4 must explicitly wait for the child process to release the zombie process ends.

Third, create a daemon

Before creating, let’s first understand the use of setsid():

  #include <unistd.h>

       Pid_t setsid(void);

The DESCRIPTION 
the setsid () Creates the session IF A new new IS Not The Calling Process A Process 
       Group Leader
 .  Of The Calling Process The new new IS The Leader of the session , 
The Process of Group Leader Group Process The new new, and has NO Control- 
       Ling TTY
 .  Of The Process The group ID and session ID of the calling process 
       are set to the PID of the calling process
 .  The calling process will be 
       the only process in this new process group and in this new session
 .

/ / The calling process must be non-current process group leader, after the call, a new session is generated, and there is only one process group in the session, and the process group leader is the calling process, there is no control terminal, the newly generated group ID And the session ID is set to the PID of the calling process

RETURN VALUE 
       On success, the (new) session ID of the calling process is returned. 
       On error, (pid_t) -1 is returned, and errno is set to indicate the 
       error.

Now create a daemon based on the above steps:

The following program creates a daemon and then uses this daemon to write the current time to the daemon.log file every minute.

#include <stdio.h> 
#include <unistd.h> 
#include <stdlib.h> 
#include <time.h> 
#include <fcntl.h> 
#include < string .h> 
#include <sys/stat.h >

#define ERR_EXIT(m) \
 do \
{\
    Perror(m);\
    Exit(EXIT_FAILURE);\
}\
While ( 0 );\

Void creat_daemon( void );
 int main( void )
{
    Time_t t;
    Int fd;
    Creat_daemon();
    While ( 1 ){
        Fd = open( " daemon.log " , O_WRONLY|O_CREAT|O_APPEND, 0644 );
         if (fd == - 1 )
            ERR_EXIT( " open error " );
        t = time( 0 );
         char *buf = asctime(localtime(& t));
        Write(fd,buf,strlen(buf));
        Close(fd);
        Sleep( 60 );
            
    }
    Return  0 ;
}
Void creat_daemon( void )
{
    Pid_t pid;
    Pid = fork();
     if ( pid == - 1 )
        ERR_EXIT( " fork error " );
     if (pid > 0 )
        Exit(EXIT_SUCCESS);
    If (setsid() == - 1 )
        ERR_EXIT( " SETSID ERROR " );
    Chdir( " / " );
     int i;
     for ( i = 0 ; i < 3 ; ++ i)
    {
        Close(i);
        Open( " /dev/null " , O_RDWR);
        Dup( 0 );
        Dup( 0 );
    }
    Umask( 0 );
     return ;
}

result:

The result shows: When I run a.out on a normal user, the newly created daemon does not appear in the process table, but when I execute as the root user, it succeeds, and the daemon.log file is created in the / directory. After cat view, it is written once every minute. Why can it only be executed by root? That is because when we create the daemon, the current directory has been switched to my / directory, so when I create the daemon.log file is actually in the / directory, it certainly does not work, because the average user does not Permission, maybe you will ask if you have not given an error? There is actually an error, except that we have closed the standard input and redirected to /dev/null when creating the daemon, so we don’t see the error message.

Fourth, use the library function daemon () to create a daemon

In fact, we can use the daemon () function to create a daemon, its function prototype:

#include <unistd.h>

Int daemon(int nochdir, int noclose);

DESCRIPTION 
       The daemon() function is for works wishinging to detach themselves from 
       the controlling terminal and run in the background as system daemons.

       If nochdir is zero, daemon() changes the process’s current working 
       directory to the root directory (“/”); otherwise,

       If noclose is zero, daemon() redirects standard input, standard output 
       and standard error to /dev/null; otherwise, no changes are made to 
       these file descriptors. 

Function: Create a daemon

parameter:

Nochdir:=0Change the current directory to “/”

Noclose:=0 redirects standard input, standard output, standard error to “/dev/null”

return value:

Success: 0

Failure: -1

Now we use daemon() to rewrite the program just now:

#include <stdio.h> 
#include <unistd.h> 
#include <stdlib.h> 
#include <time.h> 
#include <fcntl.h> 
#include < string .h> 
#include <sys/stat.h >

#define ERR_EXIT(m) \
 do \
{\
    Perror(m);\
    Exit(EXIT_FAILURE);\
}\
While ( 0 );\

Void creat_daemon( void );
 int main( void )
{
    Time_t t;
    Int fd;
     if (daemon( 0 , 0 ) == - 1 )
        ERR_EXIT( " daemon error " );
     while ( 1 ){
        Fd = open( " daemon.log " , O_WRONLY|O_CREAT|O_APPEND, 0644 );
         if (fd == - 1 )
            ERR_EXIT( " open error " );
        t = time( 0 );
         char *buf = asctime(localtime(& t));
        Write(fd,buf,strlen(buf));
        Close(fd);
        Sleep( 60 );
            
    }
    Return  0 ;
}

When daemon(0,0):

The result is the same as before, only root can succeed, ordinary users can not see the error message when executing

Now let daemon(0,1) not close the standard input and output results:

Can see the error message

Now let daemon(1,0), that is, not redirect, the result is as follows:

This time the normal user executed successfully, thinking that there is no switch to / directory, have permission

In fact, we can use the default daemon() implementation we created just to create the daemon:

code show as below:

#include <stdio.h> 
#include <unistd.h> 
#include <stdlib.h> 
#include <time.h> 
#include <fcntl.h> 
#include < string .h> 
#include <sys/stat.h >

#define ERR_EXIT(m) \
 do \
{\
    Perror(m);\
    Exit(EXIT_FAILURE);\
}\
While ( 0 );\

Void creat_daemon( int nochdir, int noclose);
 int main( void )
{
    Time_t t;
    Int fd;
    Creat_daemon( 0 , 0 );
     while ( 1 ){
        Fd = open( " daemon.log " , O_WRONLY|O_CREAT|O_APPEND, 0644 );
         if (fd == - 1 )
            ERR_EXIT( " open error " );
        t = time( 0 );
         char *buf = asctime(localtime(& t));
        Write(fd,buf,strlen(buf));
        Close(fd);
        Sleep( 60 );
            
    }
    Return  0 ;
}
Void creat_daemon( int nochdir, int noclose)
{
    Pid_t pid;
    Pid = fork();
     if ( pid == - 1 )
        ERR_EXIT( " fork error " );
     if (pid > 0 )
        Exit(EXIT_SUCCESS);
    If (setsid() == - 1 )
        ERR_EXIT( " SETSID ERROR " );
     if (nochdir == 0 )
        Chdir( " / " );
     if (noclose == 0 ){
             int i;
     for ( i = 0 ; i < 3 ; ++ i)
    {
        Close(i);
        Open( " /dev/null " , O_RDWR);
        Dup( 0 );
        Dup( 0 );
    }

    Umask( 0 );
     return ;
}