- 后台服务进程
- 独立于控制终端
- 周期性执行某任务
- 不受用户登录注销影响
- 一般采用以d结尾的名字(服务)
PGID
- 进程的组长
- 组里面的第一进程
- 进程组的ID等于进程组组长的ID
- 进程组组长如何选择
- 进程组中的第一个进程
会话即是多个进程组
- 创建一个会话的注意事项:
- 不能是进程组的组长
- 创建会话的进程成为新进程组的组长
- 有些时候需要root权限才能创建会话
- 创建出的新会话会丢弃原有的控制终端
- 获取进程所属的会话ID
pid_t getsid(pid_t pid);
- 创建进程所属的会话ID
pid_t setsid(void);
- fork子进程,父进程退出(必须)
- 子进程创建新会话(必须)
setsid()
- 改变当前工作路径
chdir
(非必须) 适用场景: 插入U盘,在U盘中启动可执行程序。程序运行过程中U盘被拔掉了 - 重设文件掩码 WHY?(非必须)
- 子进程会继承父进程的掩码
- 增加子进程的灵活性
- 调用
umask()
- 关闭文件描述符(非必须)
- 执行核心工作(必须)
下面是创建一个守护进程,每2s获取一次系统时间,将其写入到磁盘文件中
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <signal.h>
void dowork(int no)
{
time_t curtime;
// 得到系统时间
time(&curtime);
// 格式化时间
char *pt = ctime(&curtime);
// 将时间写入文件
int fd = open("/home/he/he.log", O_CREAT | O_WRONLY | O_APPEND, 0664);
write(fd, pt, strlen(pt) + 1);
close(fd);
}
int main(int argc, const char* argv[])
{
pid_t pid = fork();
if(pid > 0)
{
// 父进程退出
exit(1);
}
else if(pid == 0)
{
// 创建会话成为该会话进程组的组长
setsid();
// 改变进程工作目录
chdir("/home/he");
// 重置文件掩码
umask(0);
// 关闭文件描述符,系统前三个自带的
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
// 执行核心操作
// 注册信号捕捉
struct sigaction act;
act.sa_flags = 0;
act.sa_handler = dowork;
sigemptyset(&act.sa_mask);
sigaction(SIGALRM, &act, NULL);
// 创建定时器
struct itimerval val;
// 第一次触发的时间
val.it_value.tv_usec = 0;
val.it_value.tv_sec = 2;
// 循环周期
val.it_value.tv_usec = 0;
val.it_value.tv_sec = 1;
// 保证子进程处于运行状态
while(1);
}
return 0;
}