【Linux网络编程】守护进程

目录

一、前后台进程:

1、前台进程:

2、后台进程:

3、session:

二、前后台进程管理命令:

1、启动后台进程:

2、前台与后台进程切换

3、查看后台进程

4、终止后台进程

三、守护进程:

1、前置知识:

2、什么是守护进程:

3、编写守护进程:


一、前后台进程:

1、前台进程:

定义:直接与当前终端(Shell)交互的进程,用户输入的命令默认以前台进程运行

理解

  • 占用终端控制权,用户必须等待其执行完毕或手动暂停,才能在终端中执行其他命令
  • 终端的输入 / 输出(STDIN/STDOUT/STDERR)直接与进程关联
  • 前台进程只能有一个

当我们在xshell上远程登录主机的时候,就会有一个bash这样的进程,这个进程就是前台进程,前台进程是能够接收键盘的消息的,也就是当bash为前台进程的时候才能够解释指令,只有前台进程才能够被ctrl+c杀死(bash不能够被ctrl+c的原因是在实现的时候进行检查了,发现是ctrl+c就不执行指令)

当我们正常启动一个进程的时候,就是启动的前台进程,这个时候由于只能有一个前台进程,所以bash进程就变成后台进程了,此时也就对应着我们的现象 --- 当我们启动前台进程的时候,不能够让bash进程解释我们的指令了,这个时候就能够使用ctrl+c向前台进程发信号,终止前台进程了,此时bash就会顶上来,重新成为前台进程

2、后台进程:

定义:在终端后台运行的进程,不占用终端控制权,用户可继续在终端中操作

理解

  • 不与终端交互,终端关闭可能影响其运行(除非特殊处理,如守护进程)
  • 通常用于执行耗时任务(如文件拷贝、编译程序)或服务程序(如 Web 服务器)
  • 后台进程可以有多个

当bash成为后台进程的时候,通过键盘输入指令就是输入给前台进程的,这也就是当正在执行一个进程的时候,bash为什么不能够解释指令的原因

3、session:

因为前后台进程都能够向显示器中打印消息,所以显示器并不是区分前后台的指标

那么谁才是呢?

谁拥有键盘文件就是前台进程,也就是后台进程不能够执行标准输入,电脑只有一个键盘,所以前台进程也就只有一个

哪一个进程是前台进程本质是谁拥有键盘文件

如果把某个前台进程暂停了,bash就需要从后台进程转移到前台进程,不然当前会话就无法读取键盘数据了,所以在命令行中,前台进程必须都要存在,并且只有一个

我们可以启动多个会话,那么OS就需要将session管理起来,怎么管理 --- 先描述在组织,所以每一个session都需要有它自己的结构体来存储其状态和属性

二、前后台进程管理命令:

1、启动后台进程:

启动后台进程就是在启动前台进程的基础上,在后面加上 &即可

像上述那样,出现**[1] 3914129**那样就是证明该进程后台启动成功

并且此时能够执行命令行指令,因为此时前台进程还是bash,能够提供指令解释

2、前台与后台进程切换

后台进程变为前台进程

命令:fg [job_id]

其中的job_id就是在启动后台进程时,显示的[ ]里面的数字

示例:

像上述那样使用fg指令就能够将后台进程变为前台进程,此时就能够使用ctrl+c终止进程了

将前台进程暂停并变成后台进程

命令:ctrl+z

将前台进程暂停并变成后台进程,此时又必须有一个前台进程,那么bash就顶上来了

将后台暂停进程恢复为后台运行

命令:bg [job_id]

3、查看后台进程

当我们启动多个后台进程并且暂停多个的时候,能够知道有几个后台进程的

命令:jobs

  • 1\]:作业编号(Job ID)

  • +默认作业(下次fg,bg操作的目标)
  • -次默认作业

如上,当我们启动三个后台进程后,bash依然是前台进程,能用指令jobs查看当前后台进程有几个

当我们启动前台进程后,将其暂停后成为后台进程,此时用jobs仍然能够看到,用bg是能够启动的

4、终止后台进程

方法 1:通过作业编号终止

  • 命令:kill %job_id
  • 示例:kill %1(终止作业 1)

方法 2:通过进程 ID(PID)终止

  • 命令:kill pid
  • 示例:kill 1234(终止 PID 为 1234 的进程)

三、守护进程:

1、前置知识:

首先了解上述:以下四个是重要的:

  • PPID:父进程的 PID(进程 ID)
  • PID:进程的唯一标识符(系统内唯一)
  • PGID:进程所属的进程组ID(默认等于进程的 PID,若进程创建新进程组则可能变化)
  • SID:进程所属的会话 ID(也就是session id)

拓展:

TTY:进程关联的终端设备

TPGID:前台进程组的ID

STAT:进程的状态码

UID:进程所属用户的ID

TIME:进程累计占用的CPU时间

COMMAND:启动进程的命令行

接下来谈谈PGID:

这是进程组的ID

如上,首先启动process.exe后台启动,然后启动sleep,这样的指令启动三个

注意看他们的PID和PGID,我们发现只有一个进程的进程组的组长就是自己

sleep那样三个进程为一个组,组长就是第一个进程

并且可以看到SID都是一样的,证明这些进程是在一个终端被启动的

接下来看看会话session:

bash 复制代码
ps ajx | head -1 && ps ajx | grep -E '\-bash$'

当前会话也是一个进程,可以看到一个会话对应着一个-bash进程,前面的-表示远端登录

接下来增加会话:

依次增加session,就能够增加bash进程的个数,有这么多的session会话需要被OS管理起来 --- 先描述在组织,需要创建session结构体,然后里面的sessionid就是表示哪一个session

倘若当前会话退出了,会话里面创建的进程也会退出的:

如上左边终端里面有着如下的4个进程

然后我们把左边终端关掉,继续查看就看不到启动的4个进程了

2、什么是守护进程:

像上述那样,红色框框的进程从上面的会话自成一个新会话,从包含关系变成并且关系,这样无论之前的会话怎么样,和新成的会话无关,这样就成为了守护进程

而守护进程在后台独立运行,不受终端关闭的影响,也不向终端输出交互信息

特性 普通进程 守护进程
终端关联 依赖终端,终端关闭可能终止 无终端关联,独立运行
交互性 可接收用户输入 / 输出信息 不与用户直接交互
生命周期 任务完成或主动退出即终止 随系统启动,长期运行
启动方式 由用户主动启动(如命令行) 系统自动启动(如systemd

3、编写守护进程:

需要用到如下接口:

当调用后的返回值就是新会话的ID,也就是SID

调用这个函数的进程不能够是进程组的组长,但是如果只有一个进程,这个进程自成进程组,那么这个进程也就是自己进程组的组长,也就是说,自成进程组的进程是不能够直接调用上述接口成为新会话的,那么就需要在代码中fork创建子进程,让自己直接终止,让子进程去调用setsid,进而让子进程成为新会话,成为守护进程

所以守护进程的本质是孤儿进程

那么我们自己写守护进程的思路:

1、忽略其他信号:让守护进程不受终端信号干扰,保持稳定运行,因为守护进程脱离终端后,不需要响应终端相关信号,否则可能导致进程意外终止

2、将自己变为独立会话:脱离原终端控制,成为新会话的组长,因为会话是进程组的集合,一个会话通常与一个终端关联,通过setsid系统调用创建新会话后,守护进程成为会话组长,不再受原终端的控制(如终端关闭不会影响进程),这样调用这个Daemon后就能够让父进程直接变为子进程

3、更改当前进程的工作目录,避免依赖启动时的工作目录,确保进程稳定运行,因为守护进程启动时的工作目录可能是用户目录或临时目录,若该目录被卸载或删除,会导致进程无法访问资源,通常将工作目录设置为根目录(/)或特定系统目录,确保目录始终存在

4、将标准输出, 标准错误进行重定向到dev/null,垃圾文件,为了切断与终端的输入输出联系,避免无意义的输出或错误,因为守护进程没有终端,若保留标准输入输出,任何读写操作都会导致错误,至于dev/null,这是Linux中的"垃圾桶",写入其中的内容会被丢弃,读取会立即返回 EOF,确保进程不会因 I/O 操作受阻

cpp 复制代码
#pragma once
#include <iostream>
#include <cstdlib>
#include <unistd.h>
#include <signal.h>
#include <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
using namespace std;
 
const string nullfile = "dev/null";
 
void Daemon(const string& cwd = "")
{
    //忽略其他信号
    signal(SIGCLD, SIG_IGN);
    signal(SIGPIPE, SIG_IGN);
    signal(SIGSTOP, SIG_IGN);
 
    //将自己变成独立的会话
    if (fork() > 0) exit(0);
    setsid();
 
    //更改当前调用进程的工作目录
    if (!cwd.empty())
    {
        chdir(cwd.c_str());
    }
    
    //将标准输出, 标准错误进行重定向到dev/null, 垃圾文件
    int fd = open(nullfile.c_str(), O_RDWR);
 
    if (fd > 0)
    {
        dup2(fd, 0);
        dup2(fd, 1);
        dup2(fd, 2);
        close(fd);
    }
    
}

最后除了自己写的守护进程,系统也自带守护进程

参数解析:

nochdir(控制是否改变工作目录):

  • 0:将工作目录切换为根目录(/),避免原工作目录被卸载 / 删除导致进程异常。
  • 1:保持当前工作目录不变(需确保目录长期可用,否则有风险)。

noclose(控制是否重定向标准 I/O):

  • 0:将 stdin、stdout、stderr 重定向到 /dev/null(切断与终端的 I/O 关联,避免阻塞)。
  • 1:保持原文件描述符不变(需自行处理 I/O,否则可能因终端关闭导致错误)。

返回值

  • 成功:返回 0(进程已成为守护进程)。
  • 失败:返回 -1,并设置 errno(如 setsid 失败、chdir 失败等)。
相关推荐
小雪_Snow4 分钟前
CentOS 7 安装 docker 教程
linux·docker·centos
凤凰战士芭比Q20 分钟前
Linux部署基于Django的博客系统
linux·运维·django
源来猿往26 分钟前
高并发之nginx架构
运维·nginx
ASUJY41 分钟前
文件系统原理(基于Linux0.11)
linux·文件系统·linux0.11
边疆.1 小时前
【Linux】进程创建、进程终止、进程等待和进程程序替换
linux·运维·服务器·vim·进程控制·进程等待·进程替换
梦想的颜色1 小时前
阿里云ecs云服务器linux安装redis
linux·服务器·阿里云
Y淑滢潇潇2 小时前
RHCE Day5 SELinux
linux·运维·rhce
是垚不是土2 小时前
运维新人踩坑记录:Redis与MySQL生产故障排查&优化手册
运维·数据库·redis·mysql·云计算·bootstrap
snpgroupcn2 小时前
如何在SAP中实现数据验证自动化?5天缩短验证周期,提升转型效率的3大关键策略
运维·人工智能·自动化
optimistic_chen2 小时前
【Linux 系列】Linux 命令/快捷键详解
linux·运维·服务器·ubuntu·命令行·快捷键