1. 守护进程概念:
独立于终端控制并周期性地执行处理某些任务的后台进程。
2. 守护进程创建步骤:
核心:让进程脱离控制终端→创建新会话。
(1)创建子进程,父进程退出(必须);
a) 所有工作在子进程中进行,形式上脱离控制终端(不占用终端),但用ps命令查看仍显示属于终端;
(2)子进程创建新会话(必须);
(创建新会话的核心目的是为了让守护进程在后台独立运行、脱离原先属于某个终端的进程组、会话,不受任何终端或信号的干扰,也不影响其他进程或会话)
a)setsid函数;
b)子进程完全独立,脱离终端控制;
c)子进程成为新会话的领导进程,拥有自己的进程组和会话ID;
(3)再次创建子进程,第一个子进程退出(非必须);
(最好加入此步骤,因为步骤1的子进程是会长进程,有能力重新打开终端,其他进程不能。 会长、组长进程退出了,此会话变成了无会长的会话,但不影响其中的其他进程的运行,反而使得守护进程更独立、安全)
a)fork函数;
b)防止第一个子进程重新打开控制终端,因为它是会话的领导进程;
(4)将工作目录改为根目录(非必须);
a)chdir函数;
b)防止当前工作目录是可卸载的文件系统;
(5)重设文件权限掩码(非必须);
(守护进程从父进程继承了文件权限掩码,可能会导致守护进程创建的文件或目录的权限不符合预期,例如缺少某些必要的读、写或执行权限。为避免该问题,守护进程可调用umask(0)函数来清除文件权限掩码,这样就可以根据需要来设置文件或目录的权限。)
a)umask函数;
b)防止继承的文件创建屏蔽子拒绝某些权限;
(6)关闭文件描述符(非必须);
a)继承的打开文件用不到,占用资源,无法卸载;
b)关闭标准输入、输出和错误输出,或者重定向到/dev/null或其他地方,避免输出到控制终端或影响其他程序;
(7)执行守护进程的任务(必须);
a)使用循环、定时器、信号等机制来周期性地或事件驱动地执行任务;
b)使用syslog或其他方式记录错误信息或日志信息,方便调试和监控;
c)处理可能出现的异常,如资源耗尽、死锁、崩溃等。
3. 守护进程示例:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
int main(int argc, const char* argv[]) {
pid_t pid = -1;
// 1.创建子进程、父进程退出;
pid = fork();
if (-1 == pid) {
perror("fork");
return 1;
}
if (pid > 0) {
exit(0);
}
// 2.创建新会话; 完全脱离控制终端
pid = setsid();
if (-1 == pid) {
perror("setsid");
return 1;
}
// 3.再次创建子进程,第一个子进程退出
pid = fork();
if (-1 == pid) {
perror("fork");
return 1;
}
if (pid > 0) {
exit(0);
}
// 4.改变工作目录
if (chdir("/") < 0) {
perror("");
return 1;
}
// 5.设置权限掩码
umask(0);
// 6.关闭文件描述符
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
// 7.执行核心任务
while (1) {
/*核心任务*/
sleep(1);
}
return 0;
}
运行结果:
注意:第一个a.out是创建成功的守护进程,隶属终端已经变成 “ ? ” ,表示不属于任何终端,
第二个是a.out是grep命令查看内容的高亮显示而已。