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

文章目录

  • [1. 补充](#1. 补充)
    • [1.1 查看](#1.1 查看)
    • [1.2 控制进程组的方式](#1.2 控制进程组的方式)
  • [2. 创建守护进程](#2. 创建守护进程)
    • [step1. 忽略信号](#step1. 忽略信号)
    • [step2. 让自己不是组长](#step2. 让自己不是组长)
    • [step3. setsid 函数:给调用函数设置新的会话和进程组 ID](#step3. setsid 函数:给调用函数设置新的会话和进程组 ID)
    • [step4. chdir 函数:可以改变守护进程的工作路径](#step4. chdir 函数:可以改变守护进程的工作路径)
    • [step5. 处理文件描述符 0、1、2](#step5. 处理文件描述符 0、1、2)
  • 守护进程类样例

1. 补充

1.1 查看

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

bash 复制代码
ps axj | head -1 && ps axj | grep sleep | grep -v grep

: 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);
// ...

step2. 让自己不是组长

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

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

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

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

cpp 复制代码
#include <unistd.h>

pid_t setsid(void); 

返回值:

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

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

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

非必要步骤

cpp 复制代码
#include <unistd.h>

int chdir(const char *path);

返回值:

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

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

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

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

守护进程类样例

cpp 复制代码
#include <cstdlib>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

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);
}

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


相关推荐
想做小南娘,发现自己是女生喵12 分钟前
第二周学习文档 Linux C 文件操作
linux·c语言·学习
元岳数字人小元13 分钟前
一台终端兼顾接待与咨询,AI数字人对话赋能线下多场景服务
运维·人工智能·人机交互·交互·语音识别·源代码管理
djjjx.20 分钟前
Linux进程篇(三):深度硬核!全面起底 Linux 进程状态变化与内核链表动态解绑
linux·运维·服务器
乔旭伟23 分钟前
Linux 服务器环境搭建实战笔记:最小化安装、Docker Compose 编排与上线检查全流程
linux·服务器·笔记
燕落南枝33 分钟前
HTML 学习:input 单选、复选与文件域详解
服务器·学习·html
Chenyu_3109 小时前
DPDK、TC 和 XDP:Linux 高性能网络到底在优化什么
linux·服务器·网络·缓存
qqxiaoxingxing9 小时前
HR重复性工作自动化落地指南:先梳理任务边界,再选工具
运维·自动化
王琦031810 小时前
持续集成持续交付企业示例
运维·ci/cd
Lsetea10 小时前
SSL证书指纹相同但文件哈希不同:PEM、DER与公钥摘要怎么比
运维·https·ssl证书·openssl·证书链
wtblszn10 小时前
废气处理余热回收设备远程运维系统方案
运维