• 【Linux】进程篇(补):守护进程



    1. 补充

    1.1 查看

    为了观察进程,我们以在命令提示符处,运行 sleep 命令为例。

    ps axj | head -1 && ps axj | grep sleep | grep -v grep
    
    • 1
    得到的信息:
    PPID:父进程 ID
    PID:进程 ID
    PGID:进程组
    SID:会话 ID
    TTY:进程关联的终端

    每登录一次,都是一个新的会话,即每个会话关联一个终端文件,进程组的名称是进程组中第一个进程的 PID。

    1. 进程组,分为前台任务和后台任务
    2. 在会话中,只能有一个前台任务在运行
      (解释了我们在命令行启动一个进程的时候,bash 就无法工作了的原因)
    3. 每次登录就是创建一个新的会话、bash 任务;启动进程,就是在当前会话中创建一个后台任务;退出会话,会影响会话内部的所有任务
    4. 一般网络服务器,为了不受到用户的登陆注销的影响,网络服务器会以 守护进程 的方式运行!

    1.2 控制进程组的方式

    jobs:查看自己会话中后台运行的进程
    fg [任务号]:将相应进程提到前台
    ctrl + Z:将前台运行的进程暂停,并放入后台
    bg [任务号]:运行后台暂停的进程


    2. 创建守护进程

    为了不受用户影响,网络服务器会将其进程单独拎出来,使用新的会话和进程组,为此称守护进程

    step1. 忽略信号

    signal(SIGPIPE, SIG_IGN);
    signal(SIGCHLD, SIG_IGN);
    // ...
    
    • 1
    • 2
    • 3

    step2. 让自己不是组长

    要设置新的会话和进程组 ID,需要使用 setsid 接口,而每个进程组的组长(进程组号同自己 PID 的进程)是不能舍自己进程组不顾的,即使用 setsid 创建新组,必须不能是组长。

    if (fork() > 0) exit(0);
    
    • 1
    • fork出多进程,让父进程退掉,子进程继续跑,就相当于让出了组长。

    • 本质上,守护进程就是 孤儿进程 的一种

    step3. setsid 函数:给调用函数设置新的会话和进程组 ID

    #include 
    
    pid_t setsid(void); 
    
    • 1
    • 2
    • 3

    返回值:

    • 成功返回新的进程组 ID,失败返回 -1,并设置错误码

    注意:组长是不能使用该接口的

    step4. chdir 函数:可以改变守护进程的工作路径

    非必要步骤

    #include 
    
    int chdir(const char *path);
    
    • 1
    • 2
    • 3

    返回值:

    • 成功返回 0,失败返回 -1,并设置错误码

    step5. 处理文件描述符 0、1、2

    这里的处理是将这些文件重新向到 /dev/null 中,目的是切断新会话和键盘等的联系。

    这里的 /dev/null 是一个字符设备,传进的数据都会被直接丢弃。

    守护进程类样例

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    void Daemon()
    {
        // 1. 忽略信号
        signal(SIGPIPE, SIG_IGN);
        signal(SIGCHLD, SIG_IGN);
        
        // 2. 让自己不要成为组长
        if (fork() > 0)
            exit(0);
            
        // 3. 新建会话,自己成为会话的话首进程
        pid_t ret = setsid();
        if ((int)ret == -1)
        {
            // 日志或打印
            exit(1);
        }
        
        // 4. 可选:可以更改守护进程的工作路径
        // chdir("/")
        
        // 5. 处理后续的对于0,1,2的问题
        int fd = open("/dev/null", O_RDWR);
        if (fd < 0)
        {
            // 日志或打印
            exit(2);
        }
        dup2(fd, 0);
        dup2(fd, 1);
        dup2(fd, 2);
        close(fd);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    🥰如果本文对你有些帮助,请给个赞或收藏,你的支持是对作者大大莫大的鼓励!!(✿◡‿◡) 欢迎评论留言~~


  • 相关阅读:
    Linux 基本语句_4_指针和函数
    centos服务器搭建安装Gitlab教程使用教程
    如何用idm下载迅雷文件 idm怎么安装到浏览器 idm怎么设置中文
    [手写系列] 带你实现一个简单的Promise
    智能矩阵系统解决的问题?
    Camtasia2023免费升级最新版本下载
    如何实现一个分布式锁
    李宏毅《机器学习》丨7. Conclusion(总结)
    【C++】继承
    C#语法糖系列 —— 第二篇:聊聊 ref,in 修饰符底层玩法
  • 原文地址:https://blog.csdn.net/m0_67470729/article/details/132767833