Linux网络:守护进程

Linux网络:守护进程

    • 会话
      • 进程组
      • 会话
      • 终端
    • 守护进程
      • setsid
      • daemon


在创建一个网络服务后,往往这个服务进程是一直运行的。但是对于大部分进程来说,如果退出终端,这个终端上创建的所有进程都会退出,这就导致进程的生命周期与终端绑定,如果终端退出,服务就无法继续运行了。

为此,创建一个服务进程后,需要让其不在受到终端的影响,就算终端退出,进程依然执行,这种进程称为守护进程

在讲解如何创建一个守护进程之前,先了解一些Linux中与会话相关的知识。

会话

进程组

进程组是一个或者多个进程的集合,这些进程都有同样的进程组ID(PGID),这个ID和PID类似,都是一个正整数,可以放在pid_t类型中。

往往一个进程组内部的所有进程,要共同完成一个功能,这些进程之间可以进行通信。而当在Linux中通过管道命令创建多个进程,此时Linux认为多个进程是共同完成一个功能的,就会把它们放到同一个进程组中。

通过管道创建三个sleep进程:

在这里插入图片描述

创建进程后,通过ps -ajx查看进行信息,可以看到三个经常的PGID都是54844,也就是sleep 100这条命令的PID。这说明这三个由管道串联起来的进程,属于同一个进程组。而sleep 100这个进程是进程组长

进程组长往往是进程组中的第一个进程,其可以在当前进程组中创建新的进程,也可以创建一个新的进程组。

但是如果进程组长退出了,不代表进程组退出了,就算进程组长退出了,只要进程组中还有进程,那么这个进程组依然存在。

在这里插入图片描述

通过kill杀掉组长进程后,剩余的两个进程依然存在,并且PGID依然是54844


会话

一个Linux系统,可以有多个会话,每个会话都是一个或多个进程组的集合。

创建一个新的会话,并在会话中执行三个sleep进程:

在这里插入图片描述

在原先的会话中,查看bash进程和sleep进程的信息:

在这里插入图片描述

此处-E "bash|sleep"表示查找bash或者sleep进程。

其中SID表示会话ID,可以看到第一个bashSID = 53792,第二个bashSID = 54989

这两个bash本身也是进程,它们的PID == PGID == SID。这种PID == SID的进程,称为话首进程,它是会话中的第一个进程。一般而言,话首进程都是bash

而创建的三个sleep进程,它们的SID = 54989,与第二个bash相同,说明它们属于同一个会话,而54989会话中,包含四个进程以及两个进程组。而53792会话中只有一个进程和一个进程组。

这说明一个会话是一个或多个进程组的集合。当一个话首进程退出,这个会话内部的所有进程都会退出

在一个会话中创建三个sleep进程:

在这里插入图片描述

随后把bash话首进程杀掉:

在这里插入图片描述

可以看到,不仅仅话首进程退出了,三个sleep进程一起退出了。


终端

一个会话不仅仅包含多个进程组,它还需要一个控制终端,来接受用户的指令。

当创建一个会话,会执行以下两步:

  1. 创建一个终端
  2. 启动一个bash进程(组)

终端本质上是一个Linux的文件,存在于/dev/pts目录下。

在这里插入图片描述

当前我创建了两个会话,那么就有两个终端,这两个终端分别对应01这两个文件。

如果尝试删除这些文件会被拒绝,哪怕你是root用户,因为这些文件是由内核管理的。

除去两个终端文件,还有一个ptmx,这个文件是用于创建终端的文件,如果这个文件丢失,可能就无法创建新的终端了。

在一个会话中,包含多个进程组,这些进程组分为两类:

  1. 前台进程组:一个会话只有一个,这个进程组独占终端的输入输出流
  2. 后台进程组:一个会话可以有多个,无法接收到来自终端的数据,在后台运行

在这里插入图片描述

比如向终端输入ctrl + c,就是一个中断信号,此时这个信号会发送给前台进程,导致前台进程退出。

但是并不是所有的信号都直接发送给前台进程组,因为有一个特殊的bash进程,如果某些信号的功能是直接挂断整个会话,那么这个信号会发送给bash,哪怕bash不是当前的前台进程组。

至此,可以这样理解Linux中的会话机制:

  1. 一个进程组管理多个进程,这些进程往往共同完成一个功能
  2. 一个会话管理多个进程组,当会话退出,该会话下的所有进程组都退出
  3. 终端是会话的对外表现,一个会话只有一个进程组可以占用终端

刚才提到,一个终端本质就是/dev/pts下的一个文件,其实与终端交互,就是进程在读写这个文件。那么一个进程如何知道要读取/dev/pts下的哪一个文件?会话又是如何保证其下的所有进程都使用同一个终端的?

终端的信息存储在进程的PCB中,先前说过会话创建包括一个终端和一个bash,而这个过程中,会把这个终端的信息存储到bashPCB

而在一个会话中创建的所有进程,都是bash的后代进程,会继承来自bashPCB,也就会继承到PCB中的终端信息,从而保证同一会话的所有进程最后都操控同一个终端


守护进程

了解Linux的会话机制后,就可以谈一谈守护进程的问题了。

先前说过,当一个会话的话首进程退出,那么该会话的所有进程都会退出,因此一个守护进程一定不能属于其它会话,而是自己创建一个会话,自己就是话首进程

setsid

setsid可以创建一个新会话并把自己变成话首进程,需要头文件<sys/types.h><unistd.h>

函数声明:

pid_t setsid(void)

调用该函数有一个前提,该进程不能是进程组长

但是对于一个新创建的进程,它自成一个进程组,自己就是进程组长,如何才能让它不是进程组长?

尝试以下代码:

#include <unistd.h>int main()
{fork();while (true){}return 0;
}   

这个代码中,通过fork创建了一个子进程,随后父子进程同时陷入死循环。

运行代码:

在这里插入图片描述

此时终端被阻塞,切换到另一个终端,查看test.exe这个进程的信息。

在这里插入图片描述

此时查询到了两个进程,一个是父进程,另一个是子进程,重点在于它们的GPID都是52746,这说明通过fork创建的父子进程属于同一个进程组

frok创建的子进程会继承来自父进程的所有代码,PCB等信息。因此如果想要让一个进程调用setsid创建一个新会话,只需要通过fork创建一个子进程,子进程继承父进程的所有信息,但又不是进程组长,可以调用setsid

因此setsid的常见写法如下:

pid_t id = fork();
if (id > 0)exit(0);setsid();

这段代码中,通过fork创建一个子进程,此时父子进程属于同一个进程组,父进程是组长。如果id > 0说明是父进程,父进程直接退出,一个进程组中,进程组长退出,进程组其他进程继续运行,不会退出。因此子进程不会退出,继承了父进程的所有信息,并且还不是进程组长。

于是子进程调用setsid,自己创建一个会话,自己做话首进程,至此子进程就是一个守护进程了!

运行以下代码:

#include <unistd.h>
#include <sys/types.h>int main()
{pid_t id = fork();if (id > 0)exit(0);setsid();while (true){}return 0;
}

这个代码在子进程变为守护进程后,执行一个死循环。

在这里插入图片描述

执行./test.exe后,查看bashtest进程的信息,发现test.exe这个进程PID=PGID=SID=58278,并且不和任何一个bash重复。这说明test.exe已经自己创建一个会话,自己做话首进程,成为了一个守护进程了。

但是还有一个问题,那就是它的终端没有切断:

在这里插入图片描述

可以看到它的三个标准流,都指向/dev/pts/0,这就是当前的终端文件。如果当前终端退出,那么如果这个守护进程还向终端输出内容,就会导致错误,因此还要改变它的输出流:

// 关闭标准输入、输出和错误
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);// 打开/dev/null作为标准输入、输出和错误
open("/dev/null", O_RDONLY);
open("/dev/null", O_WRONLY);
open("/dev/null", O_WRONLY);

此处的/dev/null是一个Linux提供的文件,它可以接收任何输入,但是不论输入什么都会被系统丢弃。因此如果一个程序有输出,但是又不希望接收到它的输出时,就可以把输出重定向到/dev/null下。


daemon

setsid的用法还是有点复杂了,为此Linux提供了另一个系统调用daemon,它封装了上述所有过程,可以快速创建一个守护进程。需要头文件<unistd.h>

函数声明:

int daemon(int nochdir, int noclose)

参数:

  • nochdir:改变工作目录
    • 传入0:改变工作目录为根目录
    • 传入1:保持当前工作目录
  • nclose:改变输入输出流
    • 传入0:输入输出流重定向到/dev/null
    • 传入1:不改变输入输出流

原先的代码就可以变成:

#include <unistd.h>
#include <sys/types.h>int main()
{daemon(0, 0);while(true){}return 0;
}

这就是一个功能全面的守护进程了。


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

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

相关文章

发布jar包到maven仓库中的命令

将JAR包发布到Maven仓库中&#xff0c;可以通过以下步骤和命令来完成&#xff1a; 安装到本地Maven仓库 如果你只是想将JAR包安装到本地Maven仓库中&#xff0c;以便在当前机器上的其他Maven项目可以使用&#xff0c;可以使用以下命令&#xff1a; bash mvn install:instal…

基于gradio+networkx库对图结构进行可视化展示

前言 在gradio框架下对蛋白质-蛋白质相互作用网络&#xff08;PPI网络&#xff09;进行可视化&#xff0c;并将其在网页前端进行展示。 方法 其实很简单 可以直接使用networkx画图后保存图片&#xff0c;然后使用Gradio框架的image组件进行展示即可。 但实际上gradio还配置…

Remora

Remora Remora 模型能够独立于碱基识别过程预测甲基化/修饰碱基的状态。Remora 仓库专注于准备修饰碱基训练数据和训练修饰碱基模型。此外,还提供了一些用于运行 Remora 模型和调查原始信号的功能。对于生产环境中的修饰碱基识别,建议使用 Dorado <https://github.com/na…

【大数据学习 | HBASE高级】hive操作hbase

一般在查询hbase的数据的时候我们可以直接使用hbase的命令行或者是api进行查询就行了&#xff0c;但是在日常的计算过程中我们一般都不是为了查询&#xff0c;都是在查询的基础上进行二次计算&#xff0c;所以使用hbase的命令是没有办法进行数据计算的&#xff0c;并且对于hbas…

Rust:原子操作 AtomicBool

在 Rust 中&#xff0c;你可以使用 std::sync::atomic 模块来进行原子操作。原子操作在多线程环境中特别有用&#xff0c;因为它们可以确保操作的原子性和可见性&#xff0c;从而避免数据竞争和其他并发问题。 为了读取和设置布尔值&#xff0c;你可以使用 AtomicBool 类型。以…

Python小游戏28——水果忍者

首先&#xff0c;你需要安装Pygame库。如果你还没有安装&#xff0c;可以使用以下命令进行安装&#xff1a; 【bash】 pip install pygame 《水果忍者》游戏代码&#xff1a; 【python】 import pygame import random import sys # 初始化Pygame pygame.init() # 设置屏幕尺寸 …

基于SpringBoot的养老院管理系统+文档

&#x1f497;博主介绍&#x1f497;&#xff1a;✌在职Java研发工程师、专注于程序设计、源码分享、技术交流、专注于Java技术领域和毕业设计✌ 温馨提示&#xff1a;文末有 CSDN 平台官方提供的老师 Wechat / QQ 名片 :) Java精品实战案例《700套》 2025最新毕业设计选题推荐…

智能购物时代:AI在电商平台的革命性应用

在当今数字化时代&#xff0c;人工智能&#xff08;AI&#xff09;技术已成为推动电商行业发展的关键力量。AI技术的应用不仅改变了电商的运营模式&#xff0c;还极大地丰富了消费者的购物体验。随着技术的不断进步&#xff0c;AI在电商领域的应用越来越广泛&#xff0c;从个性…

卷积、频域乘积和矩阵向量乘积三种形式之间的等价关系与转换

线性移不变系统 线性移不变系统&#xff08;Linear Time-Invariant System, LTI系统&#xff09;同时满足线性和时不变性两个条件。 线性&#xff1a;如果输入信号的加权和通过系统后&#xff0c;输出是这些输入信号单独通过系统后的输出的相同加权和&#xff0c;那么该系统就…

基于51单片机的电子钟+秒表LCD1602仿真设计

电子钟秒表 0. 设计资料内容清单&&下载链接1. 主要功能&#xff1a;2. 讲解视频&#xff1a;3. 仿真4. 程序代码5. 设计报告6. 原理图 基于51单片机的电子钟秒表LCD1602仿真设计( proteus仿真程序设计报告原理图讲解视频&#xff09; 仿真图proteus7.8及以上 程序编译…

unity 3d到idea

第一步&#xff0c;确保jdk,sdk,gradle版本一致 unity 3d的配置 idea配置 第二步&#xff0c;整个unity导出安卓项目到idea idea配置项目&#xff08;修改gradl配置&#xff09; gradle配置代码 distributionUrlhttps\://services.gradle.org/distributions/gradle-8.7-bin.z…

通过地址获取LONG和LAT并且存入csv

通过地址获取LONG和LAT并且存入csv 1. Address存在Address这个column里&#xff0c;从网上复制&#xff08;如果可以爬虫自动更好&#xff09; 2. 用代码获取GPS&#xff0c;再存入表格 import pandas as pd from geopy.geocoders import Nominatim from time import sleep#…

【日志】binlog 文件的三种格式类型

MySQL 的 binlog&#xff08;binary log&#xff09; 是一种日志文件&#xff0c;它记录了所有对数据库的数据更改操作&#xff0c;包括插入、更新、删除等。 主用于主从复制和数据恢复等操作。 binlog 文件的格式有三种主要类型&#xff1a;STATEMENT、ROW 和 MIXED。 1. STA…

微信小程序的云函数

微信小程序的云开发中&#xff0c;云函数是一种运行在云端的 Node.js 函数&#xff0c;它允许开发者在云端执行代码&#xff0c;而无需自己搭建服务器。编写云函数时&#xff0c;需要遵循一定的思维模式来确保代码的安全性、效率和可维护性。以下是编写云函数时的一些关键思维&…

要查看你的系统是 x64(64位)还是 x86(32位),可以按照以下步骤操作

文章目录 1. 通过“系统信息”查看系统架构2. 通过“设置”查看系统架构3. 通过命令提示符查看系统架构4. 通过 PowerShell 查看系统架构5. 通过文件资源管理器查看系统架构总结 要查看你的系统是 x64&#xff08;64位&#xff09;还是 x86&#xff08;32位&#xff09;&…

ARM 汇编指令

blr指令的基本概念和用途 在 ARM64 汇编中&#xff0c;blr是 “Branch with Link to Register” 的缩写。它是一种分支指令&#xff0c;主要用于跳转到一个由寄存器指定的地址&#xff0c;并将返回地址保存到链接寄存器&#xff08;Link Register&#xff0c;LR&#xff09;中。…

跟李笑来学美式俚语(Most Common American Idioms): Part 11

Most Common American Idioms: Part 11 前言 本文是学习李笑来的Most Common American Idioms这本书的学习笔记&#xff0c;自用。 Github仓库链接&#xff1a;https://github.com/xiaolai/most-common-american-idioms 使用方法: 直接下载下来&#xff08;或者clone到本地…

手机ip地址异常怎么解决

在现代社会中&#xff0c;手机已成为我们日常生活中不可或缺的一部分&#xff0c;无论是工作、学习还是娱乐&#xff0c;都离不开网络的支持。然而&#xff0c;有时我们会遇到手机IP地址异常的问题&#xff0c;这不仅会影响我们的网络体验&#xff0c;还可能带来安全隐患。本文…

validate简单入门

validate第三方验证库 介绍&#xff1a; 在我们的平常业务中难免会遇到参数验证的情况&#xff0c;这就不免需要我们手动的为每组参数写一段代码&#xff1a;是否满足某种参数数据的传输格式&#xff08;json、xml等&#xff09;、是否满足参数字段的格式&#xff08;长度、字…

【Linux系统编程】第四十七弹---深入探索:POSIX信号量与基于环形队列的生产消费模型实现

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】【Linux系统编程】 目录 1、POSIX信号量 2、基于环形队列的生产消费模型 2.1、代码实现 2.1.1、RingQueue基本结构 2.1.2、PV操作 2.1.3、构造析构…