• 实现守护进程


    守护进程

    守护进程:也称为精灵进程,守护进程是一个在后台运行并且不受任何终端控制的进程。Unix操作系统有很多典型的守护进程(其数目根据需要或20—50不等),它们在后台运行,执行不同的管理任务。用户使守护进程独立于所有终端是因为,在守护进程从一个终端启动的情况下,这同一个终端可能被其他的用户使用。

    有关命令

    ps命令:Linux中的ps命令是Process Status的缩写。ps命令用来列出系统中当前运行的那些进程。ps命令列出的是当前那些进程的快照,就是执行ps命令的那个时刻的那些进程,如果想要动态的显示进程信息,就可以使用top命令。
    ps [options] [--help]

    例子:
    ps -ef显示所有进程的完整信息
    -e:显示所有进程
    -f:显示完整格式的进程信息
    -o:用户自定义格式

    grep命令:Linux系统中grep命令是一种强大的文本搜索工具,它能使用正则表达式搜索文本,并把匹 配的行打印出来。grep全称是Global Regular Expression Print,表示全局正则表达式版本,它的使用权限是所有用户。

    普通进程:普通进程在终端运行时,输入命令不会有效果
    在运行程序时,在末尾加上&,或者在运行是按ctrl+z都可以使程序到后台运行
    (1.将程序在后台运行,2.将程序暂停)
    但是如果终端关闭,所以程序都将终止

    守护进程:守护进程却能够突破这种限制,它从被执行开始运转,直到接收到某种信号或者整个系统关闭时才会退出。如果想让某个进程不因为用户、终端或者其他的变化而受到影响,那么就必须把这个进程变成一个守护进程。

    如何创建守护进程

    1. 创建子进程,退出父进程

    为了脱离控制终端需要退出父进程,之后的工作都由子进程完成。在Linux中父进程先于子进程退出
    会造成子进程成为孤儿进程,而每当系统发现一个孤儿进程时,就会自动由1号进程(init)收养它,
    这样,原先的子进程就会变成init进程的子进程。

    1. 在子进程中创建新的会话
      pid_t setsid(void);用于创建一个新的会话,并担任该会话组的组长。
    • 让进程摆脱原会话的控制;
    • 让进程摆脱原进程组的控制;
    • 让进程摆脱原控制终端的控制;
    1. 改变当前目录为根目录

    使用fork创建的子进程继承了父进程的当前的工作目录。由于在进程运行中,当前目录所在的文件系统是不能卸载的,这对以后的使用会造成诸多的麻烦。因此,通常的做法是让根目录”/”作为守护进程的当前工作目录。这样就可以避免上述的问题。如有特殊的需求,也可以把当前工作目录换成其他的路径。改变工作目录的方法是使用chdir函数。

    1. 重设文件权限掩码

    由于fork函数创建的子进程继承了父进程的文件权限掩码,这就给子进程使用文件带来了诸多的麻烦。因此,把文件权限掩码设置为0(即,不屏蔽任何权限),可以增强该守护进程的灵活性。设置文件权限掩码的函数是umask。通常的使用方法为umask(0)。文件权限掩码:是指屏蔽掉文件权限中的对应位。

    1. 关闭文件描述符

    用fork创建的子进程也会从父进程那里继承一些已经打开了的文件。在使用setsid调用之后,守护进程已经与所属的控制终端失去了联系,因此从终端输入的字符不可能达到守护进程,守护进程中用常规方法(如printf)输出的字符也不可能在终端上显示出来。所以,文件描述符为0、1、2(即,标准输入、标准输出、标准错误输出)的三个文件已经失去了存在的价值,也应该关闭。

    文件描述符

    当你打开一个存在的文件或者创建一个新文件,操作系统都会返回这个文件描述符(其实就是代表这个文件的),后续对这个文件的操作的一些函数,都会用到这个文件描述符作为参数;
    linux中三个特殊的文件描述符,数字分别为0,1,2

    • 0:标准输入【键盘】,对应的符号常量叫STDIN_FILENO
    • 1:标准输出【屏幕】,对应的符号常量叫STDOUT_FILENO
    • 2:标准错误【屏幕】,对应的符号常量叫STDERR_FILENO

    代码实现

    ```c
    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<fcntl.h>
    #include<sys/types.h>
    #include<sys/stat.h>
    int main(){
        pid_t pid;
    
        //1. 创建子进程,退出父进程
        pid = fork();
        if(pid<0){
            printf("fork errno
    ");
        }
        else if(pid>0){
            exit(0);
        }
    
        //2. 在子进程中创建新的会话
        setsid();
    
        //3. 改变当前目录为根目录
        chdir("/");
    
        //4. 重设文件权限掩码
        umask(0);
    
        //5. 关闭文件描述符
        close(0);
        int fd0;
        fd0=open("dev/null",O_RDWR);
        dup2(fd0,1);
        dup2(fd0,2);
    
        while(true){
            sleep(1);
        }
        return 0;
    }
    ```
    

    运行程序后,使用命令查看系统进程ps -eo pid,ppid,sid,tty,pgrp,comm,stat,cmd | grep -E 'bash|PID|mydaemon'

      PID  PPID   SID TT        PGRP COMMAND         STAT CMD
    23000     1 23000 ?        23000 mydaemon        Ss   ./mydaemon
    

    PPID父进程ID为1,说明fork子进程后,父进程退出,子进程被1号进程收养,TT=?说明已和终端脱离

  • 相关阅读:
    20210312
    20210311
    20210310
    例5-1
    例5-2
    例4-12-2
    例4-12
    例4-11
    例4-10
    例4-9
  • 原文地址:https://www.cnblogs.com/xcantaloupe/p/11118752.html
Copyright © 2020-2023  润新知