[Linux operating system] the creation of the daemon process and the step-by-step implementation of code analysis

In Unix and Linux systems, a daemon is a process that runs in the background, independent of the terminal, and continues to perform certain tasks. Creating a daemon is a common task, and it can be used in various scenarios, such as network services, scheduled tasks, etc. This blog will introduce how to create a daemon process in C language and provide some sample code. By studying this blog, you will learn the general steps and precautions for creating a daemon, and how to write the main logic of a daemon. Whether you are a beginner or an experienced developer, this blog post will provide you with useful guidance in creating reliable daemons.

insert image description here


prerequisite knowledge points

  1. Process and process control : understand the basic concepts of process and system calls related to process control, such as fork, exec, waitand so on.
  2. File descriptors : understand the concept and function of file descriptors, as well as related system calls, such as open, close, dupand so on.
  3. Signal processing : Understand the concept of signal and the mechanism of signal processing, including signal sending, receiving and processing.
  4. File permissions and file system : Master the concept and setting method of Linux file permissions, as well as the basic operations of the file system.

I will not explain all the knowledge in this part. My previous blog has been involved, so you can go through it. This blog mainly talks about daemons: process creation, sharing and management. File descriptor signals describe file
 permissions
 and
 file
 systems


Steps to create a daemon

  1. Create a child process, and copy forkthe file descriptor table and signal processing function of the parent process through system calls.
  2. Call the system call in the child process setsidto create a new session and become the first process of the session.
  3. Close file descriptors that are no longer needed, such as standard input, standard output, standard error output, etc.
  4. Change the working directory to the root directory to prevent the daemon process from occupying the file system.
  5. Reset the file permission mask so that the daemon creates files with appropriate permissions.
  6. Handling signals, such as SIGCHLD, , SIGHUPetc., you can choose to ignore or customize the processing function.
  7. Run the main logic of the daemon, such as service monitoring, data processing, etc.
  8. Exit the daemon.

daemon process creation

Below we will step by step introduce the specific implementation of each step and the corresponding code.

1. Create a child process and copy the file descriptor and signal handler

First, we need to create a child process and copy forkthe file descriptor table and signal handler of the parent process through system calls.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>

void signal_handler(int signum) {
    
    
    // 信号处理函数的具体实现
}

int main() {
    
    
    pid_t pid = fork();
    if (pid < 0) {
    
    
        perror("fork error");
        exit(1);
    } else if (pid > 0) {
    
    
        exit(0);  // 父进程退出
    }
    
    // 子进程继续执行
    // 复制信号处理函数
    signal(SIGCHLD, signal_handler);
    
    // 子进程的主要逻辑
    // ...
    
    return 0;
}

In the above code, we created a child process through the fork system call, and exited directly in the parent process, making the child process an orphan process. In the child process, we set the handler function of the signal as a function through signalthe function .SIGCHLDsignal_handler


2. Create a new session and become the first process of the session

Next, we need to call the setsid system call in the child process to create a new session and become the first process of the session:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>

void signal_handler(int signum) {
    
    
    // 信号处理函数的具体实现
}

int main() {
    
    
    pid_t pid = fork();
    if (pid < 0) {
    
    
        perror("fork error");
        exit(1);
    } else if (pid > 0) {
    
    
        exit(0);  // 父进程退出
    }
    
    // 子进程继续执行
    // 复制信号处理函数
    signal(SIGCHLD, signal_handler);
    
    // 创建新的会话并成为会话的首进程
    if (setsid() < 0) {
    
    
        perror("setsid error");
        exit(1);
    }
    
    // 子进程的主要逻辑
    // ...
    
    return 0;
}

In the above code, we setsidcreate a new session through the system call and make the child process the first process of the session. Doing this keeps the daemon out of touch with the terminal and is unaffected by the terminal being closed.


3. Close file descriptors that are no longer needed

In the daemon process, we need to close file descriptors that are no longer needed, such as standard input, standard output, and standard error output.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>

void signal_handler(int signum) {
    
    
    // 信号处理函数的具体实现
}

int main() {
    
    
    pid_t pid = fork();
    if (pid < 0) {
    
    
        perror("fork error");
        exit(1);
    } else if (pid > 0) {
    
    
        exit(0);  // 父进程退出
    }
    
    // 子进程继续执行
    // 复制信号处理函数
    signal(SIGCHLD, signal_handler);
    
    // 创建新的会话并成为会话的首进程
    if (setsid() < 0) {
    
    
        perror("setsid error");
        exit(1);
    }
    
    // 关闭不再需要的文件描述符
    close(STDIN_FILENO);  // 关闭标准输入
    close(STDOUT_FILENO);  // 关闭标准输出
    close(STDERR_FILENO);  // 关闭标准错误输出
    
    // 子进程的主要逻辑
    // ...
    
    return 0;
}

In the above code, we closeclosed the file descriptors for standard input, standard output and standard error output using system calls.


4. Modify the working directory to the root directory

In order to prevent the daemon process from occupying the file system, we need to change the working directory to the root directory.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>

void signal_handler(int signum) {
    
    
    // 信号处理函数的具体实现
}

int main() {
    
    
    pid_t pid = fork();
    if (pid < 0) {
    
    
        perror("fork error");
        exit(1);
    } else if (pid > 0) {
    
    
        exit(0);  // 父进程退出
    }
    
    // 子进程继续执行
    // 复制信号处理函数
    signal(SIGCHLD, signal_handler);
    
    // 创建新的会话并成为会话的首进程
    if (setsid() < 0) {
    
    
        perror("setsid error");
        exit(1);
    }
    
    // 关闭不再需要的文件描述符
    close(STDIN_FILENO);  // 关闭标准输入
    close(STDOUT_FILENO);  // 关闭标准输出
    close(STDERR_FILENO);  // 关闭标准错误输出
    
    // 修改工作目录为根目录
    if (chdir("/") < 0) {
    
    
        perror("chdir error");
        exit(1);
    }
    
    // 子进程的主要逻辑
    // ...
    
    return 0;
}

In the above code, we use the chdir system call to change the working directory to the root directory.


5. Reset the file permission mask

In order for the daemon to create files with appropriate permissions, we need to reset the file permission mask. Here is a sample code:

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

void signal_handler(int signum) {
    
    // 信号处理函数的具体实现
}

int main() {
    
    
    pid_t pid = fork();
    if (pid < 0) {
    
    
        perror("fork error");
        exit(1);
    } else if (pid > 0) {
    
    
        exit(0);  // 父进程退出
    }
    
    // 子进程继续执行
    // 复制信号处理函数
    signal(SIGCHLD, signal_handler);
    
    // 创建新的会话并成为会话的首进程
    if (setsid() < 0) {
    
    
        perror("setsid error");
        exit(1);
    }
    
    // 关闭不再需要的文件描述符
    close(STDIN_FILENO);  // 关闭标准输入
    close(STDOUT_FILENO);  // 关闭标准输出
    close(STDERR_FILENO);  // 关闭标准错误输出
    
    // 修改工作目录为根目录
    if (chdir("/") < 0) {
    
    
        perror("chdir error");
        exit(1);
    }
    
    // 重设文件权限掩码
    umask(0);
    
    // 子进程的主要逻辑
    // ...
    
    return 0;
}

In the above code, we umaskreset the file permission mask to 0 using a system call.


6. Execute the main logic of the daemon process

The main logic of the daemon process can be written according to actual needs. Here is a sample code:

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

void signal_handler(int signum) {
    
    
    // 信号处理函数的具体实现
}

int main() {
    
    
    pid_t pid = fork();
    if (pid < 0) {
    
    
        perror("fork error");
        exit(1);
    } else if (pid > 0) {
    
    
        exit(0);  // 父进程退出
    }
    
    // 子进程继续执行
    // 复制信号处理函数
    signal(SIGCHLD, signal_handler);
    
    // 创建新的会话并成为会话的首进程
    if (setsid() < 0) {
    
    
        perror("setsid error");
        exit(1);
    }
    
    // 关闭不再需要的文件描述符
    close(STDIN_FILENO);  // 关闭标准输入
    close(STDOUT_FILENO);  // 关闭标准输出
    close(STDERR_FILENO);  // 关闭标准错误输出
    
    // 修改工作目录为根目录
    if (chdir("/") < 0) {
    
    
        perror("chdir error");
        exit(1);
    }
    
    // 重设文件权限掩码
    umask(0);
    
    // 子进程的主要逻辑
    while (1) {
    
    
        // 守护进程的具体逻辑
        sleep(1);
    }
    
    return 0;
}

In the above code, we use whilea loop to continuously execute the main logic of the daemon. In this example, the daemon process executes a sleepfunction every 1 second, simulating some operations that need to be performed continuously.


Daemon process creation total code

The following is the integration of each part of the code for creating the daemon above into a complete code, and an explanation of each part:

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

void signal_handler(int signum) {
    
    
    // 信号处理函数的具体实现
}

int main() {
    
    
    pid_t pid = fork();
    if (pid < 0) {
    
    
        perror("fork error");
        exit(1);
    } else if (pid > 0) {
    
    
        exit(0);  // 父进程退出
    }
    
    // 子进程继续执行
    // 复制信号处理函数
    signal(SIGCHLD, signal_handler);
    
    // 创建新的会话并成为会话的首进程
    if (setsid() < 0) {
    
    
        perror("setsid error");
        exit(1);
    }
    
    // 关闭不再需要的文件描述符
    close(STDIN_FILENO);  // 关闭标准输入
    close(STDOUT_FILENO);  // 关闭标准输出
    close(STDERR_FILENO);  // 关闭标准错误输出
    
    // 修改工作目录为根目录
    if (chdir("/") < 0) {
    
    
        perror("chdir error");
        exit(1);
    }
    
    // 重设文件权限掩码
    umask(0);
    
    // 子进程的主要逻辑
    while (1) {
    
    
        // 守护进程的具体逻辑
        sleep(1);
    }
    
    return 0;
}

Code explanation:

  1. fork()The function creates a child process. If the return value is less than 0, it means that the creation of the child process failed; if the return value is greater than 0, it means that the current parent process needs to exit; if the return value is equal to 0, it means that the current child process continues to execute .
  2. signal()The function replicates the signal handler function, associating the SIGCHLD signal with signal_handler()the function.
  3. setsid()The function creates a new session and becomes the leader of the session. If the return value is less than 0, it means that session creation failed.
  4. close()The function closes file descriptors that are no longer needed, including standard input, standard output, and standard error output.
  5. chdir()The function changes the working directory to the root directory. If the return value is less than 0, it means that the modification of the working directory failed.
  6. umask()The function resets the file permission mask to 0, ensuring that files created by the daemon have appropriate permissions.
  7. whileThe loop is the main logic of the daemon process and can be programmed according to actual needs. In this example, the daemon process executes a sleep()function every 1 second, simulating some operations that need to be performed continuously.

Summarize

This blog answers the question of how to create a daemon process in C language. The general steps to create a daemon include:

  1. Use fork()the function to create a child process, the parent process exits, and the child process continues to execute.
  2. Use setsid()the function to create a new session and become the session's first process.
  3. Close file descriptors that are no longer needed, including standard input, standard output, and standard error.
  4. Change the working directory to the root directory.
  5. Reset the file permission mask.
  6. Write the main logic of the daemon and whileexecute it continuously using a loop.

It should be noted that when creating a daemon process, issues such as signal processing functions, file descriptors, working directories, and file permissions need to be carefully handled to ensure that the daemon process can run normally without causing unnecessary impact on the system.

Guess you like

Origin blog.csdn.net/Goforyouqp/article/details/132392543