Linux--任务管理与守护进程

目录

任务管理

进程组概念

作业概念

会话概念

补充

守护进程

基本概念

守护进程的查看

守护进程的创建

自己手写守护进程

使用系统调用函数创建守护进程


任务管理

进程组概念

  • 每一个进程除了有一个进程ID之外,还有一个进程组ID,进程组是一个或多个进程的集合
  • 通常它们与同一个作业相关联,可以接收来自同一个终端的各种信号。每个进程组都有一个唯一的进程组ID。每一个进程组都有一个组长进程。组长进程ID==进程组ID。组长进程可以创建一个进程组,创建该组中的进程,然后终止。
  • 需要注意,只要在某个进程组中有一个进程存在,则该进程就存在,这与组长进程是否存在没有关系。

作业概念

  • Shell分前后台来控制的不是进程而是作业(Job)或者进程组(Process Group).
  • 一个前台作业可以有多个进程组成,Shell可以运行一个前台作业和多个后台作业,这称为作业控制。
  • 作业与进程的区别:如果作业中某个进程又创建了子进程,则子进程不属于作业。一旦作业运行结束,Shell就把自己提到前台,如果原来的前台进程还存在,也就是这个被创建的子进程还没有终止,那么它就将自动变为后台进程组。

会话概念

  • 会话(Session)是一个或多个进程组的集合。
  • 一个会话可以又一个控制终端,这里通常是登录到其他设备的终端(在终端登录情况下)或伪终端设备(在网络登录情况下)。建立于控制终端连接的会话进程被称为控制进程。一个会话中的进程组可以被分为一个前台进程组以及多个后台进程组。
  • 会话也有ID用来标识,这里会话ID==bash的PID。

补充

(1)前台进程和后台进程

前台进程: ./运行时,默认将程序放到前台执行,在前台运行时,进程状态后面会有一个+号

后台进程: ./XXX & .在执行可执行程序时在后面加上 & 。就时将程序放到后台执行。进程的状态后面没有+号。

这里我们将程序放到后台执行我们会发现多了一行提示信息。

这里[3]时作业编号,如果同时运行多个作业可以用这个编号区分,23612时该作业中某个进程的PID(一个作业可以由多个进程组成)。

对于前后台进程经常使用到的命令

jobs  //查看当前会话中由哪些作业
fg 后台进程编号 //将编号对应的作业提至前台运行
bg 后台进程编号 //可以让某个停止的作业到后台重新运行

关于会话

  • 当我们用一个Xshell或者终端登录时,本质都是先创建一个bash进程,整体称之为一个会话(所有的命令行的进程都是bash的子进程),所有的命令行启动的任务都是在对应的会话内运行的。
  • 实际我们每一次登录的过程都是新建会话的过程,同一个会话中的所有进程的SESS是相同

守护进程

基本概念

  • 守护进程也称精灵进程(Daemon),是运行在后台的一种特殊进程,它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。
  • 为了保证一个任务,当终端会话退出的时候,这个任务还在服务后端跑,所以我们需要一种进程叫守护进程
  • 守护进程是一种很有用的进程,Linux的大多数服务器就是用守护进程实现的,比如Internet服务器inetd,Web服务器httpd等。同时守护进程完成许多系统任务,比如作业规划进程crond等。
  • Linux系统启动时会启动很多系统服务进程,这些系统服务进程没有控制终端,不能直接和用户交互。其他进程都是在用户登录或运行程序时创建,在运行结束或用户注销时终止,但系统服务进程不受用户登录注销的影响,它们一直在运行着,这种进程有一个名称叫守护进程(Daemon)。

守护进程的查看

  • 参数a表示不仅列出当前用户的进程,也列出所有其他用户的进程。
  • 参数x表示不仅列出有控制终端的进程,也列出所有无控制终端的进程。
  • 参数j表示列出与作业控制相关的信息

  • 凡是TPGID一栏写着-1的都是没有控制终端的进程,也就是守护进程
  • 除此之外,在COMMAND一列用[ ]括起来的名字表示内核线程,这些线程在内核里创建,没有用户空间代码,因此没有程序文件名和命令行,通常采用以k开头的名字,表示Kernel。
  • 个别说明:udevd负责维护/dev目录下的设备文件 ,acpid负责电源管理 , syslogd负责维护/var/log下的日志文件。
  • 可以看出,守护进程通常采用以d结尾的名字,表示Daemon。

守护进程的创建

自己手写守护进程

创建思路:

  • 1.设置文件掩码为0.
  • 2.fork后终止父进程,子进程调用setsid创建新会话。
  • 3.忽略SIGCHLD信号。
  • 4.更改工作目录为根目录。
  • 5.将标准输入,标准输出,标准错误重定向到/dev/null。

解释:

  • 1.将文件掩码设置为0,是为了保证后续创建进程是,创建出来的文件权限符合我们的预期。
  • 2.setsid函数创建新会话的目的是,让当前进程自成会话,与目前bash脱离关系(即关闭当前页面进程仍可以在我们的云服务器上运行)。这也是创建守护进程的核心。
  • 3.之所以让子进程调用setside创建新会话,是因为创建新会话的进程不能是组长进程,但是当我们在命令行上启动多个进程协同完成一个任务时,第一个被我们创建的进程就是组长进程,所以这里我们使用fork创建子进程,让子进程调用setsid创建新的会话并继续执行后续代码,而父进程直接退出。
  • 4.我们一般会将守护进程的工作目录设置为根目录,便于让守护进程以绝对路径的形式访问某种资源。(该操作不是必须的)
  • 6.守护进程不能直接和用户交互,也就是说守护进程已经与终端去关联了,因此一般我们会将守护进程的标准输入、标准输出以及标准错误都重定向到/dev/null,/dev/null是一个字符文件(设备),通常用于屏蔽/丢弃输入输出信息。(该操作不是必须的)

守护进程代码(Demon.hpp):

#pragma once

#include <iostream>
#include <string>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include<cstdlib>
const std::string nullfile = "/dev/null";

void Demon(const std::string &cwd = "")
{
    // 忽略其他异常信号
    signal(SIGPIPE, SIG_IGN);
    signal(SIGCHLD, SIG_IGN);
    signal(SIGSTOP, SIG_IGN);

    // 2.将自己变为独立的会话
    // 因为进程组的组长无法调用系统调用函数setsid,所以这里采用fork创建子进程,
    // 让父进程直接结束,子进程创建独立的会话,并执行下面的代码
    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);
    }
}

测试代码(test.cpp):

#include<iostream>
#include"Demon.hpp"

int main()
{

    Demon();
    while(1)
    {
        std::cout<<"1adasd"<<std::endl;
        sleep(1);
    }

    return 0;
}

这里我们启动进程后可以使用指令:

//该指令用来查看我们的守护进程是否真的运行起来了
ps -ajx | head -1 && ps -ajx | grep test

  • 这里我们可以发现使用ps产看给进程的信息时,发现给进程的TTY为?,意味着该进程与终端失去关联。
  • test进程的SID与grep进程的SID不同,即它们不属于同一个会话。

同时我们也可以使用指令:

//查看该进程的标准输出,输入,错误是否已经重定向到/dev/null
ls /proc/进程PID/fd -al

使用系统调用函数创建守护进程

我们创建守护进程时可以直接调用daemon接口进行创建,daemon函数的函数原型如下:

int daemon(int nochdir, int noclose);

参数说明:

  • 如果参数nochdir为0,则将守护进程的工作目录该为根目录,否则不做处理。
  • 如果参数noclose为0,则将守护进程的标准输入、标准输出以及标准错误重定向到/dev/null,否则不做处理。

例子:

#include<iostream>
#include<unistd.h>

int main()
{

    daemon(0,0);
    while(1)
    {
        std::cout<<"1adasd"<<std::endl;
        sleep(1);
    }

    return 0;
}

  • 这里调用系统调用函数daemon创建的守护进程与我们手动创建的守护进程区别不大。唯一的区别就是,daemon函数创建的守护进程即使组长进程,又是会话首进程。
  • 系统调用的进程没有防止守护进程打开终端

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

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

相关文章

新零售SaaS架构:线上商城系统架构设计

零售商家为什么要建设线上商城&#xff1f; 传统的实体门店服务范围有限&#xff0c;只能吸引周边500米以内的消费者。因此&#xff0c;如何拓展服务范围&#xff0c;吸引更多的消费者到店&#xff0c;成为了店家迫切需要解决的问题。 缺乏忠实顾客&#xff0c;客户基础不稳&a…

clickhouse突然启动不起来问题排查

场景&#xff1a; 在实现postgreql数据迁移到clickhouse中&#xff0c;想使用MaterializedPostgreSQL的功能实现&#xff0c;但是中途clickhouse突然挂了&#xff0c;就再启动不了了。 现象&#xff1a; systemctl start clcikhouse-server启动报错 [rootlocalhost clickhous…

python矢量算法-三角形变化寻找对应点

1.算法需求描述 现有随机生成的两个三角形A与B&#xff0c;在三角形A中存在Pa&#xff0c;使用算法计算出三角形B中对应的点Pb 2.python代码 import numpy as np # 计算三角形A的面积 def area_triangle(vertices): return 0.5 * np.abs(np.dot(vertices[0] - vertices[…

用python爬取CSDN博客的总字数

一、下载pycahrm 此处推荐博客&#xff1a;PyCharm安装教程&#xff0c;图文教程&#xff08;超详细&#xff09;-CSDN博客 二、安装相应的库 pycharm安装库的步骤&#xff1a; 1、打开pycharm&#xff1b; 2、在菜单栏中&#xff0c;选择 "file">"setti…

d3dcompiler43.dll缺失怎么修复,教你五个方法快速搞定

在数字世界的深渊中&#xff0c;有一个名为d3dcompiler_43.dll的神秘文件&#xff0c;它就像一把打开现代科技之门的钥匙。这个文件是DirectX 12的一部分&#xff0c;由微软公司开发&#xff0c;用于编译和处理图形数据。 d3dcompiler_43.dll是一个动态链接库&#xff08;DLL&…

代码随想录算法训练营第三十一天 | 455. 分发饼干、376. 摆动序列、53. 最大子数组和

代码随想录算法训练营第三十一天 | 455. 分发饼干、376. 摆动序列、53. 最大子数组和 455. 分发饼干题目解法 376. 摆动序列题目解法 53. 最大子数组和题目解法 感悟 455. 分发饼干 题目 解法 class Solution { public:int findContentChildren(vector<int>& g, vec…

AcWing 1250. 格子游戏 (并查集,坐标变换)

记录此题的目的&#xff1a; 明确二维的坐标可以映射到一维&#xff1a;在x和y都是从0开始的前提下&#xff0c;假如图形列数为n&#xff0c;(x,y)映射到一维可以写成x * n y。并查集并不好存储二维数据&#xff0c;如果遇到二维数据可以将其映射到一维。 Alice和Bob玩了一个…

Amazon Bedrock 实践 | 动手玩转 Claude 3

生成式 AI 和大模型在 2024 年已经进入落地实践阶段。因此&#xff0c;围绕开发者在生成式应用程序开发中的主要痛点和需求&#xff0c;我们组织了这个 “Amazon Bedrock 实践” 的系列&#xff0c;希望可以帮助开发者高效地上手生成式 AI 和大模型的应用开发&#xff0c;本篇为…

Spring:面试八股

文章目录 参考Spring模块CoreContainerAOP 参考 JavaGuide Spring模块 CoreContainer Spring框架的核心模块&#xff0c;主要提供IoC依赖注入功能的支持。内含四个子模块&#xff1a; Core&#xff1a;基本的核心工具类。Beans&#xff1a;提供对bean的创建、配置、管理功能…

第十三届蓝桥杯省赛真题 Java B 组【原卷】

文章目录 发现宝藏【考生须知】试题 A: 星期计算试题 B: 山试题 C: 字符统计试题 D: 最少刷题数试题 E \mathrm{E} E : 求阶乘试题 F : \mathrm{F}: F: 最大子矩阵试题 G: 数组切分试题 H: 回忆迷宫试题 I: 红绿灯试题 J 拉箱子 发现宝藏 前些天发现了一个巨牛的人工智能学习…

C++ 侯捷 程序设计(Ⅱ)兼谈对象模型 笔记

Conversion function 转换函数 侯捷老师使用分数 Fraction举例&#xff0c;分数理应可以被看作是小数 提供了Fraction类对象一个转换为double的方法&#xff0c;当碰到需要转换为double的情况下&#xff0c;会调用该方法。 黄色的就是转换函数&#xff0c;没有return type&am…

【免费】基于扩展(EKF)和无迹卡尔曼滤波(UKF)的电力系统动态状态估计

目录 1 主要内容 2 部分代码 3 程序结果 4 下载链接 1 主要内容 该程序对应文章《Power System Dynamic State Estimation Using Extended and Unscented Kalman Filters》&#xff0c;电力系统状态的准确估计对于提高电力系统的可靠性、弹性、安全性和稳定性具有重要意义&a…

RIPGeo代码理解(五)utils.py( 辅助函数)第一部分

​ 代码链接:RIPGeo代码实现 ├── lib # 包含模型(model)实现文件 │ |── layers.py # 注意力机制的代码。 │ |── model.py # TrustGeo的核心源代码。 │ |── sublayers.py # layer.py的支持文件。 │ |── utils.p…

详解Python的函数嵌套

Python语言允许在定义函数的时候&#xff0c;其函数体内又包含另外一个函数的完整定义&#xff0c;这就是我们通常所说的嵌套定义。 实例1&#xff1a; def OutFun(): #定义函数OutFun()&#xff0c;m3 #定义变量m3;def InFun(): #在OutFun内定义函…

python学生作业管理系统flask-django-nodejs-php

课题主要分为三大模块&#xff1a;即管理员模块和学生、教师模块&#xff0c;主要功能包括&#xff1a;学生、教师、作业信息、学习模块、教学评价、学习情况等&#xff1b; 关键词&#xff1a;学生作业管理系统&#xff1b;作业信息 目录 摘 要 I Abstrac II 目录 III 1绪论 1…

jmeter接口导入方式

curl直接导入 1、操作页面后&#xff0c;F12查看接口&#xff0c;右击接口-copy-copy as cURL 2、jmeter 工具-import from cURL&#xff0c;粘贴上面复制的curl 根据接口文档导入 1、接口文档示例如下&#xff1a; Path&#xff1a; /api/jobs/xps/exec Method&#xf…

图像几何变换(仿射变换和透视变换...)及python-opencv实现

文章目录 图像变换类型仿射变换透视变换python-opencv实现参考文献 图像变换类型 图像几何变换主要包括以下几种类型&#xff1a; 平移&#xff08;Translation&#xff09;&#xff1a;将图像在水平或垂直方向上移动&#xff0c;不改变图像的尺寸和形状。缩放&#xff08;Sca…

理解静态库、动态库加载

个人主页&#xff1a;Lei宝啊 愿所有美好如期而遇 系统角度理解 我们先来谈谈进程地址空间&#xff0c;当我们将一个可执行程序跑起来的时候&#xff0c;操作系统首先会在内存中创建出task_struct&#xff0c;也就是进程控制块&#xff0c;然后将可执行程序的代码和数据加载…

整数和浮点数在内存中存储

整数在内存中的存储 整数的2进制表⽰⽅法有三种&#xff0c;即原码、反码和补码。 对于整形来说&#xff0c;数据存放内存中的其实是补码。 在计算机系统中&#xff0c;数值一律用补码来表示和存储。原因是&#xff0c;使用补码&#xff0c;可以使符号位和数值域统一处理&am…

ARMday7

VID_20240322_203313 1.思维导图 2.main.c #include"key_inc.h" //封装延时函数 void delay(int ms) {int i,j;for(i0;i<ms;i){for(j0;j<2000;j){}} } int main() {//按键中断的初始化key1_it_config();key2_it_config();key3_it_config();while(1){printf(&q…