A daemon is a process with a long lifetime. They are independent of control terminals and periodically execute a task or wait for some events to be processed. They are often started during system boot loading and terminated when the system is shut down. UNIX systems have many daemon processes. Most servers use daemon. For example, the network service inetd and Web Service HTTP. At the same time, the daemon completes many system tasks. For example, job planning process crond and printing process lqd.
The following describes the process structure of the daemon and how to compile the daemon program. Because the daemon process does not have a control terminal, we also need to introduce the method of error output when the daemon process is running.
Daemon and Its Features
The most important feature of a daemon is that it runs in the background. At this point, the resident memory program TSR in DOS is similar.
Second, the daemon must be isolated from the environment before running. These environments include unclosed file descriptors, control terminals, sessions and process groups, working directories, and file creation masks. These environments are generally inherited by the daemon from the parent process (especially the shell) that executes the daemon.
Finally, the daemon startup method has its own special features. It can start from the startup script/etc/rc at system startup. d, which can be started by the inetd daemon, the job planning process crond, and the user terminal (usually shell.
In short, apart from these special features, the daemon process is basically no different from the common process. Therefore, writing a daemon actually transforms a common process into a daemon according to the features of the preceding daemon. If you have a deep understanding of the process, you can easily understand and program the daemon process.
First, let's look at some common system Daemon Processes and look at their relationships with several concepts: Process Groups, control terminals, and dialogs. The p s command prints the status of each process in the system. This command has multiple options. For more information, see the system manual. To view the required information, run:
PS-axj
Ppid PID pgid Sid tty tpgid stat uid Time Command
0 1 0 0? -1 s 0: 04 init
1 2 1? -1 Sw 0 0: 00 [keventd]
1 3 1? -1 Sw 0 0: 00 [kapm-idled]
0 4 1 1? -1 swn 0 0: 00 [ksoftirqd_cpu0]
0 5 1 1? -1 Sw 0 0: 00 [kswapd]
0 6 1 1? -1 Sw 0 0: 00 [kreclaimd]
0 7 1 1? -1 Sw 0 0: 00 [bdflush]
0 8 1 1? -1 Sw 0 0: 00 [kupdated]
1 9 1 1? -1 Sw <0 0: 00 [mdrecoveryd]
1 17 1 1? -1 Sw 0 0: 02 [kjournald]
1 92 1 1? -1 Sw 0 0: 00 [khubd]
1 573 573 573? -1 s 0: 03 syslogd-r-x
1 578 578 578? -1 s 0 0: 00 klogd-2
1 598 598 598? -1 s 32 0: 00 Portmap
Process numbers 1 and 2 are very special and exist throughout the lifecycle of the system. They do not have a parent process ID, a group process ID, or a session ID. The syslogd daemon can be used in any program that records system messages for the operator. You can print these messages on an actual console or write them to a file. Sendmail is a standard mail daemon. The update program regularly writes content in the kernel cache to the hard disk (usually every 30 seconds ). To do this, the program calls the sync (2) function every 30 seconds. The cron daemon executes the specified command on the specified date and time. Many system management tasks are implemented by cron regularly executing related programs. The network interface of the inetd process listening system to input requests to various network servers. In the last daemon process, lpd processes various print requests to the system.
Note that all daemon processes run with the superuser (User ID 0) priority. No daemon has a control terminal. The terminal name is set to question mark (?) And the frontend Process Group ID of the terminal is set to-1. The missing control terminal is the result of the daemon calling setsid. All daemon except update are the first processes in the process group and the first processes in the dialog period. They are also the only processes in these Process Groups and dialog periods. Finally, it should be noted that all the parent processes of these daemon processes are init processes.
Before getting started with actual programming, let's take a look at the concept of a daemon: Process combination session.
Process Group
In addition to a process ID, each process also belongs to a process group (a process group is involved when the signal is discussed). A process group is a collection of one or more processes. Each process has a unique process group ID. The process group ID is similar to the process ID. It is a positive integer and can be stored in the pid_t data type.
Each process group has a leader process. The ID of the Process leader process is that its process group ID is equal to its process ID. The process leader can create a process group, create processes in the group, and then terminate the process, as long as a process exists in a process group, the process exists, which has nothing to do with whether the process leader stops. The time interval from the process group creation to the last process exit is called the life cycle of the process group. The last process in a process group can be terminated or joined to another process group.
As mentioned above, processes that call setgid can participate in an existing group or create a new process group (setsid can also create a new process group, which will be used later)
Session Period
A session is a collection of one or more Process Groups. There are three process groups in a session, usually a Shell Pipe Line that organizes several processes into a group.
The following describes the features of session and Process Groups:
A session can have a single control terminal (controlling terminal), which is usually the terminal device (terminal login) or Pseudo Terminal Device (network login) on which we log on ), however, this control terminal is not necessary.
The first process of the session that establishes a connection with the control terminal is called the control process ). Several process groups in a session can be divided into one foreground process group and one or more background process groups)
If a session has a control terminal, it has a foreground process group, and other process groups are background process groups. Whenever you type the interrupt key (usually delete or Ctrl-C) or return key (usually Ctrl -/), this will cause the interruption signal or exit signal to all processes in the future process group.
Programming rules of the daemon
In different UNIX environments, the detailed programming details of the daemon are inconsistent. Fortunately, the programming principles of the daemon are the same. The difference is that the specific implementation details are different. This principle is to satisfy the characteristics of the daemon. The programming rules are as follows:
1. Run in the background
To avoid suspending the control terminal, you need to put the daemon into the background for execution by calling fork in the process to terminate the parent process and let the daemon be executed in the background of the child process. Specifically, f o r k is called, and then the parent process e x I t is made. In this way, the following points are implemented:
First, if the wizard process is started by a simple s h e l command, terminate the parent process so that s h e l considers that the command has been executed completely.
Second, the child process inherits the process group I d of the parent process, but has a new process I D, which ensures that the child process is not the first process of a process group. This is a necessary precondition for the next s e t s I d call.
2. log on to the session and process group from the control terminal
A logon session can contain multiple process groups that share a control terminal. This control terminal is usually used to create a logon terminal and control terminal for a process, logon sessions and process groups are generally inherited from the parent process. Our goal is to get rid of them so that they are not affected.
The method is to call setsid () on the basis of the first point to make the process a session leader:
It should be noted that when the process is the session leader, the setsid () call will fail, but the first point is that the process is not the session leader. After the setsid () call is successful, the process becomes a new session leader and a new process leader, and disconnects from the original logon session and process group. Because the session process is dedicated to the control terminal, the process is separated from the control terminal at the same time.
The specific operation is:
(A) become the first process in the new dialog Period
(B) The first process to become a new process group
(C) There is no control terminal.
3. Prohibit the process from re-opening the control terminal
Now, the process has become the no-end session leader, but it can re-apply to open a control terminal. You can disable the process from re-opening the control terminal by making the process no longer the session leader:
4. Disable the opened file descriptor.
A process inherits the opened file descriptor from the parent process that created it. If you do not close it, system resources will be wasted, causing undetachable and unexpected errors to the file system where the process is located. Generally, it is necessary to disable file descriptors 0, 1, and 2, namely, standard input, standard output, and standard error. Because we generally want the daemon to have a set of information output and input systems, rather than sending everything to the terminal screen. Call fclose ();
5. Change the current working directory
Change the current working directory to the root directory. The current working directory inherited from the parent process may be in an assembled file system. Because the sprite process usually exists until the system is started again, if the current working directory of the sprite process is in an assembly file system, the file system cannot be detached.
In addition, some genie processes may change the current working directory to a specified location to do their work. For example, the fake offline wizard process of a row printer often changes its working directory to their S p o l directory.
You can call chdir ("directory ");
6. Resetting the file creation mask
Set the blocking word created in file mode to 0. Creating a blocked word by using an inherited file may deny some permission settings. For example, if a Sprite process creates a group of readable and written files, and the inherited file creates a blocked word, the two permissions are blocked, then, the Group read and write operations are not allowed.
7. Process sigchld Signal
Processing sigchld signals is not necessary. However, some processes, especially server processes, often generate requests from child processes when requests arrive. If the parent process does not wait for the child process to end, the child process will become a zombie process (zombie) and still occupy system resources. If the parent process waits for the child process to end, it will increase the burden on the parent process and affect the concurrent performance of the server process. Under System V, the operation of sigchld signal can be set as SIG-IGN simply:
Signal (sigchld, sig_ign );
In this way, the kernel will not generate a zombie process when the child process ends. This is different from bsd4. In bsd4, the zombie process must be released only after the child process ends.
Daemon instance
The daemon instance consists of the main program test. C and the initialization program init. C. The main program sends
The Log Test. log in the directory reports the running status. The init_daemon function in the initialization program is responsible for generating the daemon process.
Void make_daemon (void)
{
Pid_t PID;
File * lockfd;
Sigset_t sighup;
Int I;
Extern pid_t getsid (pid_t );
PID = fork (); // generated by the first sub-process
If (PID <0 ){
Printinfo ("fork error! ", Infoerror );
Exit (failexit );
} Else if (pid>; 0 ){
Printinfo ("fork 1 OK! ", Infoscreen );
Exit (okexit); // exit the parent process to get rid of shell control.
}
PID = getpid (); // obtain the ID of the sub-process itself
Lockfd = fopen (pidfile, "W"; // The PID is written to the file below
If (lockfd! = NULL ){
Fprintf (lockfd, "% d/N", pid );
Fclose (lockfd );
} // Write PID
If (getsid (0 )! = PID) {// create a new session
If (setsid () <0 ){
Printinfo ("backupdaemon setsid error! ", Infoerror );
Perror ("setsid ";
}
}
If (pid = fork () {// generates a child process again. This is the child process.
Exit (0); // exit the previous generation process
} Else if (PID <0 ){
Exit (1 );
}
Close (1); // close the file
Close (2 );
Chdir (rundir); // change the running directory
Umask (022); // change the File Permission
}
Daemon error output
The daemon does not belong to any terminal, so when some information needs to be output, it cannot directly output the information to the standard output and standard error output as the general program does. We usually do not want every daemon to write its own error messages to a separate file. It is a headache for system administrators to remember which daemon writes to which record files and regularly check these files. Therefore, we need a centralized mechanism for logging errors of daemon processes. Currently, many systems have introduced syslog record processes for this purpose.
Since Berkeley has developed and widely used BSD syslog, the BSD Syslog mechanism has been used by most daemon. The following describes the usage of BSD syslog. There are three methods to generate record messages:
1. the kernel routine can call the log function. Any user process can read these messages by opening and reading the/dev/klog device. Because we have no intention of writing routines in the kernel, we will not describe this function further.
2. Most user processes (Daemon) Call the syslog function to generate a record message. The call sequence is described below. This allows the message to be sent to the Unix-domain datagram interface/dev/log.
3. A user process on this host or a user process connecting to another host on this host through a TCP/IP network can send the recorded messages to UDP port 514.
Note: syslog functions do not generate these UDP datagram-they require that the process that generates this record message have explicit network programming.
Generally, the Syslog daemon reads record messages in three formats. This daemon reads a configuration file at startup. Generally, the file name is/etc/syslog. conf, which determines where different types of messages should be sent. For example, an emergency message can be sent to the system administrator (if logged on) and displayed on the console. A warning message can be recorded in a file. This mechanism provides syslog functions. The Calling format is as follows:
# Include <syslog. h>;
Void openlog (char * Ident, int option, int facility );
Void syslog (INT priority, char * format ,......)
Void closelog ();
Openlog is optional. If openlog is not called, openlog is automatically called when syslog is called for the first time. You can also choose to call closelog, which only disables the descriptor used for communicating with the Syslog daemon. By calling openlog, we can specify an ident. Later, this ident will be added to each recorded message. IDENT is generally the program name (for example, Cron, inetd ). Option has four possibilities:
Log_cons if the log message cannot be sent to syslog through Unix domain datagram, write the message to the console.
Log_ndelay1 immediately opens the Unix domain datagram interface to the Syslog daemon, instead of waiting until the first message is recorded. Generally, this interface is not enabled until the first message is recorded.
In addition to sending a log message to syslog, log_perror also sends it to a standard error. This option is only supported by 4.3bsdreno and later versions.
Log_pid each message contains a process ID. This option is used by the daemon that fork a sub-process for each request.
The purpose of setting the facility parameter in openlog is to allow the configuration file to describe that messages from different facilities are processed in different ways. If you do not call openlog or call it with a facility of 0, you can describe the facility as a part of the priority parameter when calling syslog. Call syslog To generate a record message. The priority parameter is a combination of facility and level. The values they can select are listed below. Level values are sorted in order of priority from advanced to lowest.
A complete piece of code:
# Include <stdio. h>;
# Include <fcntl. h>;
# Include <signal. h>;
# Include <unistd. h>;
# Define running_dir "/tmp"
# Define lock_file "Exampled. Lock"
# Define LOG_FILE "Exampled. log"
Void log_message (filename, message)
Char * filename;
Char * message;
{
File * logfile;
Logfile = fopen (filename, "");
If (! Logfile) return;
Fprintf (logfile, "% s/n", message );
Fclose (logfile );
}
Void signal_handler (SIG)
Int SIG;
{
Switch (SIG ){
Case sighup:
Log_message (LOG_FILE, "hangup signal catched ");
Break;
Case sigterm:
Log_message (LOG_FILE, "Terminate signal catched ");
Exit (0 );
Break;
}
}
Void daemonize ()
{
Int I, LFP;
Char STR [10];
If (getppid () = 1) return;/* already a daemon */
I = fork ();
If (I <0) Exit (1);/* fork Error */
If (I>; 0) Exit (0);/* parent exits */
/* Child (Daemon) continues */
Setsid ();/* obtain a new process group */
For (I = getdtablesize (); I >;= 0; -- I) Close (I);/* close all descriptors */
I = open ("/dev/null", o_rdwr); DUP (I);/* handle standart I/O */
Umask (027);/* Set newly created file permissions */
Chdir (running_dir);/* change running directory */
LFP = open (lock_file, o_rdwr | o_creat, 0640 );
If (LFP <0) Exit (1);/* can not open */
If (lockf (LFP, f_tlock, 0) <0) Exit (0);/* can not lock */
/* First Instance continues */
Sprintf (STR, "% d/N", getpid ());
Write (LFP, STR, strlen (STR);/* record PID to lockfile */
Signal (sigchld, sig_ign);/* ignore child */
Signal (sigtstp, sig_ign);/* ignore tty signals */
Signal (sigttou, sig_ign );
Signal (sigttin, sig_ign );
Signal (sighup, signal_handler);/* Catch hangup signal */
Signal (sigterm, signal_handler);/* Catch kill signal */
}
Main ()
{
Daemonize ();
While (1) sleep (1);/* run */
}
1. Compile CC-O Exampled examped. c
2. Run./Exampled
3. Test the daemon process
PS-Ef | grep Exampled (or PS-Aux on BSD systems)
4. Log
Tail-F/tmp/Exampled. Log
5. Test Signal
Kill-HUP 'cat/tmp/Exampled. lock'
6. Termination
Kill 'cat/tmp/Exampled. lock'