网络入门---守护进程

news2024/9/21 11:06:33

目录标题

  • 什么是守护进程
  • 会话的理解
  • setsid函数
  • daemonSelf函数模拟实现
  • 测试

什么是守护进程

在前面的学习过程中我们知道了如何使用TCP协议和UDP协议来实现通信,比如说登录xshell运行了服务端:
在这里插入图片描述
然后再登录一个xshell运行客户端并向服务端发送信息:
在这里插入图片描述
服务端就会将接收到的消息打印出来:
在这里插入图片描述
但是这里就存在这么一个疑问?我们把运行服务端的xshell关掉这里的通信还能正常的实现吗?
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
可以看到把运行服务端的xshell关闭之后客户端也会跟着出现问题,那么这是不符合我们的预期的,我们希望看到的是:服务端的运行不受xshell的登录或者注销的影响,除非我们手动的使用kill指令将其关闭,那么我们把不受用户登录或者注销的进程称之为守护进程,那么本篇文章就要将我们之前写的程序变成守护进程。

会话的理解

首先我们的linux服务器就就是下面图片的一个大方框,xshell就是下面图片中的一个小方框:
在这里插入图片描述
每当我们登录xshell的时候,xshell就会在linux主机上创建一个会话,在会话里面就有一个进程用来做命令行解释器也就是bash:
在这里插入图片描述
然后在这个会话里面我们还可以创建其他的进程(也可以叫做任务或者作业)
在这里插入图片描述
在一个会话里面只能同时存在一个前台任务和一个或者多个后台任务,比如说当前的前台任务就是命令行解释器bash,我们可以通过bash执行各种指令:
在这里插入图片描述
但是将TCP的服务端运行之后我们可以看到这些指令是无法执行了:
在这里插入图片描述
那么这就是因为一个会话只能有一个前台进程,运行了服务端可执行程序之后服务端就变成了前台,bash就来到了后台,所以这个时候我们输入的指令是无法被bash读取的,我们先让程序终止然后在运行程序的指令后面添加&符号就可以让一个程序在后端运行不影响bash的指令读取:
在这里插入图片描述

使用jobs指令可以查看当前会话有几个后台进程正在运行,因为我们就运行了一个所以下面的图片就只显示了一个:
在这里插入图片描述
我们可以再使用sleep指令在后台多运行几个进程,然后就可以看到jobs显示的后台任务会变多:
在这里插入图片描述
最前面的数字表示这是几号作业,tcpserver就是2号作业,sleep 3 4 5就是3号作业,sleep 6 7 8就是4号作业,数字后面的Running就表示当前的作业是运行状态,使用grep指令查看进程就可以看到下面这样的内容:
在这里插入图片描述
仔细的观察不难发现进程sleep3 4 5的pid分别为3574 3575 3576是一段连续数字,进程sleep 6 7 8的pid分别为3651 3652 3653又是另外一段连续的数字,进程的pid如果是连续的话就说明这些进程之间的关系是兄弟关系,这些兄弟进程就属于同一个进程组,也就是说sleep345是一个进程组,sleep678又是另外一个进程组,一个进程组中可能会存在多个兄弟进程,但是一个进程组中必须得有一个老大,这个老大就是进程组中第一个被创建的进程,大家网上翻一下就不难发现sleep 30000是进程组中第一个被创建的,sleep 60000也是进程组中第一个被创建,每个进程都有一个PGID来记录当前组的组长是谁,如果自己的PID和PGID相等的话就说明你就是组长,该组其他的兄弟进程的PGID就记录着你的PID:
在这里插入图片描述
一个进程组的所用成员共同来完成一个作业,这就好比工地中的甲方会讲工地上的各种任务分给多个包工头,那么包工头就是组长,包工头又会号召很多其他的工人来共同完成不同的任务。图片中还有一个SID:
在这里插入图片描述
虽然当前的sleep进程分为两个不同的进程组,但是这些进程组成员的SID都是一样的都为2373,那么这个SID就是xshell给我们创建的会话id,SID的全称就是session id。使用指令fg加作业号可以将后台的作业调整到前台来运行:
在这里插入图片描述
使用ctrl +z就可以将前台作业全部暂停,暂停之后就会默认将bash调到前台继续运行,并且通过jobs指令也可以看到3号作业的状态由之前的running变成stoped
在这里插入图片描述
使用指令bg加作业号就可以将后台暂停的作业变成运行状态:
在这里插入图片描述
所以通过上面的实验我们证明了一个会话中可以存在多个进程,这些进程中只能有一个是前台进程但是可以有多个后台进程,并且不同的进程可以通过指令来做到前后台转换,创建一个会话的时候会创建多个进程或者作业,那么当我们关闭xshell时这个会话就会被销毁,那销毁之后这个会话创建出来的是前台进程还是后台进程都将不复存在这也是为什么上面xshell关闭之后服务端不能正常运行的原因,那么要想服务端不受xshell关闭的影响我们就得自成会话,自成进程组和终端设备无关就好比要想不受老板的骂要想不被强制加班就得主动辞职自己创建一个公司当老板:
在这里插入图片描述

setsid函数

实现守护进程有很多的方式,其中操作系统以及给我们提供了专门用来实现守护进程的接口deamon
在这里插入图片描述
但是该接口在实现的过程中会遇到很多未定义的问题,所以我们接下来在实现守护进程的时候就不使用该接口而是自己模拟实现一个daemon函数。要想实现守护进程就必须得调用setsid函数:
在这里插入图片描述
该函数的作用就是哪个进程调用setsid函数哪个进程就能够自成一个会话,会话的id就是该进程的pid,该进程也就变成了组长,进程调用该函数有一个前提就是该进程不能是组长,只有非组长进程才能调用setsid函数,这就好比只有普通员工跳槽辞职创建公司当老板,哪有公司老板辞职又去创建公司的呢!

daemonSelf函数模拟实现

该函数我们主要完成下面三件事情,第一:让调用进程忽略掉异常的信号,因为有些客户端可能会搞事情,比如说客户端给服务端发送了一条消息,服务端刚准备发送一条消息给客户端时,客户端就关闭这就好比向一个已经关闭的文件中写入内容,这时就会收到异常的信号从而影响到服务端的运行,所以第一步就是忽略掉一些异常信号,第二:因为组长进程无法调用setsid函数所以第二件事就是如何让当前的调用进程不再是组长。第三:因为守护进程是脱离终端的那所以就不需要向终端的上面显示数据和读取数据,所以就需要关闭或者重定向之前进程默认打开的一些文件。

void daemonSelf()
{
    //忽略掉一些异常信号

    //让自己不再是组长

    //关闭之前默认打开的文件
}

第一件事情很简单直接使用signal函数将SIGPIPE信号忽略即可:

void daemonSelf()
{
    //忽略掉一些异常信号
    signal(SIGPIPE, SIG_IGN);

    //让自己不再是组长

    //关闭之前默认打开的文件
}

对于第二件事我们可以创建使用fork函数创建一个子进程,因为父进程先被创建所以他就是组长,子进程不是,那么我们就可以通过if语句和exit让父进程直接退出,子进程执行setsid函数,setsid可能会执行失败,所以创建一个变量记录函数的返回值并判断会不会出现错误:

void daemonSelf()
{
    //忽略掉一些异常信号
    signal(SIGPIPE, SIG_IGN);

    //让自己不再是组长 
    if (fork() > 0)
        exit(0);
    // 子进程 -- 守护进程,精灵进程,本质就是孤儿进程的一种!
    pid_t n = setsid();
    assert(n != -1);
    //关闭之前默认打开的文件
}

第三件事我们不能直接关闭默认打开的0 1 2文件,因为我们不能完全保证调用的程序中不会向这些文件发送或者读取问题,一旦出现了就可能会出现问题,所以我们这里就采用这样的方法,系统中存在这么一个文件:/dev/null,他是一个黑洞文件任何进程都可以向他发送任何的数据,但是任何进程也不能从这个文件中读取到任何的数据
在这里插入图片描述
所以为了防止出现上面所述的问题,我们可以将0 1 2重定向到黑洞文件里面,所以我们这里先使用open函数打开黑洞文件,如果打开成功就使用dup2重定向,如果打开失败就只能关闭0 1 2,那么这里的代码如下:

#define DEV "/dev/null"

void daemonSelf(const char *currPath = nullptr)
{
    // 1. 让调用进程忽略掉异常的信号
    signal(SIGPIPE, SIG_IGN);

    // 2. 如何让自己不是组长,setsid
    if (fork() > 0)
        exit(0);
    // 子进程 -- 守护进程,精灵进程,本质就是孤儿进程的一种!
    pid_t n = setsid();
    assert(n != -1);

    // 3. 守护进程是脱离终端的,关闭或者重定向以前进程默认打开的文件
    int fd = open(DEV, O_RDWR);
    if(fd >= 0)
    {
        dup2(fd, 0);
        dup2(fd, 1);
        dup2(fd, 2);

        close(fd);
    }
    else
    {
        close(0);
        close(1);
        close(2);
    }
}

完成了上面三步我们就基本上实现了该函数的主要内容,但是在函数最后我们还可以做一些其他的事情比如说切换当前进程的执行路径等等,那么完整的代码就如下:

#pragma once

#include <unistd.h>
#include <signal.h>
#include <cstdlib>
#include <cassert>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define DEV "/dev/null"

void daemonSelf(const char *currPath = nullptr)
{
    // 1. 让调用进程忽略掉异常的信号
    signal(SIGPIPE, SIG_IGN);

    // 2. 如何让自己不是组长,setsid
    if (fork() > 0)
        exit(0);
    // 子进程 -- 守护进程,精灵进程,本质就是孤儿进程的一种!
    pid_t n = setsid();
    assert(n != -1);

    // 3. 守护进程是脱离终端的,关闭或者重定向以前进程默认打开的文件
    int fd = open(DEV, O_RDWR);
    if(fd >= 0)
    {
        dup2(fd, 0);
        dup2(fd, 1);
        dup2(fd, 2);

        close(fd);
    }
    else
    {
        close(0);
        close(1);
        close(2);
    }

    // 4. 可选:进程执行路径发生更改

    if(currPath) chdir(currPath);
}

测试

#include"tcpserver.hpp"
#include<memory>
#include<stdlib.h>
#include"daemon.hpp"
#include"log.hpp"
static void Usage(string proc)
{
    cout << "\nUsage:\n\t" << proc << " serverport\n\n";
}
int main(int args,char* argv[])
{
    int sock=3;
    if(args!=2)
    {
        Usage(argv[0]);
        exit(1);
    }
    uint16_t port=atoi(argv[1]);
    unique_ptr<tcpserver> tcil(new tcpserver(port));
    tcil->inittcpserver();
    daemonSelf();
    tcil->start();
    return 0;
}

在服务端运行之前我们先调用daemon函数使其变成守护进程,然后运行服务端:
在这里插入图片描述
可以看到运行之后bash依然存在,然后我们再运行客户端:
在这里插入图片描述
是可以直接运行的,并且我们关掉服务端再输入消息时也不会有任何的影响:
在这里插入图片描述
那么这就是守护进程的概念和模拟实现,希望大家能够理解。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1316619.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C#基础知识 - 基本语法篇

C#基础知识-基本语法篇 第2节 C#基本语法2.1 C#程序结构2.2 C# 结构解析2.3 命名空间及标识符、关键字2.3.1 别名的使用2.3.2 标识符2.3.3 C#关键字 更多C#基础知识详解请查看&#xff1a;C#基础知识 - 从入门到放弃 第2节 C#基本语法 2.1 C#程序结构 “Hello, World”程序历…

CSS复合选择器(在基础选择器上元素选择的方式不同)

后代选择器&#xff1a; ------------ 此情况下&#xff0c;红色的可以划去 子&#xff08;元素&#xff09;选择器&#xff1a; 并集选择器&#xff1a; 伪类选择器&#xff1a; 如放上字符会变色。 链接伪类选择器&#xff1a; foucus伪类选择器&#xff1a;

时序预测 | Python实现LSTM-Attention电力需求预测

时序预测 | Python实现LSTM-Attention电力需求预测 目录 时序预测 | Python实现LSTM-Attention电力需求预测预测效果基本描述程序设计参考资料预测效果 基本描述 该数据集因其每小时的用电量数据以及 TSO 对消耗和定价的相应预测而值得注意,从而可以将预期预测与当前最先进的行…

AutoSAR(基础入门篇)1.3-AutoSAR的概述

目录 一、到底什么是AutoSAR 1、大白话来讲 2、架构上来讲 应用软件层(APPL) 实时运行环境&#xff08;RTE&#xff09; 基础软件层(BSW) 3、工具链上来讲 二、AutoSAR的目标 一、到底什么是AutoSAR 1、大白话来讲 AUTOSAR 就是AUTomotive Open System ARchitecture的…

腾讯地图绘画多边形和计算面积

<!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>地图</title></head><script src…

[论文笔记] GAMMA: A Graph Pattern Mining Framework for Large Graphs on GPU

GAMMA: A Graph Pattern Mining Framework for Large Graphs on GPU GAMMA: 基于 GPU 的针对大型图的图模式挖掘框架 [Paper] [Code] ICDE’23 摘要 提出了一个基于 GPU 的核外(out-of-core) 图模式挖掘框架(Graph Pattern Mining, GPM) GAMMA, 充分利用主机内存来处理大型图…

【Spark精讲】Spark五种JOIN策略

目录 三种通用JOIN策略原理 Hash Join 散列连接 原理详解 Sort Merge Join 排序合并连接 Nested Loop 嵌套循环连接 影响JOIN操作的因素 数据集的大小 JOIN的条件 JOIN的类型 Spark中JOIN执行的5种策略 Shuffle Hash Join Broadcast Hash Join Sort Merge Join C…

ffmpeg踩坑之手动编译报错Unrecognized option ‘preset‘及rtsp/rtmp推流

本文解决的问题记录&#xff1a; 报错1&#xff1a;Unrecognized option preset. Error splitting the argument list: Option not found 报错2&#xff1a;ERROR: x264 not found using pkg-config 报错3&#xff1a;ffmpeg: error while loading shared libraries: libavd…

【Linux】文件系统、文件系统结构、虚拟文件系统

一、文件系统概述 1. 什么是文件系统&#xff1f;2. 文件系统&#xff08;文件管理系统的方法&#xff09;的种类有哪些&#xff1f;3. 什么是分区&#xff1f;4. 什么是文件系统目录结构&#xff1f;5. 什么虚拟文件系统Virtual File System &#xff1f;6. 虚拟文件系统有什…

Unity中 URP Shader 的纹理与采样器的分离定义

文章目录 前言一、URP Shader 纹理采样的实现1、在属性面板定义一个2D变量用于接收纹理2、申明纹理3、申明采样器4、进行纹理采样 二、申明纹理 和 申明采样器内部干了什么1、申明纹理2、申明采样器 三、采样器设置采样器的传入格式1、纹理设置中&#xff0c;可以看见我们的采样…

〖大前端 - 基础入门三大核心之JS篇(55)〗- 内置对象

说明&#xff1a;该文属于 大前端全栈架构白宝书专栏&#xff0c;目前阶段免费&#xff0c;如需要项目实战或者是体系化资源&#xff0c;文末名片加V&#xff01;作者&#xff1a;哈哥撩编程&#xff0c;十余年工作经验, 从事过全栈研发、产品经理等工作&#xff0c;目前在公司…

C# 获取Windows所有窗口句柄

写在前面 在做录屏或截屏操作时&#xff0c;需要获取当前正在运行中的桌面程序句柄&#xff0c;在网上查找资源的的时候&#xff0c;发现了一个工具类还不错&#xff0c;这边做个验证记录。 参考代码 public class WindowApi{//寻找目标进程窗口 [DllImport("USER…

前端桌面通知(Desktop Notifications)API

聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…

antd+vue:tree组件:父级节点禁止选择并不展示选择框——基础积累

antdvue:tree组件&#xff1a;父级节点禁止选择并不展示选择框——基础积累 1.判断哪些是父节点&#xff0c;给父节点添加disabled属性——this.permissionList是数据源2.通过css样式来处理disabled的父节点3.完整代码如下&#xff1a; 最近在写后台管理系统的时候&#xff0c;…

【linux】图形界面Debian的root用户登陆

图形界面Debian默认不允许以root用户登录。这是出于安全考虑&#xff0c;以防止用户使用root权限执行可能损害系统的操作。 如果需要使用root用户&#xff0c;可以通过以下步骤进行登录&#xff1a; 打开终端&#xff0c;使用su命令切换到root用户。修改/etc/gdm3/daemon.con…

2023年中国法拍房用户画像和数据分析

法拍房主要平台 法拍房主要平台有3家&#xff0c;分别是阿里、京东和北交互联平台。目前官方认定纳入网络司法拍卖的平台共有7家&#xff0c;其中阿里资产司法拍卖平台的挂拍量最大。 阿里法拍房 阿里法拍房数据显示2017年&#xff0c;全国法拍房9000套&#xff1b;2018年&a…

CentOS 7系统加固详细方案SSH FTP MYSQL加固

一、删除后门账户 修改强口令 1、修改改密码长度需要编译login.defs文件 vi /etc/login.defs PASS_MIN_LEN 82、注释掉不需要的用户和用户组 或者 检查是否存在除root之外UID为0的用户 使用如下代码&#xff0c;对passwd文件进行检索&#xff1a; awk -F : ($30){print $1) …

基于vue+element-plus+echarts制作动态绘图页面(柱状图,饼图和折线图)

前言 我们知道echarts是一个非常强大的绘图库&#xff0c;基于这个库&#xff0c;我们可以绘制出精美的图表。对于一张图来说&#xff0c;其实比较重要的就是配置项&#xff0c;填入不同的配置内容就可以呈现出不同的效果。 当然配置项中除了样式之外&#xff0c;最重要的就是…

Mr. Cappuccino的第66杯咖啡——解决MacOS中终端下的中文乱码问题

解决MacOS中终端下的中文乱码问题 中文乱码问题解决方法 中文乱码问题 解决方法 查看Mac使用的是哪个shell echo $SHELL我这里使用的是zsh&#xff0c;将配置添加到.zshrc配置文件中 vi ~/.zshrc 输入i进入编辑模式 esc退出编辑模式 :wq# UTF-8 export LANGen_US.UTF-8加载配…

k8s-1.23版本安装

一、主机初始化 1、修改主机名 hostnamectl set-hostname master hostnamectl set-hostname node1 hostnamectl set-hostname node2 hostnamectl set-hostname node32、主机名解析 echo 192.168.1.200 master >> /etc/hosts echo 192.168.1.201 node1 >>…