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.
Article Directory
-
- prerequisite knowledge points
- Steps to create a daemon
- daemon process creation
-
- 1. Create a child process and copy the file descriptor and signal handler
- 2. Create a new session and become the first process of the session
- 3. Close file descriptors that are no longer needed
- 4. Modify the working directory to the root directory
- 5. Reset the file permission mask
- 6. Execute the main logic of the daemon process
- Daemon process creation total code
- Summarize
prerequisite knowledge points
- Process and process control : understand the basic concepts of process and system calls related to process control, such as
fork
,exec
,wait
and so on. - File descriptors : understand the concept and function of file descriptors, as well as related system calls, such as
open
,close
,dup
and so on. - Signal processing : Understand the concept of signal and the mechanism of signal processing, including signal sending, receiving and processing.
- 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
- Create a child process, and copy
fork
the file descriptor table and signal processing function of the parent process through system calls. - Call the system call in the child process
setsid
to create a new session and become the first process of the session. - Close file descriptors that are no longer needed, such as standard input, standard output, standard error output, etc.
- Change the working directory to the root directory to prevent the daemon process from occupying the file system.
- Reset the file permission mask so that the daemon creates files with appropriate permissions.
- Handling signals, such as
SIGCHLD
, ,SIGHUP
etc., you can choose to ignore or customize the processing function. - Run the main logic of the daemon, such as service monitoring, data processing, etc.
- 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 fork
the 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 signal
the function .SIGCHLD
signal_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 setsid
create 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 close
closed 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 umask
reset 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 while
a loop to continuously execute the main logic of the daemon. In this example, the daemon process executes a sleep
function 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:
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 .signal()
The function replicates the signal handler function, associating the SIGCHLD signal withsignal_handler()
the function.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.close()
The function closes file descriptors that are no longer needed, including standard input, standard output, and standard error output.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.umask()
The function resets the file permission mask to 0, ensuring that files created by the daemon have appropriate permissions.while
The loop is the main logic of the daemon process and can be programmed according to actual needs. In this example, the daemon process executes asleep()
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:
- Use
fork()
the function to create a child process, the parent process exits, and the child process continues to execute. - Use
setsid()
the function to create a new session and become the session's first process. - Close file descriptors that are no longer needed, including standard input, standard output, and standard error.
- Change the working directory to the root directory.
- Reset the file permission mask.
- Write the main logic of the daemon and
while
execute 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.