【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 失败等)。
相关推荐
LPH311913 分钟前
Linux系统安全管理
linux·网络·安全·系统安全
linux行者1 小时前
linux基础重定向及组合重定向
linux·运维
m0_653031362 小时前
腾讯云TCCA认证考试报名 - TDSQL数据库交付运维工程师(PostgreSQL版)
运维·数据库·腾讯云
朱小勇本勇2 小时前
Clang Code Model: Error: The clangbackend executable “D:\Soft\Qt5.12.12\Tool
运维·服务器·数据库·qt·nginx
网硕互联的小客服2 小时前
502 Bad Gateway:服务器作为网关或代理时收到无效响应处理方式
运维·服务器·gateway
kikumaru7142 小时前
Mac 安装 finalshell
linux·网络·macos
哈哈浩丶3 小时前
Linux系统移植⑦:uboot启动流程详解-board_init_r执行过程
linux·驱动开发
mit6.8243 小时前
[Linux_core] “虚拟文件” | procfs | devfs | 上下文
linux·c语言·c++
安顾里3 小时前
aws各类服务器编号
服务器·云计算·aws
再见晴天*_*4 小时前
超图superMap iObjects for Java的Jar使用中遇到的问题
运维·服务器