守护进程的特点:
- 常驻内存
- 会话leader
会话是一堆进程组,新会话的第一个进程即是会话leader,又是该会话中第一个进程组的组长 - 进程组组长
- 无控制终端
一个会话能绑定一个控制终端,由leader控制。
会话leader成为控制进程,控制该控制终端。
前台进程 和 后台进程
在控制终端中使用快捷键来产生信号会给 该信号的默认目标进程 发过去:
- 中断SIGINT(ctl+c)、退出SIGQUIT(ctrl+\)、挂起SIGSTP(ctl+z)信号只会发给终端的前台进程组;(挂起的意思是,让一个前台进程切到后台去并暂停运行;配合bg id 可以让这个进程在后台运行)
- 断开信号SIGHUP(终端断开)发给终端的控制进程(会话leader)
约定俗成:SIGHUP用于通知(守护)进程重读配置,程序自己实现handler。 SIGHUP的默认行为:进程会退出。
#include <unistd.h>
#include <iostream>
#include <fstream>
#include <fcntl.h>
std::ofstream fs;
void handler(int sig) //信号处理函数参数为int ,返回值为void
{
fs << "exit!!!" << std::endl;
exit(0);
}
void deamonize()
{
pid_t pid;
int fd;
if((pid = fork())<0){
std::cerr << "fork failed!" << std::endl;
}
else if(pid != 0){
exit(0);
}
//子进程
//创建一个新的会话,新会话的创建进程自然是会话组长,
//也是进程组组长,同时也没有控制终端
setsid();//此函数调完就已经是守护进程了
if((fd = open("/dev/null", O_RDWR)) != -1){//用任意文件描述符打开一个Null设备
dup2(fd, 0);//把刚才的fd复制给0
dup2(fd, 1);
dup2(fd, 2);
for(int i = 3; i <= fd; ++i){
close(i);//没用的文件描述符全关闭
}
}
}
int main(int argc, char **argv)
{
deamonize();
signal(SIGINT, handler); //处理中断
fs.open("log.txt", std::ofstream::out | std::ofstream::app);
for(;;){
fs << "hello" << std::endl;
sleep(5);
}
return 0;
}