守护进程(精灵进程)

        守护进程(Daemon) 也称为精灵进程,是运行在后台的一种特殊进程,通常运行在操作系统启动时就开始运行,并一直在后台运行,直到系统关闭或它被停止为止(普通进程都是在用户登录或运行程序时创建,在运行结束或用户注销时终止,但守护进程不受用户登录注销的影响,它们将会一直运行着、直到系统关机)。

        守护进程通常不与用户界面交互,其主要任务是提供某种服务或执行某些常规任务(系统与用户交互的界面称为终端,每一个从终端开始运行的进程都会依附于这个终端,当控制终端被关闭的时候,由控制终端运行的所有进程都会被终止, 这使得普通进程都是和运行该进程的终端相绑定的; 但守护进程能突破这种限制,它脱离终端并且在后台运行)。

        特点:长期运行、与控制终端脱离

        作用:通常对服务器应用程序特别有用,而且降低了资源占用和干扰用户输入输出操作的风险。通过监管其自身运行状态,系统可以不间断地进行自我管理,从而确保系统的稳定性和可靠性

编写守护进程程序:详细解释都在代码里面注释

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>

int main(void)
{
    pid_t pid;
    int i;
    long max;

    /*创建子进程、终止父进程*/
    pid = fork();
    if(0>pid)/*创建失败*/
    {
        perror("fork error");
        exit(-1);
    }
    else if (0<pid) /*父进程直接退出*/
        exit(0);
    /*下面的操作就是子进程的了*/

/*第一步:子进程调用 setsid 创建会话
     *子进程继承了父进程的会话、进程组、控制终端等,虽然父进程退出了
     *但原先的会话期、进程组、控制终端等并没有改变,那还不是真正意义上使两者独立开来
     *由于子进程并不是进程组的组长进程,所以调用 setsid()会使得子进程创建一个新的会话
     *子进程成为新会话的首领进程,同样也创建了新的进程组、子进程成为组长进程
     *此时创建的会话将没有控制终端
     */

    /*调用 setsid 作用:
     *调用该函数的进程设置为该会话的领头进程,让子进程摆脱原会话的控制,不再与前一个会话有任何联系。
     *新的会话ID和进程组ID都会被设置为调用进程的进程ID,让子进程摆脱原进程组的控制和摆脱原控制终端的控制
     *setsid 函数的原型:pid_t setsid(void);
     *如果调用 setsid 函数的进程是一个进程组的组长,那么该函数会失败并返回 -1
     */
     if (0>setsid())
     {
        perror("setsid error");
        exit(-1);
     }
    /*这时候子进程已经脱离了原来的控制终端和进程组,成为了一个独立的进程*/

/*第二步:将工作目录更改为根目录
     *子进程是继承了父进程的当前工作目录
     *由于在进程运行中,当前目录所在的文件系统是不能卸载的,这对以后使用会造成很多的麻烦
     *因此通常的做法是让“/”作为守护进程的当前目录
     *chdir函数原型:int chdir(const char *path);
     *用于改变当前工作目录,即将当前工作目录切换到指定路径 path。如果成功,则返回 0;否则返回 -1 
     */
    if (0>chdir("/"))
    {
        perror("chdir error");
        exit(-1);
    }
/*第三步:重设文件权限掩码 umask
     *子进程继承了父进程的文件权限掩码,这就给子进程使用文件带来了诸多的麻烦
     *因此,把文件权限掩码设置为 0, 将所有的权限位都打开,确保子进程有最大操作权限,这样大大增强该守护进程的灵活性
     *umask函数原型:mode_t umask(mode_t mask);
     *掩码的每一位对应着某个文件操作权限(如读、写、执行),如果对应位为 1,表示该权限禁止;为 0 则表示允许
     */
     umask(0);
/*第四步:关闭不再需要的文件描述符
    *子进程继承了父进程的所有文件描述符, 这些被打开的文件可能永远不会被守护进程读或写
    *但它们一样消耗系统资源,可能导致所在的文件系统无法卸载,所以必须关闭这些文件
    *sysconf函数原型:long sysconf(int name);
    *_SC_CHILD_MAX: 进程可以拥有的最大子进程数
    *函数返回指定系统参数的值。如果查询失败,则返回 -1
    */
    max=sysconf(_SC_OPEN_MAX);
    /*获取系统支持的最大文件描述符数量,然后通过循环调用关闭所有已经打开的文件描述符*/
    for (i = 0; i < max; i++)
        close(i);
/*第五步:将文件描述符号为 0、 1、 2 定位到/dev/null
    *将守护进程的标准输入、标准输出以及标准错误重定向到/dev/null
    *使得守护进程的输出无处显示、也无处从交互式用户那里接收输入
    *dup函数原型:int dup(int oldfd);
    *oldfd:需要被复制的文件描述符
    *常见用途之一就是将一个文件描述符复制成标准输入、标准输出或标准错误输出的文件描述符
    */
    open("/dev/null", O_RDWR);
    dup(0);
    dup(0);
/*第六步:忽略 SIGCHLD 信号主要是处理进程回收,处理 SIGCHLD 信号不是必须的, 但对于某些进程,特别是并发服务器进程往往是特别重要的
     *让内核将僵尸进程转交给 init 进程去处理,这样既不会产生僵尸进程、又省去了服务器进程回收子进程所占用的时间
     *函数原型:void (*signal(int signum, void (*handler)(int)))(int);
     *SIGCHLD:代表子进程结束或停止时向父进程发送的信号
     *SIG_IGN:表示忽略指定的信号
     */
     /* 表示将 SIGCHLD 信号的处理方式设置为忽略,即当子进程结束时,系统会自动清除僵尸进程*/
     signal(SIGCHLD, SIG_IGN);

     /*正式进入到守护进程,下面就是守护进程工作内容*/
     for (;;) {
        sleep(1);
        puts("守护进程运行中......");
     }
     exit(0);
}

         运行之后,没有任何打印信息输出,原因在于守护进程已经脱离了控制终端,它的打印信息并不会输出显示到终端,在代码中已经将标准输入、输出以及错误重定位到了/dev/null, /dev/null 是一个黑洞文件,自然是看不到输出信息。

        查看一下进程

守护进程 Daemon,通常简称为 d,一般进程名后面带有 d 就表示它是一个守护进程;一栏是问号?表示该进程没有控制终端,也就是守护进程

猜你喜欢

转载自blog.csdn.net/weixin_46829095/article/details/130786917