先给工具
源码和工具都在这里自取
https://github.com/tangbu/myblob/tree/master/c/deamon
这是一个 c 编译而成的可运行程序,名字叫 deamon,比方本来执行 /bin/test -Darg1=value1, 当初执行 deamon /bin/test -Darg1=value1 就能够
原理浅显易懂,java 程序员不影响浏览
产生了什么事件
笔者的电脑操作系统为 ubuntu,数年以前在兴高采烈的关上 postman 的时候调试,忽然想整顿多余的 bash 命令界面,忽然正在工作的 postman 退出了工作,留下我默默发愣。通过测试,应用 postman 的时候必须放弃 bash 界面关上状态,也就是我要始终面对这个黑黑的窗口无奈关掉
寻找解决办法
解决办法 1 nohup,
网上搜寻 nohup 能够让过程在后盾运行,会在当前目录下生成 nohup.out, 输入会重定向增加到 nohup.out 外面去,然而并不胜利
解决办法 2 & 符号和 -d
命令前面加 & 符号和 -d,postman -d &, 也不胜利页面卡死
解决办法 3 disown
应用 disown 命令,执行 postman 的时候界面不给输出命令的机会,导致无奈执行 disown 命令
postman 退出起因
执行 man bash 命令看一下为什么会导致 postman 退出
shell 退出的时候会向 shell 启动的 jobs 发送 SIGHUP 信号,过程在接管到 SIGHUP 的时候,如果过程没有向操作系统注册信号处理函数的时候 ProcessId,操作系统就会敞开该过程
守护过程
守护过程 在 linux 操作系统中, 守护过程 (deamon) 是作为后盾过程运行的过程,对于在 bash 启动的工作(job), 如果间接退出 job 同样会失落。为了解决这个问题,须要应用守护过程的形式启动工作。
接下来咱们看一下如何把命令行运行的程序编程守护过程
守护过程的创立
依然以命令行间接运行 postman 为例,执行命令查看过程树结构
ps -ajx | egrep 'postman|bash|PPID'
能够看到,终端过程和 postman 命令过程在同一会话 ID 中,postman 的父过程为终端。
从终端创立一个过程 A,A 过程再 fork 一个子过程 B 执行 postman 命令, 而后 A 过程退出
这样就能够创立运行 postman 命令的过程,实质上和间接在终端运行 postman 命令过程成果一样,然而咱们能够通过 c 代码对 B 过程属性更改让最终的过程继承更改后的个性
在子过程中创立新会话
从终端创立一个过程 A,A 过程再 fork 一个子过程 B,这时候,子过程的 SID 会话 id 和终端依然是一个,当终端退出的时候,依然会发送 SIGHUP 信号,子过程默认依然会退出,所以子过程 B 依然须要新建一个会话,就用到了上面的函数
setsid() 函数
使子过程齐全独立进去,脱离终端管制,能够 man 3 setsid 看一下应用
扭转当前目录为根目录
chidr()函数
避免工作在可卸载的文件系统,文件系统的卸载后,过程也随之沦亡,所以个别更改到根目录下
重设文件权限掩码
umask()函数
本机的 umask 的是 0002 第一个 0 不思考
所以在代码中创立 777,002 代表写,这个 umask 创立的文件默认去掉写权限(位运算)775, 防止影响到守护过程
umask(0);
敞开文件描述符
重定向 dup2()
规范输入输出、谬误都给丢掉。而后把继承下来的关上的文件描述符关掉,防止占用资源
理论运行的源码
上面是附正文的 C 代码
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char* argv[])
{
//pid_t int 类型, a 代表子过程号
pid_t a;
// 创立子过程,父过程中 a 是子过程号,子过程中 a =0
a = fork();
if(a > 0)
{
// 父过程退出,子过程继承父过程代码块持续运行,a 就是子过程号
exit(0);
}
// 子过程疏忽终端退出 SIGHUP 信号
signal(SIGHUP, SIG_IGN);
// 创立新会话,脱离原会话,脱离管制终端。setsid();
// 脱离原过程组,创立并进入只蕴含本身的过程组
setpgrp();
// 敞开父辈继承下来的所有文件
int max_fd = sysconf(_SC_OPEN_MAX);
for (int i = 0; i < max_fd && i > 2; i++)
{close(i);
}
// 设置掩码位
umask(0);
int drop_fd=open("/dev/null", O_RDWR);
dup2(0, drop_fd);
dup2(1, drop_fd);
dup2(2, drop_fd);
// 切换工作门路
chdir("/");
// deamon postman arg1 args 变成了 postman arg1 arg2
if (argc > 2) {char *params[argc - 2];
memcpy(params, argv + 1, sizeof params);
params[argc - 3] = NULL;
// execvp 反对在环境变量 PATH 中寻找 postman 命令并替换代码段,运行新过程的代码段,execvp(*params, params);
} else {execvp(argv[1], NULL);
}
return 0;
}
接下来执行 deamon postman 命令查看过程关系
能够看到 postman 的父过程曾经变成了 systemd,比拟新的零碎守护过程曾经由 systemd 接管,咱们的守护过程创立胜利