先给工具
源码和工具都在这里自取
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接管,咱们的守护过程创立胜利