一、基本概念
- 守护进程:运行周期长,在后台执行,不需要和用户交互
- 会话:打开一个终端,伴随着一个会话的产生,直到
exit
会话结束 - 会话首进程:在会话中运行的第一个进程,一般来讲是
/usr/bin/bash
- 进程组:单进程程序中只有一个进程,多进程程序中有多个进程,进程组最后一个成员消失,进程组才消失
- 组长进程:进程组中的第一个进程
当守护进程运行起来后,我们可能退出这个终端,并希望它一直执行。但是终端结束,会话结束,会话中所有进程结束。所以创建守护进程,就要为它独立创建一个会话,不要附着于某个终端,这样就不会因为关闭某个终端而结束这个进程,然后将当前进程转移到这个新的会话中。
而用于建立新会话的进程需要是普通的组员进程(不能是组长进程,更不能是会话首进程),因为会使用进程的id作为会话的id,如果原来这个进程就是一个会话首进程,那么这个进程的id已经标识了一个会话,再使用这个进程id标识会话就会重复
1. 会话id=会话首进程id
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
int main()
{
printf("sid=%d,pid=%d\n",getsid(0),getpid());
}
2. 组长进程id=进程组id
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
int main()
{
fork();
printf("sid=%d,pid=%d,gid=%d\n",getsid(0),getpid(),getpgrp());
}
父子进程同一进程组,同一会话。
二、创建守护进程
1.编程流程
-
fork()
:产生子进程,退出父进程,这时候子进程只是个普通的组员进程,不可能是组长或者会话首进程 。 -
setsid()
:创建一个新会话,将刚才fork()出的子进程从原来会话中脱离出来到新创建的这个会话中,这个进程即是组长又是会话首进程。 -
fork():退出首进程,这样就会失去会话首进程和进程组长的身份。(这一步可做可不做,失去了这个身份是为了不和其他终端关联,起保险作用)
后面几个步骤为了守护进程能够长久的运行做准备
-
由于守护进程运行时间长,所以把该进程工作目录改到根目录下
chdir("/")
,防止守护进程长期占用工作空间,导致其他进程无法使用 -
因为最后具体工作的位置可能有自己的掩码,会影响到创建文件的权限等等,所以用
umask(0)
清空掩码 -
close():关闭所有不用的描述符
-
如果程序还产生子进程,还需要处理僵死进程。
2. 代码
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/stat.h>
#include<time.h>
void set_demon()
{
pid_t pid=fork();
//退出父进程
if(pid!=0)
{
exit(0);
}
setsid();//创建新会话
//退出当前会话首进程,子孙进程成为会话唯一进程
pid=fork();
if(pid!=0)
{
exit(0);
}
chdir("/");//设置工作路径
umask(0);//清空掩码
int open_size=getdtablesize();//拿到打开文件数的最大数量
//关闭所有不用的描述符
for(int i=0;i<open_size;i++)
{
close(i);
}
}
int main()
{
set_demon();//将当前进程设置为守护进程
while(1)
{
//任何用户可在tmp下创建文件
FILE* fp=fopen("/tmp/a.log","a");
if(fp==NULL)
{
break;
}
time_t tv;
time(&tv);
fprintf(fp,"time is:%s",asctime(localtime(&tv)));
fclose(fp);// 这里不关闭,下次循环再fopen,返回的fp不一样,日志文件也无法写入新的信息
sleep(5);
}
}
tail
可以在文件内容发生变化的情况下把最后的内容打印出来