守护进程的概念和创建步骤
1. 进程组
也称之为作业。进程组ID等于第一个进程ID,因此可以通过进程ID是否等于进程组ID来判断该进程是不是进程组的组长。
只要进程组中有一个进程存在,进程组就存在,哪怕组长已经没了。
一个进程可以为自己或子进程设置进程组ID。
2. 会话
由多个进程组组成。
创建会话要注意以下5点:
- 调用进程不能是进程组组长,该进程变成新会话首进程(session header)
- 该进程成为一个新进程组的组长进程。
- 新会话丢弃原有的控制终端,该会话没有控制终端。
- 该调用进程是组长进程,则出错返回。
- 建立新会话时,先调动fork,父进程终止,子进程调用setsid(setsid后子进程不受终端影响,终端退出,不影响子进程)。
man 2 setsid
3. 守护进程
守护进程常常以“d”
结尾
3.1 创建守护进程模型
创建守护进程最关键的一步就是调用setsid函数创建一个新的Session,并成为Session Leader
- 创建子进程,父进程退出
所有工作在子进程中进行,形式上脱离了控制终端 - 在子进程中创建新会话
setsid()函数
使子进程完全独立出来,脱离控制 - 改变当前目录为根目录
chdir()函数
防止占用可卸载的文件系统
也可以换成其他路径 - 重设文件权限掩码
umask()函数
防止继承的文件创建屏蔽字拒绝某些权限
增加守护进程的灵活性 - 关闭文件描述符
- 开始执行守护进程核心工作
- 守护进程退出处理程序模型
其中,3、4、5步不必要
3.2 守护进程的步骤
- 创建子进程fork
- 父进程退出
- 子进程当会长setsid
- 切换工作目录$HOME
- 设置掩码umask
- 关闭文件描述符0,1,2,为了避免浪费资源
- 执行核心逻辑
3.3 创建一个守护进程实例:每份中在$HOME/log/ 创建一个文件 ,文件名—— 程序名.时间戳
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
#include<stdlib.h>
#include<signal.h>
#include<sys/time.h>
#include<time.h>
//通过宏定义文件名
#define _FILE_NAME_FORMAT_ "%s/log/mydaemon.%ld" //定义文件格式化
void touchfile(int num){
//获取家目录
char *HomeDir=getenv("HOME");
char strFilename[250]={0};
//获取时间戳的函数
//time(NULL);
sprintf(strFilename, _FILE_NAME_FORMAT_, HomeDir, time(NULL));
int fd=open(strFilename, O_RDWR|O_CREAT, 0666);
//文件打开失败
if(fd<0){
perror("open err");
exit(1);
}
close(fd);
}
int main(){
//创建守护进程的步骤如下
//创建子进程,父进程退出
pid_t pid=fork();
if(pid>0){
exit(1);
}
//当会长
setsid();
//设置掩码
umask(0);
//切换目录
//getenv获取环境变量
chdir(getenv("HOME"));//切换到家目录
//关闭文件描述符(一般通过测试之后,才会关闭文件描述符)
//close(0),close(1),close(2)
//执行核心逻辑
//设置一个定时器——每60秒来一次
struct itimerval myit={{60,0},{60,0}};
setitimer(ITIMER_REAL, &myit, NULL);
//注册捕获函数
struct sigaction act;
act.sa_flags=0;
//清空阻塞信号集
sigemptyset(&act.sa_mask);
//绑定捕获函数
act.sa_handler=touchfile;
//注册捕获函数
sigaction(SIGALRM, &act, NULL);
while(1){
//每隔一分钟在/home/itheima/log创建文件
sleep(1);
}
return 0;
}
3.4 nohup指令
指令nohup
去执行进程就不会收到第一个信号
通过nohup
指令也可以达到守护进程的创建效果。(可以把进程输出重定向到特定的地方)
- nohup 指令会让cmd收不到SIGHUP信号
- & 代表后台运行