欢迎关注博主 Mindtechnist 或加入【Linux C/C++/Python社区】一起探讨和分享Linux C/C++/Python/Shell编程、机器人技术、机器学习、机器视觉、嵌入式AI相关领域的知识和技术。
专栏传送门 :《Linux从小白到大神》 | 系统学习Linux开发、VIM/GCC/GDB/Make工具、Linux文件IO、进程管理、进程通信、多线程等,请关注专栏免费学习。
守护进程,也叫做精灵进程Daemon,是Linux中的后台服务进程,通常独立于控制终端并且周期性的执行某种任务或等待处理某些发生的事件,一般使用d结尾的名字。
Linux后台的一些系统服务进程,没有控制终端,不能直接和用户交互,不受用户登录、注销的影响,一直在运行着,这些就是守护进程,比如,预读入缓输出机制的实现,ftp服务器,nfs服务器等等。而我们运行的普通进程在终端关闭的时候就会随之终止,它实际上是这么实现的,当关闭终端的时候,会给所有进程发送一个1号信号SIGHUP,进程收到这个信号就会进行相应动作。创建一个守护进程最关键的一步是调用setsid函数创建一个新的session,并成为session leader。
通过 ps aux 可以查看进程是否有依靠的终端,?表示不依赖终端,红色标出的表示依赖这个终端。
① 创建子进程,父进程退出
所有工作在子进程中进行,形式上脱离了控制终端。
② 在子进程中创建会话
使用setsid()函数创建会话,使子进程完全独立出来,脱离控制(脱离终端)。
③ 改变当前目录为根目录
使用chdir()函数改变目录为根目录,防止占用可卸载的文件系统,也可以换成其他路径(最好是家目录)。这一步使非必须的,目的是使进程和某个目录脱离联系,不然的话可能会影响到出于其他原因而对该目录的删除等需求。
④ 重设文件权限掩码
使用umask()函数重设文件掩码,即可以防止继承的文件创建屏蔽字来拒绝某些权限,也可以增加守护进程的灵活性。一般设置为0002或0022,实际上每个进程都有自己的掩码,比如我们在shell下输入命令umask就可以看到shell进程的掩码,这个掩码决定了在shell进程下创建文件或目录时。文件所拥有的默认权限。
⑤ 关闭文件描述符
继承的打开文件不会用到,浪费系统资源,无法卸载。这一步也是非必要的,主要是考虑资源的合理利用。
⑥ 开始执行守护进程的核心工作
⑦ 守护进程退出处理程序模型(非必要)
调用fork创建新进程,它会是将来的守护进程;
在父进程中调用exit,保证子进程不是进程组组长;
调用setsid创建新的会话期session;(当我们在客户端登陆服务器时,会建立一个会话,这就是会话期,在会话期会有很多进程组,比如我输入shell命令 ps -ef | gref test,这时候会执行ps命令和gref命令,这两个命令就是一个进程组,在进程组中会有一个进程组长,进程组的组号就等于进程组长的PID)。
打开telnet工具,登录服务器,登录校验成功以后,Linux服务器会在终端和服务器之间建立一个会话期,在这个会话期中,会默认启动一个shell程序,在会话期会有很多进程组,shell就是一个进程组,这里每一个进程都和终端相连,如果脱离终端,把进程放在一个新的会话期中并让进程脱离终端,就会变成一个守护进程,这是通过setsid函数实现 的;
将当前目录改为根目录(如果把当前目录作为守护进程的目录,当前目录不能被卸载,他作为守护进程的工作目录了);fork一个进程,把父进程杀死(因为父进程时进程组组长,她不能作为新会话期的id)
比如secureCRT打开一个会话就是一个会话期,再打开一个会话就会再创建一个会话期,命令tty可以查看控制终端
setsid创建一个新的会话,调用者进程会是这个会话期唯一的进程,是唯一的组长,调用者进程id即使组id也是会话期id,不能用进程组组长去调用setsid函数;
#include
#include
#include
#include
#include
#include
#include
int main()
{
pid_t pid;
pid = fork();
if(pid == -1)
{
perror("fork err");
exit(0);
}
if(pid > 0)
{
//父进程,杀掉,不能够作为新的会话期进程组长
exit(0);
}
if(pid == 0)
{
printf("pid: %d\n", getpid());
pid = setsid();
if(pid == -1)
{
perror("setsid err");
exit(0);
}
sleep(100);
// ps -ef 可以查看进程
}
return 0;
}
守护进程会关联一个目录,那么这个目录无法卸载,因为有一个守护进程和它关联
案例:创建一个守护进程,每经过30秒在$HOME/log/目录下创建一个文件,并命名为 file.time
/************************************************************
>File Name : daemon_test.c
>Author : Mindtechnist
>Company : Mindtechnist
>Create Time: 2022年05月24日 星期二 22时10分06秒
************************************************************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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;
}
nohup:表示忽略1号信号SIGHUP,这个信号是杀死进程的,当终端或shell退出时,发送给所有进程。
SIGHUP 1 Term Hangup detected on controlling terminalor death of controlling process
&:表示后台运行程序。
一般我们后台执行程序时是这样用的
nohup ./a.out > file.log &
这样运行的程序就是一个守护进程,也可以达到上面那样创建守护进程的效果。