网络入门---守护进程

目录标题

  • 什么是守护进程
  • 会话的理解
  • 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依然存在,然后我们再运行客户端:
在这里插入图片描述
是可以直接运行的,并且我们关掉服务端再输入消息时也不会有任何的影响:
在这里插入图片描述
那么这就是守护进程的概念和模拟实现,希望大家能够理解。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

笔记本电脑如何安装openwrt

环境&#xff1a; 联想E14笔记本 装机U盘 DiskImage v1.6 刷写工具 immortalwrt镜像 问题描述&#xff1a; 笔记本电脑如何安装openwrt 解决方案&#xff1a; 一、官方版 1.官网下载固件 2.BIOS关闭安全启动改为引导 3.用U盘启动进入PE系统后&#xff0c;需要先用PE系…

国产浪潮服务器:风扇免手动调节脚本

简介&#xff1a;浪潮集团&#xff0c;是中国本土顶尖的大型IT企业之一&#xff0c;中国领先的云计算、大数据服务商。浪潮集团旗下拥有浪潮信息、浪潮软件、浪潮国际&#xff0c;业务涵盖云计算、大数据、工业互联网等新一代信息技术产业领域&#xff0c;为全球120多个国家和地…

JNA实现JAVA调用C/C++动态库

1.JNA JNA全称Java Native Access&#xff0c;是一个建立在经典的JNI技术之上的Java开源框架&#xff08;https://github.com/twall/jna&#xff09;。JNA提供一组Java工具类用于在运行期动态访问系统本地库&#xff08;native library&#xff1a;如Window的dll&#xff09;而…

计算机网络——数据链路层-可靠传输的实现机制:回退N帧协议GBN(无差错情况、累积确认、有差错情况、发送窗口尺寸)

目录 回退N帧协议GBN 介绍 无差错情况 累积确认 有差错情况 发送窗口尺寸 小结 练习 解析 示意图 上篇中所介绍的停止-等待协议的信道利用率很低&#xff1b;若出现超时重传&#xff0c;则信道利用率更低。 如果发送方在收到接收方的确认分组之前可以连续发送多个数…

Leetcode—2413.最小偶倍数【简单】

2023每日刷题&#xff08;六十&#xff09; Leetcode—2413.最小偶倍数 class Solution { public:int smallestEvenMultiple(int n) {return (n % 2 1) * n;} };运行结果 之后我会持续更新&#xff0c;如果喜欢我的文章&#xff0c;请记得一键三连哦&#xff0c;点赞关注收藏…

局域网环境下的ntp对时

服务端&#xff1a; 此处为v4-sp4服务器 安装ntp&#xff0c;apt-get install ntp -y ,若为离线环境&#xff0c;则安装ntp和libopts25两个包。 配置&#xff1a; 在/etc/ntp.conf的配置文件里 加入 restrict default nomodify notrap noquery restrict 127.0.0.1 rest…

libxlsxwriter - 编译

文章目录 libxlsxwriter - 编译概述笔记编译环境编译思路编译安装组件写个测试程序, 看看编译的组件是否好使END libxlsxwriter - 编译 概述 想换一个新版的libxlsxwriter, 自己编译一个出来. libxlsxwriter依赖zlib, 前面已经成功编译了zlib(zlib - 编译). 笔记 libxlsxwr…

大模型Transformer 推理 :kvCache原理浅析

大模型Transformer 推理 :kvCache原理浅析 kvCache 原理 在采样时,Transformer模型会以给定的提示/上下文作为初始输入进行推理(可以并行处理),然后逐一生成额外的标记来继续完善生成的序列(体现了模型的自回归性质)。在采样过程中,Transformer会执行自注意力操作,为…

若依 ruoyi-vue3 集成aj-captcha实现滑块、文字点选验证码

目录 0. 前言0.1 说明 1. 后端部分1.1 添加依赖1.2. 修改 application.yml1.3. 新增 CaptchaRedisService 类1.4. 添加必须文件1.5. 移除不需要的类1.6. 修改登录方法1.7. 新增验证码开关获取接口1.8. 允许匿名访问 2. 前端部分&#xff08;Vue3&#xff09;2.1. 新增依赖 cryp…

C++中STL的概念——零基础/小白向,适合竞赛,初学C++者使用

目录 1.STL的诞生 2. STL的基本概念 3. STL六大组件 4. STL容器&#xff0c;算法&#xff0c;迭代器 容器&#xff1a;存放数据的地方 算法&#xff1a;解决问题的方法 迭代器&#xff1a;容器和算法之间的桥梁 5. STL初始&#xff1a;打印0 ~ 9 的数字 这篇文章是一篇…

day34算法训练|贪心算法

1005.K次取反后最大化的数组和 两次贪心算法思路 1. 数组中有负数时&#xff0c;把绝对值最大的负数取反 2. 数组全为非负数时&#xff0c;一直取反最小的那个数 步骤&#xff1a; 第一步&#xff1a;将数组按照绝对值大小从大到小排序&#xff0c;注意要按照绝对值的大小…

云仓酒庄为您甄选西班牙葡萄酒

西班牙是一个拥有悠久葡萄酒酿造与饮用历史的国家&#xff0c;其葡萄酒产量位居世界第三位。云仓酒庄的品牌雷盛红酒分享翻开西班产区地图&#xff0c;不少葡萄酒刚入门的朋友会感到头疼&#xff0c;众多产区、分级制度、陈年标准&#xff0c;想要短时间内搞懂实在不容易。不用…

案例069:基于微信小程序的计算机实验室排课与查询系统

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;SSM JDK版本&#xff1a;JDK1.8 数据库&#xff1a;mysql 5.7 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.5.4 小程序框架&#xff1a;uniapp 小程序开发软件&#xff1a;HBuilder X 小程序…

设计模式-状态(State)模式

目录 开发过程中的一些场景 状态模式的简单介绍 状态模式UML类图 类图讲解 适用场景 Java中的例子 案例讲解 什么是状态机 如何实现状态机 SpringBoot状态自动机 优点 缺点 与其他模式的区别 小结 开发过程中的一些场景 我们在平时的开发过程中&#xff0c;经常会…

【C语言(十五)】

动态内存管理 一、为什么要有动态内存分配? 我们已经掌握的内存开辟方式有&#xff1a; int val 20 ; // 在栈空间上开辟四个字节 char arr[ 10 ] { 0 }; // 在栈空间上开辟 10 个字节的连续空间 但是上述的开辟空间的方式有两个特点&#xff1a; • 空间开辟大小是固…

leetcode LCR 173. 点名

代码&#xff1a; class Solution {public int takeAttendance(int[] records) {int left0,rightrecords.length-1;while (left<right){int midleft(right-left)/2;if(midrecords[mid]){leftmid1;}else {rightmid;}}if(leftrecords[left]){return left1;}else {return left…

北斗三号短报文+4G的低功耗太阳能船载报位监控方案

国内海洋船舶群体长期在海上航行&#xff0c;多数海员由于海面无信号覆盖、个人卫星通信费用昂贵、无法自由使用船载公用卫星通信设备等原因&#xff0c;无法与家人和朋友保持联系&#xff0c;甚至在遇到危险的时候也无法及时向外界发出求救信号&#xff0c;管理单位难以掌握船…

新钛云服助力爱达邮轮·魔都号首航,保驾护航,共创辉煌

随着2024年1月1日的临近&#xff0c;中国首艘国产大型邮轮——爱达邮轮魔都号即将迎来激动人心的首航时刻。作为爱达邮轮的IT系统运维和安全服务伙伴&#xff0c;新钛云服有幸提前登船体验&#xff0c;并为魔都号即将到来的航行提供全面的技术支持与保障。 爱达魔都号&#xff…

微积分-三角函数2

三角函数 在上一节中&#xff0c;讨论了如何在直角三角形中定义三角函数&#xff0c;限制让我们扩展三角函数的定义域。 事实上我们可以取任意角的正弦和余弦&#xff0c;而不只是局限于 0 0 0~ π 2 \frac{\pi}{2} 2π​当中。 当然需要注意的是&#xff0c;正切函数对不是对…

Git使用rebase和merge区别

Git使用rebase和merge区别 模拟环境使用merge合并使用rebase 模拟环境 本地dev分支中DevTest增加addRole() 远程dev被同事提交增加了createResource() 使用merge合并 使用idea中merge解决冲突后, 推送远程dev后,日志图显示 使用rebase idea中使用功能rebase 解决冲突…