1. 目的:
为了周期性的执行某个任务,或者防止程序出现意外情况挂掉。
2.相关概念:
父进程(PPID),子进程,进程(PID)
- 场景1:这个线程属于该终端的第一个程序,于是它的父线程就是这个shell,比如下图的ps指令就是这个终端的第一个程序,于是它的父进程就是7073,也就是这个shell的PID。
- 场景2:这个线程是在程序中创建的子线程,那么这个子线程的父进程ID就是这个程序的ID。比如下图的父进程是22554,创建的子线程是22555,可以看出子线程的父进程就是22554。
进程组(PGID):
-
组长进程标识: 其进程组ID==其进程ID
- 组长进程可以创建一个进程组,创建该进程组中的进程,然后终止
- 只要进程组中有一个进程存在,进程组就存在,与组长进程是否终止无关
- 进程组可根据这个shell中的不同程序来创建,一般一个终端命令创建一个进程组。而我们的可执行程序也是一组进程组。
会话组(SID):
多个进程组构成一个会话组,比如打开一个终端,有前台进程组,后台进程组等等,然后这些进程组一起构成一个会话组。。
3.创建流程:
- 创建子进程。父进程退出:fork()
- 现有的机制是:某个运行在终端里面的程序,如果关闭终端,就会杀死这个线程。。而使用了setdsid()后,也就是子进程独立出来,不受限于终端。关闭终端后,这个线程也不会被关闭。
- 改变工作目录chdir()函数:守护线程--脱离文件目录防止目录被删除,导致守护线程不可用。
- 重设文件权限掩码 umask()函数
- 关闭文件描述符
- 开始守护进程的任务
- 退出
目的:
- 创建一个守护进程:每分钟在$HOME/log创建一个文件:名为程序名,时间戳。。
执行步骤:
- 编译生成可执行文件。。。
- 执行,在终端可看到打印。。不受终端的限制。。
- 如果想要杀死守护线程:ps aux即可看到这个进程,然后kill即可
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <sys/time.h>
#include <signal.h>
#include <time.h>
// ps aux即可查看正在运行的线程 ---即可kill
// 此代码执行后,可脱离父进程(终端)的限制
#define _file_name_prefix_ "%s/log/mydaemon.%s" // 定义文件格式
void touchfile(int num) {
char *HomeDir = getenv("HOME");
//printf("%s\n",HomeDir);
const time_t demo = time(NULL);
//printf("%s\n",ctime(&demo));
char str_Filename[250] = {0};
sprintf(str_Filename,_file_name_prefix_,HomeDir,ctime(&demo));
printf("%s\n",str_Filename);
int fd = open(str_Filename,O_RDWR|O_CREAT, 0666); //设置文件的可执行权限
if (fd < 0) {
perror("open error");
exit(1);
}
close(fd);
}
int main() {
//创建子进程,父进程退出,独立于终端
pid_t pid = fork(); //父进程复制出一个和当前进程一样的子进程
if(pid > 0) {
printf("son\n");
//exit(1); // 如果exit结束执行的话,那么子进程会成为孤儿进程,并被init收养。
} else if(pid == 0) {
printf("dady\n");
}
pid_t pid1 = setsid();
if (-1 == pid1) {
printf("this is process leader\n"); //当前线程是会话(session)的领头进程
exit(1); // 如果exit结束执行的话,那么子进程会成为孤儿进程,并被init收养。
} else {
printf("this is son %d\n",pid1); // 子进程重新获得了一个新的会话(session)id,那么此时父进程死了也不会影响到子进程。。。
// 那么此时起子线程的任务就可以作为守护进程
}
//设置掩码
//umask(0);
//切换目录
//chdir(getenv("home")); // 切换到home目录
//关闭文件描述符
//执行核心逻辑
struct itimerval my_it = {
{6, 1}, // 每次的任务之间间隔6s+1us
{1, 2} // 设置定时器setitimer后1s+2us开始执行任务
};
setitimer(ITIMER_REAL, &my_it, NULL); //ITIMER_REAL--系统真实时间,发出sigalrm信号
struct sigaction act;
act.sa_flags = 0;
sigemptyset(&act.sa_mask); // 将参数信号集初始化并清空.--这里感觉就是把sa_flags清0
act.sa_handler = touchfile; // 收到信号执行的函数
sigaction(SIGALRM,&act,NULL);
while(1) {
sleep(1);
}
}