1. Daemon creation steps
Daemons are processes without terminals. They run in the background and are often started when the system boots So how do I create a daemon?
Refer to APUE 13.3 to create a daemon:
- Call umask to set the permission mask word (umask) of the file created by the process to facilitate the daemon to create the file
umask is usually set to 0. If you call the library function to create a file, it can be set to 007 - Call fork, and the parent process exit s
To call setsid to create a session, you need to ensure that the calling process (sub process) is not the process group leader. fork sub process can ensure this
PS: it is uncertain how to start the program. It may or may not be the process group leader. - Call setsid to create a new session
The subprocess calls setsid to become the new session header process and the leader of the new process group, and disconnects the terminal.
PS: calling setsid to the process group leader will fail, return - 1, and errno will be set. Therefore, you must first pass fork + parent process exit, so that the child process will become an orphan process and be adopted by init, so as to ensure that the child process is not the process leader. - Call fork again and the parent process exit s (optional)
It is not necessary, mainly to ensure that the process cannot obtain the terminal again through open /dev/tty, because when open is called, the system will create a control terminal for the first process of the session by default. - Call chdir to change the current working directory to the root directory
Daemons generally exist for a long time. When daemons exist, they cannot uninstall the working directory To avoid this, change the current working directory to the root directory ("/") - Call close to close all unnecessary file descriptors
open_ Max, getrlimit and sysconf (_sc_open_max) can obtain the highest value of the file descriptor - Open the / dev/null file so that the file descriptor 0,1,2 points to the file
It can effectively prevent accidental effects
2. Create daemon code
The custom function daemon converts a process into a daemon
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <sys/resource.h> #include <sys/time.h> #include <errno.h> #include <unistd.h> #include <syslog.h> #include <signal.h> #include <sys/types.h> #include <sys/stat.h> #include <stdarg.h> #define MAXLINE 200 static void err_doit(int errnoflag, int error, const char *fmt, va_list ap) { char buf[MAXLINE]; // Convert the format string fmt (parameter ap) into a string and store it in buf vsnprintf(buf, MAXLINE - 1, fmt, ap); if (errnoflag) { // The last parameter of snprintf is const char *, and the last parameter of vsnprintf is va_list, same function snprintf(buf + strlen(buf), MAXLINE - strlen(buf) - 1, ": %s", strerror(error)); } strcat(buf, "\n"); // Paste "\ n" at the end of buf and end with '\ 0' fflush(stdout); // In case stdout and stderr are the same equipment, flush stdout first fputs(buf, stderr); } /** * Fatal error related to system call * Print messages and terminate programs */ static void err_quit (const char *fmt, ...) { va_list ap; // va_start, va_end pairing to get variable parameters, Store to va_list ap va_start(ap, fmt); err_doit(0, 0, fmt, ap); va_end(ap); exit(1); } /** * Convert a process to a daemon * Steps: * 1. Call umask to set umask for creating file permissions * 2. Call fork to let the parent process exit and the child process become an orphan process * 3. Call setsid to create a new session. The sub process becomes the leader of the new session header process and incoming process group, and disconnect the control terminal * 4. Call fork again and let the parent process exit. The child process is not the first process of the session (to prevent the control terminal from being obtained again) * 5. Change the current working directory to the root directory to prevent the directory from being uninstalled * 6. Close unneeded file descriptors * 7. Open / dev/null with file descriptor 0,1,2 */ void daemonize(const char *pname) { int i, fd0, fd1, fd2; pid_t pid; struct rlimit rl; struct sigaction sa; // 1. Call umask to modify the umask of creating file permissions umask(0); if (getrlimit(RLIMIT_NOFILE, &rl) < 0) { // <=> sysconf(_SC_OPEN_MAX); err_quit("%s: getrlimit error", pname); } // 2. Call fork, the parent process exits, and the child process is called an orphan and adopted by the init process if ((pid = fork()) < 0) err_quit("%s: fork error", pname); else if (pid > 0) { // Parent process exit(0); } // 3. Call setsit to create a new session, and the child process becomes the first process setsid(); /* Situation of sending SIGHUP signal: 1)When the terminal is closed, the signal is sent to the first process of the session and the process submitted as a job (the process run by the shell in & mode) 2)session When the first process exits, the signal is sent to all foreground processes in the same session 3)If the parent process exits, the process is formed into an orphan process group, and some processes in the process group are stopped (SIGSTOP signal or sigstp signal is received), the signal will be sent to each member of the process group */ sa.sa_handler = SIG_IGN; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; if (sigaction(SIGHUP, &sa, NULL) < 0) err_quit("%s: can't ignore SIGHUP", pname); // 4. Call fork again if ((pid = fork()) < 0) err_quit("%s: fork error", pname); else if (pid > 0) exit(0); // 5. Change the working directory to "/ (root directory) if (chdir("/") < 0) err_quit("%s: can't change directory to /", pname); // 6. Close unnecessary file descriptors if (rl.rlim_max == RLIM_INFINITY) rl.rlim_max = 1024; for (i = 0; i < rl.rlim_max; ++i) close(i); #if 1 // 7. Attach file descriptor 0,1,2 to / dev/null // Since all file descriptors have been closed previously, reopen and the file descriptors obtained by DUP are incremented fd0 = open("/dev/null", O_RDWR); fd1 = dup(0); fd2 = dup(0); #else open("/dev/null", O_RDONLY); open("/dev/null", O_RDWR); open("/dev/null", O_RDWR); #endif /* Establish a connection to syslog LOG_CONS: If it cannot be sent to the syslogd daemon, it is registered to the console LOG_DAEMON: Identifies that the type of message sending process is system daemon */ openlog(pname, LOG_CONS, LOG_DAEMON); /* File descriptor exception check: under normal conditions, fd0, FD1 and fd2 should be 0, 1 and 2 respectively */ if (fd0 != 0 || fd1 != 1 || fd2 != 2) { syslog(LOG_ERR, "unexpected file descriptors %d %d %d", fd0, fd1, fd2); exit(1); } }
main function
#include <stdio.h> #include <unistd.h> #include "daemonize.h" int main() { char *s = "mydaemonize"; printf("ready to convert a normal process into a daemonize process\n"); daemonize(s); while(1) sleep(1); return 0; }
Check Daemons
Compile and run the new daemon a.out
$ ./a.out $ ps -efj UID PID PPID PGID SID C STIME TTY TIME CMD martin 12596 1614 12595 12595 0 23:27 ? 00:00:00 ./a.out $ ps -efj | grep 12595 martin 12596 1614 12595 12595 0 23:27 ? 00:00:00 ./a.out
Termination daemon
Using the kill -9 command
$ kill -9 12596