进程守护进程
2021-04-01 本文已影响0人
wayyyy
什么是守护进程?
Linux 的大多数服务器就是用守护进程实现的,使用ps -axj可以查看守护进程:
image.png
- 守护进程基本上都是以超级用户启动( UID 为 0 )
- 没有控制终端( TTY 为 ?)
- 终端进程组 ID 为 -1 ( TPGID 表示终端进程组 ID)
如何编写守护进程?
设置umask
Linux 不同,它是通过使用 umask 默认权限来给所有新建的文件和目录赋予初始权限的。
# umask # root用户默认是0022,普通用户默认是 0002
0022
这里先调用umask将文件模式创建屏蔽之设置为一个已知的值,通常是0。
umask(0);
在后台运行
//1)在父进程中,fork返回新创建子进程的进程ID;
//2)在子进程中,fork返回0;
//3)如果出现错误,fork返回一个负值;
int pid = fork();
if (pid < 0)
{
std::cout << "fork error" << std::endl;
exit(-1);
}
//父进程退出,子进程独立运行
else if (pid > 0)
{
exit(0);
}
调用fork,然后使父进程exit,这样做的目的是:
1、如果该进程是作为一条简单的 shell 命令启动的,那么父进程终止会让 shell 认为这条命令已经执行完毕。
2、虽然子进程继承了父进程的进程组ID,但获得了一个新的进程ID,这样就保证了 子进程不是一个进程组的组长进程,这是下面将要进行的setsid调用的先决条件。
脱离控制终端,登录会话和进程组
setsid();
signal(SIG_HUP, SIG_IGN);
if ((pid = fork()) < 0)
{
std::cout << "fork error" << std::endl;
exit(-1);
}
else if (pid != 0)
{
exit(0);
}
关于setsid()
pid_t setsid(); // 成功返回进程组ID,出错返回-1
image.png
如果调用此函数的进程不是一个进程组的组长,则此函数创建一个新会话,具体会发生下列3件事:
1、该进程变成新会话的会话首进程
2、成为一个新进程组的组长进程,组ID就是该调用进程的ID
3、该进程没有控制终端
如果该调用进程已经是一个进程组的组长进程,则函数返回出错,为了便面这种情况,通常先调用fork,然后使其父进程终止,子进程继续。
改变当前的工作目录
更改工作目录,从父进程继承过来的当前工作目录可能在 一个挂载的文件系统中,如果守护进程的当前目录挂载在一个文件系统中,那么该文件系统就不能被卸载。
chdir("/");
关闭打开的文件描述符
int fd = open("/dev/null", O_RDWR, 0);
if (fd != -1)
{
dup2(fd, STDIN_FILENO);
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
}
if (fd > 2)
{
close(fd);
}
处理 SIGCHLD 信号
signal(SIGCHLD, SIG_IGN);
完整实现
#include <iostream>
#include <sys/types.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
void daemon_run()
{
pid_t pid = fork();
if (pid < 0)
{
std::cout << "fork error" << std::endl;
exit(-1);
}
else if (pid > 0)
{
exit(0);
}
pid_t group = setsid();
if (group < 0)
{
std::cout << "setsid error" << std::endl;
exit(-1);
}
if ((pid = fork()) < 0)
{
std::cout << "fork error" << std::endl;
exit(-1);
}
else if (pid != 0)
{
exit(0);
}
int fd = open("/dev/null", O_RDWR, 0);
if (fd != -1)
{
dup2(fd, STDIN_FILENO);
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
}
if (fd > 2)
{
close(fd);
}
signal(SIGHUP, SIG_IGN);
signal(SIGCHLD, SIG_IGN);
}
int main(int argc, char **argv)
{
int ch;
bool b_daemon = false;
while ((ch = getopt(argc, argv, "d")) != -1)
{
switch (ch)
{
case 'd':
b_daemon = true;
break;
}
}
if (b_daemon)
{
daemon_run();
}
while (1)
{
}
}
参考资料
1、《UNIX环境高级编程》
2、https://blog.csdn.net/lianghe_work/article/details/47659889