Home  >  Article  >  System Tutorial  >  Daemons in Linux: How to write and use simple daemons

Daemons in Linux: How to write and use simple daemons

PHPz
PHPzforward
2024-02-14 22:42:33578browse

The daemon process is a special process in the Linux system. It runs in the background without a control terminal and is not subject to user interference. It is responsible for performing some system or application-related tasks and functions. The role of the daemon is to improve the stability and efficiency of the system to deal with some unexpected accidents or abnormalities. In embedded Linux devices, daemon processes can be used to protect the main process in the system and prevent it from ending abnormally, causing the system to completely crash and damaging the user experience. But, do you really understand the daemon process under Linux? Do you know how to write and use a simple daemon under Linux? This article will introduce you to the relevant knowledge of daemon processes under Linux in detail, allowing you to better use and understand this powerful process type under Linux.

Linux 下的守护进程:如何编写和使用简单的守护进程

Create a daemon process in the Linux device to protect the main process in the system and prevent some unexpected accidents from causing the main process to end abnormally, causing the system to completely shut down without any response, destroying the user experience. However, after reviewing a lot of information, I found that most people only talked about how to create and implement daemon processes on the x86 platform, and no one introduced how to create and implement daemon processes on embedded platforms. So, after some exploration and a general understanding of everything from principles to code, I came up with some ideas myself. Below is a brief summary and organization.

1. Technical principles

The following is an excerpt from the Internet, about the introduction and description of the daemon process in the x86 Linux system.

Daemon is a special process that runs in the background. It is independent of the control terminal and periodically performs certain tasks or waits for processing of certain events.

The daemon process is a special orphan process. This kind of process is separated from the terminal. Why should it be separated from the terminal? The reason why it is separated from the terminal is to prevent the process from being interrupted by information generated by any terminal, and its information during execution will not be displayed on any terminal. Since in Linux, the interface through which each system communicates with users is called a terminal, every process that starts running from this terminal will be attached to this terminal. This terminal is called the control terminal of these processes. When the control terminal is closed, The corresponding processes will be automatically closed. However, the daemon process can break through this limitation. It is separated from the terminal and runs in the background. The purpose of being separated from the terminal is to prevent the information during the running process from being displayed in any terminal and the process will not be accessed by any terminal. Interrupted by the generated terminal message. It starts running when it is executed and does not exit until the entire system is shut down (of course it can be considered as killing the corresponding daemon process). If you want a process not to be affected by user or interruption or other changes, then you must turn this process into a daemon process.

2. Design steps

For Linux systems on the x86 platform, theoretically, in order to achieve the above effects, the daemon process has a strict set of implementation steps. In other words, the daemon process must remove some system-related restrictions at the beginning of startup, so that it can run stably in the background without being interfered with and affected by other tasks.

The following is the basic process of writing a daemon on the x86 platform:

  1. Block some signals that control terminal operations. This is to prevent the control terminal from being disturbed and exiting or hanging before the daemon is running. For more detailed usage of signals, please see "Signal Interrupt Handling".
  2. Runs in the background. This is to put the daemon process into the background to avoid hanging the control terminal. The method is to call fork() in the process to terminate the parent process and let the daemon execute in the background in the child process.
  3. Detach control of terminals, login sessions, and process groups. It is necessary to first introduce the relationship between processes and control terminals, login sessions and process groups in Linux: a process belongs to a process group, and the process group number (GID) is the process number (PID) of the process group leader. A login session can contain multiple process groups. These process groups share a controlling terminal. This controlling terminal is usually the shell login terminal from which the process was created. Controlling terminals, login sessions, and process groups are usually inherited from the parent process. Our goal is to get rid of them and not be affected by them. Therefore, setsid() needs to be called to make the child process the new session leader. After the setsid() call is successful, the process becomes the new session group leader and new process group leader, and is separated from the original login session and process group. Due to the exclusivity of the session process to the control terminal, the process is detached from the control terminal at the same time.
  4. Prevents the process from reopening the controlling terminal. Now, the process has become the terminalless session leader, but it can reapply to open a controlling terminal. You can prevent a process from reopening the controlling terminal by making it no longer the session leader, by creating a child process again.
  5. Close an open file descriptor. A process inherits open file descriptors from the parent process that created it. If it is not closed, system resources will be wasted, the file system where the process is located will not be able to be unmounted, and unpredictable errors will occur.
  6. Change the current working directory. While a process is active, the file system containing its working directory cannot be dismounted. Generally you need to change the working directory to the root directory. For core dumps that need to be performed, the process writing the run log changes the working directory to a specific directory such as /tmp.
  7. Reset file creation mask. A process inherits the file creation mask from the parent process that created it. It may modify the access permissions of files created by the daemon. To prevent this, the file creation mask must be cleared.
  8. Handle the SIGCHLD signal. For some processes, especially server processes, child processes are often generated to handle requests 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 occupy system resources (for more details about zombie processes, please see "Zombie Processes"). If the parent process waits for the child process to end, it will increase the burden on the parent process and affect the concurrency performance of the server process. Under Linux you can simply set the operation of the SIGCHLD signal to SIG_IGN. In this way, the kernel will not generate zombie processes until the child process ends.


The following is a complete set of source code taken from a senior’s blog:

#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
int init_daemon(void) { 
    int pid; int i; // 1)屏蔽一些控制终端操作的信

号  signal(SIGTTOU,SIG_IGN); signal(SIGTTIN,SIG_IGN); signal(SIGTSTP,SIG_IGN); signal(SIGHU

P ,SIG_IGN); // 2)在后台运行  if( pid=fork() ){// 父进程  exit(0);//结束父进程,子进程继

续  }else if(pid 的宏定义  // NOFILE 为文件描述符最大个数,不同系统有不同限

制  for(i=0; i

3. Actual situation

It can be seen from the above process logic and actual code that the daemon process of the x86 platform is actually quite complex and requires a lot of tedious initialization processes. However, for embedded platforms, the process seems to be simpler, without such complicated processing. Because, the daemon process is enabled in this embedded system. The purpose is simply to use this daemon process to start another daemonized process, and then regularly monitor whether the process is still running normally. Once it is found that it is running abnormally, just restart the process immediately.

So, I simplified the above process and got the following process:

  1. Start the process that needs to be monitored in the daemon process.
  2. Create a thread in the daemon process to regularly monitor the running status of the daemon process
  3. The daemon process determines whether the daemonized process is still running normally. Once it finds that it is running abnormally, it immediately restarts the process.

4. Actual source code

The following is the complete code of the daemon process module designed in this embedded system project.

/******************************************************************************************

******** ** 函数名称: lockfile ** 功能描述: 对文件加锁/解锁 ** 输入参数: lock: 1表示进行加锁处理,

0表示进行解锁处理 ** 输出参数: 无 ** 返回参

数: 无 *************************************************************************************

*************/ int tryto_lockfile(int fd, int lock) { struct flock fl; fl.l_type = (lock =

= 1) ? F_WRLCK : F_UNLCK; fl.l_start = 0; fl.l_whence = SEEK_SET; fl.l_len = 0; return (f

cntl(fd, F_SETLK, &fl)); } /***************************************************************

*********************************** ** 函数名称: get_proc_running_state ** 功能描述: 获取进程

运行状态 ** 输入参数: 无 ** 输出参数: 无 ** 返回参数: 返回-1表示路径错误 ** 返回参数: 返回0表示进程

从未运行过,返回1表示进程曾经运行过但是现在停止运行了,返回2表示进程正在运行

中 ****************************************************************************************

**********/ static int get_proc_running_state(const char* filename) { int fd; if (filename

 == NULL) { /* 文件名为

空 */ return -1; } fd = open(filename, O_RDWR, (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)); i

f (fd 0) { /* 文件不存在,表示进程从未运行

过 */ return 0; } if (tryto_lockfile(fd, 1) == -1) { /* 文件加锁失败,表示进程在运行

中 */ close(fd); return 2; } else { /* 文件加锁成功,表示进程已经消

失 */ tryto_lockfile(fd, 0); /* 此处要注意记得解锁和关闭文

件 */ close(fd); return 1; } } /***********************************************************

*************************************** ** 函数名称: proc_watch ** 功能描述: 检测进程是否有在运

行,没有运行则重新启动之 ** 输入参数: procname: 进程名 ** 输出参数: 无 ** 返回参数: 返回-1表示进程从

未运行过;返回0表示进程当前运行正常; ** 返回参数: 返回其他非零值表示进程不存在且已被重新启动,返回的值

是新的pid值 ***************************************************************************

***********************/ int proc_watch(const char *procname) { int result, state; char fi

lename[100]; result = 0; sprintf(filename, "/var/run/%s.pid", procname); state = get_proc_

running_state(filename); switch (state) { case 0: result = -1; break; case 1: result = sta

rt_proc_by_name(procname); break; case 2: result = 0; break; default: break; } return resu

lt; } /************************************************************************************

************** ** 函数名称: start_proc ** 功能描述: 启动进程开始运行 ** 输入参数: 无 ** 输出参

数: 无 ** 返回参数: 进程的ID号,若启动失败则返回

0 *****************************************************************************************

*********/ int start_proc_by_name(const char* procname) { pid_t pid, child_pid; char filen

ame[100]; sprintf(filename, "%s%s", PROC_FILE_PATH, procname); child_pid = 0; if (access(f

ilename, X_OK | F_OK) != 0) { /* 如果文件存在,并且可执行 */ return 0; } pid = fork(); /* 首

先要fork一个进程出来 */ if (pid 0) { /* 创建进程失

败 */ return 0; } else if (pid == 0) { /* 创建进程成功,此处是子进程的代

码 */ if (execl(filename, procname, (char *)NULL) != -1) { return 1; } else { return 0; } 

} else { /* 创建进程成功,此处是父进程代


******************************************************************* ** 函数名

称: thread_client_hdl ** 功能描述: client进程监视线程 ** 输入参数: 无 ** 输出参数: 无 ** 返回参

数: 无 *************************************************************************************

*************/ static void *thread_client_hdl(void *pdata) { int result; pdata = pdata; sl

eep(10); /* 第一次要进行延

时 */ for (;;) { printf("time to check thread_client...\n"); result = proc_watch(PROC_NAME



_CLIENT); if (result == -1) { printf("thread_client never exist...\n"); } else if (result 

== 0) { printf("thread_client running ok...\n"); } else { printf("thread_client has gone! 

but restarted...\n"); } sleep(10); } return NULL; } /*************************************

************************************************************* ** 函数名称: main ** 功能描

述: 入口主函数 ** 输入参数: 无 ** 输出参数: 无 ** 返回参

数: 无 *************************************************************************************

*************/ int main(int argc, char *argv[]) { int client_para; char *p, *process_name;

 pthread_t thread_client; process_name = argv[0]; /* 获取进程名

称 */ p = process_name + strlen(process_name); while (*p != '/' && p != process_name) { p-

-; } if (*p == '/') { process_name = p + 1; } printf("\"%s\" starting...\n", process_name)

; client_para = 0x01; if (pthread_create(&thread_client, NULL, thread_client_hdl, &client_

para) != 0) { printf("create thread_client failed!\n"); return 1; } if (start_proc_by_name

(PROC_NAME_CLIENT) == 0) { printf("start thread_client failed!\n"); return 1; } for (;;) {

 sleep(60); printf("i am still alive...\n"); } return 0; }

通过本文,你应该对 Linux 下的守护进程有了一个基本的了解,知道了它的定义、特点和用途。你也应该明白了如何在 Linux 下编写和使用简单的守护进程,以及使用守护进程时需要注意的一些问题和技巧。我们建议你在使用 Linux 系统时,使用守护进程来提高系统的稳定性和效率。同时,我们也提醒你在使用守护进程时要注意一些潜在的问题和挑战,如信号处理、日志记录、资源管理等。希望本文能够帮助你更好地使用 Linux 系统,让你在 Linux 下掌握守护进程的编写和使用。

The above is the detailed content of Daemons in Linux: How to write and use simple daemons. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:lxlinux.net. If there is any infringement, please contact admin@php.cn delete