1. 什么是守护进程
守护进程,也叫做精灵进程Daemon,是Linux中的后台服务进程,通常独立于控制终端并且周期性的执行某种任务或等待处理某些发生的事件,一般使用d结尾的名字。
Linux后台的一些系统服务进程,没有控制终端,不能直接和用户交互,不受用户登录、注销的影响,一直在运行着,这些就是守护进程,比如,预读入缓输出机制的实现,ftp服务器,nfs服务器等等。而我们运行的普通进程在终端关闭的时候就会随之终止,它实际上是这么实现的,当关闭终端的时候,会给所有进程发送一个1号信号SIGHUP,进程收到这个信号就会进行相应动作。创建一个守护进程最关键的一步是调用setsid函数创建一个新的session,并成为session leader。
通过 ps aux 可以查看进程是否有依靠的终端,?表示不依赖终端,红色标出的表示依赖这个终端。
2. 如何创建守护进程模型
- ① 创建子进程,父进程退出
所有工作在子进程中进行,形式上脱离了控制终端。 - ② 在子进程中创建会话
使用setsid()函数创建会话,使子进程完全独立出来,脱离控制(脱离终端)。 - ③ 改变当前目录为根目录
使用chdir()函数改变目录为根目录,防止占用可卸载的文件系统,也可以换成其他路径(最好是家目录)。这一步使非必须的,目的是使进程和某个目录脱离联系,不然的话可能会影响到出于其他原因而对该目录的删除等需求。 - ④ 重设文件权限掩码
使用umask()函数重设文件掩码,即可以防止继承的文件创建屏蔽字来拒绝某些权限,也可以增加守护进程的灵活性。一般设置为0002或0022,实际上每个进程都有自己的掩码,比如我们在shell下输入命令umask就可以看到shell进程的掩码,这个掩码决定了在shell进程下创建文件或目录时。文件所拥有的默认权限。 - ⑤ 关闭文件描述符
继承的打开文件不会用到,浪费系统资源,无法卸载。这一步也是非必要的,主要是考虑资源的合理利用。 - ⑥ 开始执行守护进程的核心工作
- ⑦ 守护进程退出处理程序模型(非必要)
3. 守护进程编程实战
案例:创建一个守护进程,每经过30秒在$HOME/log/目录下创建一个文件,并命名为 file.time
/************************************************************ >File Name : daemon_test.c >Author : Mindtechnist >Company : Mindtechnist >Create Time: 2022年05月24日 星期二 22时10分06秒 ************************************************************/ #include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <signal.h> #include <sys/time.h> #include <time.h> #include <string.h> #include <stdlib.h> #define FILE_NAME "%s/log/date.%ld" void mkfile(int signo) { char* homedir = getenv("HOME"); char filename[256] = {0}; sprintf(filename, FILE_NAME, homedir, time(NULL)/*当前时间戳*/); int fd = open(filename, O_RDWR | O_CREAT, 0644); if(fd < 0) { perror("open err"); _exit(1); /*守护进程也会退出*/ } close(fd); } int main(int argc, char* argv[]) { /*创建子进程,父进程退出*/ pid_t pid = fork(); if(pid > 0) { _exit(1); } /*成为会长*/ setsid(); /*设置掩码*/ umask(0); /*切换目录*/ chdir(getenv("HOME")); /*关闭文件描述符*/ close(0); close(1); close(2); /*执行核心逻辑*/ struct itimerval mit = {{30, 0}, {30, 0}}; setitimer(ITIMER_REAL, &mit, NULL); struct sigaction mact; mact.sa_flags = 0; sigemptyset(&mact.sa_mask); mact.sa_handler = mkfile; sigaction(SIGALRM, &mact, NULL); while(1) { sleep(1); } /*退出*/ return 0; }
4. nohup与&后台执行
- nohup:表示忽略1号信号SIGHUP,这个信号是杀死进程的,当终端或shell退出时,发送给所有进程。
SIGHUP 1 Term Hangup detected on controlling terminalor death of controlling process
- &:表示后台运行程序。
一般我们后台执行程序时是这样用的
nohup ./a.out > file.log &
这样运行的程序就是一个守护进程,也可以达到上面那样创建守护进程的效果。