守护进程的创建
什么是守护进程?
守护进程是运行在后台的一种特殊进程,它独立于控制终端并且周期性地执行某种任务或循环等待处理某些事件的发生; 守护进程一般在系统启动时开始运行,除非强行终止,否则直到系统关机才随之一起停止运行。
为了方便区分,守护进程的文件名一般以d结尾,例如xxxd.cpp。
如何创建守护进程?
①fork()创建子进程,父进程退出。由于主进程与终端强制关联在一起,当终端关闭时主进程也就销毁了,所以我们需要一个新的进程,让它在终端关闭后,依旧存活。即确保子进程不是一个进程组的组长,从而避免接收终端发送的信号。
②setsid()创建新会话 。创建一个新的会话,使fork()出来的子进程成为新的会话的首进程,新的进程组的组长,并且不受终端控制。
③再次fork()子进程。这一步是为了禁止进程重新获得控制终端,确保进入新会话的是子进程。
④修改当前目录为根目录(或其他不常更改的目录)。这是为了防止出现如果该进程处于可被卸载的文件系统上,当需要写在这个文件系统时,卸载不掉的情况。
⑤重设文件权限掩码。因为子进程继承父进程文件权限掩码,即屏蔽掉文件权限中的对应位。子进程需将其重置为0,即拥有更大的权限,从而提高该守护进程灵活度。
⑥关闭所有文件描述符。由于子进程会继承父进程中所有打开的文件描述符,不关闭不仅会造成资源的浪费,也会影响到子进程的文件系统。
⑦处理僵尸进程(如果存在的话)。
实践
这里我们创建守护进程来模拟日志系统。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <time.h>
#include <sys/stat.h>
int main()
{
/*创建子进程*/
pid_t pid = fork();
if (pid != 0)
{
/*父进程退出*/
exit(0);
}
/*创建新会话*/
setsid();
/*确保进入新会话的是子进程*/
pid = fork();
if (pid != 0)
{
exit(0);
}
/*改变当前目录为根目录*/
chdir("/");
/*重置文件掩码*/
umask(0);
/*获取所有打开的文件描述符*/
int maxfd = getdtablesize();
/*关闭所有文件描述符*/
for (int i = 0; i < maxfd;i++)
{
close(i);
}
/*接下来是守护进程该做的事情*/
while(1)
{
FILE *fp = fopen("/tmp/defend.log", "a");
if (fp == NULL)
{
printf("打开文件失败/n");
break;
}
time_t tv;
time(&tv);
fprintf(fp, "time is %s", asctime(localtime(&tv)));
fclose(fp);
/*睡眠,模拟守护进程在干其他事情*/
sleep(10);
}
exit(0);
}
运行结果:
当我们使用ps -ef(查看所有进程)命令时,可以看到
其中,?代表此进程不依附于任何终端。
当我们使用cd /tmp时,可以看到
确实存在一个日志文件。
当我们使用tail -f defend.log命令时,可以看到
日志文件一直在写入数据(使用Ctrl+c)来关闭。
如何关闭守护进程?
①当守护进程所在的计算机/服务器关闭时,守护进程也就自动停止了。
②使用ps -ef | grep defend(守护进程的文件名)来获取到守护进程的pid号,然后使用kill+pid号来停止守护进程。