深入理解Linux下的任务管理,守护进程

文章目录

  • 1. 任务管理
    • 1.1 进程的基本概念
      • ps 命令字段含义
        • ps aux
        • ps axj
    • 1.2 进程组,作业,会话概念解释
      • 1. 进程组(Process Group)
      • 2. 作业(Job)
        • 2.1 jobs命令
          • 状态标记 ([], -, +): 在方括号中的数字是作业的编号。方括号外的符号表示作业的状态。
          • 作业编号 (Job ID): 方括号中的数字是作业的编号。你可以使用这个编号来操作或引用特定的作业。
          • 作业状态 (Job Status): 表示作业的当前状态。
        • 2.2操作作业
          • 任务的挂起与恢复
      • 3. 会话(Session)
    • 小结
  • 2. 守护进程
    • 2.1 什么是守护进程
    • 2.2 查看守护进程
    • 2.2 创建守护进程
    • 2.3 示例:日志守护进程

导言

在Linux操作系统中,任务管理和守护进程是系统运维和开发中非常重要的方面。本篇博客将深入探讨Linux下任务管理和守护进程的概念、使用方法以及一些实际的例子。

1. 任务管理

1.1 进程的基本概念

在Linux中,每个运行的程序都是一个进程。每个进程都有一个唯一的进程标识符(PID),并且可以属于一个父进程。通过ps命令可以查看当前系统中运行的进程。

ps 命令字段含义

ps 命令用于查看系统中的进程信息。以下是常见的字段及其含义:

ps aux
$ ps aux
USER       PID  %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.1  12345  6789 ?        Ss   Dec01   0:05 /sbin/init
john        22  1.0  0.5 123456 54321 pts/0    R+   10:30   0:10 ./my_program
字段名称含义
USER (UID):进程属于哪个用户。
PID:进程ID,唯一标识一个进程。
USER (UID):进程属于哪个用户。
%CPU:进程占用的CPU使用率。
%MEM:进程占用的内存使用率。
RSS (resident set size):进程占用的物理内存大小(以KB为单位)。
TTY:进程关联的终端。
STAT (process status):进程状态,如R(运行)、S(睡眠)、Z(僵尸)等。
TIME:进程累计占用CPU的时间。
COMMAND:启动进程的命令。
ps axj
$ ps axjPID  PPID  PGID   SID TTY      STAT   TIME COMMAND1     0     1     1 ?        Ss     0:03 /usr/lib/systemd/systemd2     0     0     0 ?        S      0:00 [kthreadd]3     2     0     0 ?        I<     0:00 [rcu_gp]...

对比ps aux不同字段含义

字段名称含义
PPID (Parent Process ID):父进程ID,标识创建当前进程的父进程。
PGID (Process Group ID):进程组ID,标识进程所属的进程组。
SID (Session ID):会话ID,标识进程所属的会话。
TTY (Controlling Terminal):控制终端,表示与进程关联的终端设备。

1.2 进程组,作业,会话概念解释

我们以一个示例演示

首先创建四个进程,process_1,porcess_2,process_3,porcess_4
在这里插入图片描述

1. 进程组(Process Group)

概念:
进程组是一个或多个相关进程的集合,它们共享同一个进程组ID(PGID)。进程组的一个典型应用是将一组相关的进程组织在一起,以便能够方便地对它们进行管理。

process_1,porcess_2放在后台运行
process_3,porcess_4放在前台运行
开启另一终端(shell),用命令行ps axj | head -1 && ps axj | grep process | grep -v grep查看前台进程和后台进程

在这里插入图片描述

此时杀掉进程也是按照进程组进行的

在这里插入图片描述
当需要杀掉一个或多个进程时,通常会按照进程组进行操作。可以使用kill命令发送信号给进程和进程组。通过kill -n -pgid可以将信号n发送到进程组pgid中的所有进程。也可以使用kill -n pid将信号n发送到指定pid进程。

需要注意的是,如果一个进程启动了子进程,只杀死父进程,子进程仍在运行,因此仍消耗资源。为了防止这些“僵尸进程”,应确保在杀死父进程之前,先杀死其所有的子进程。

2. 作业(Job)

概念:
作业是一个或多个相关联的进程组的集合。通常,作业表示由一个用户发起的命令或脚本。

2.1 jobs命令

jobs 命令用于显示当前 shell 中正在运行的作业(jobs)列表。在一个 shell 会话中,你可能会同时运行多个作业,其中一些可能在后台运行。
在这里插入图片描述

状态标记 ([], -, +): 在方括号中的数字是作业的编号。方括号外的符号表示作业的状态。
  • []: 代表当前正在前台运行的作业。
  • -: 代表当前正在后台运行的作业,并且有一个或多个作业处于停止状态。
  • +: 代表最近放入后台的作业。
作业编号 (Job ID): 方括号中的数字是作业的编号。你可以使用这个编号来操作或引用特定的作业。
作业状态 (Job Status): 表示作业的当前状态。
  • Running: 作业正在前台或后台运行。
  • Stopped: 作业已被停止,通常是通过 Ctrl+Z 发送 SIGTSTP 信号。
    命令 (Command): 是启动作业的命令及其参数。
$ jobs
[1]+  Running                 command1 &
[2]-  Stopped                 command2
[3]+  Running                 command3

在上面的例子中:

command1 正在后台运行,并且是最近放入后台的作业。
command2 已经停止。
command3 正在后台运行。
2.2操作作业

如果你想操作这些作业,你可以使用 fg(将作业放到前台运行)或 bg(将作业放到后台运行)命令,以及 kill 命令来终止作业。例如:

$ fg %1   # 将作业1放到前台运行
$ bg %2   # 将作业2放到后台运行
$ kill %3 # 终止作业3
任务的挂起与恢复

使用Ctrl+Z可以将当前前台任务挂起,然后可以使用bg将其放入后台运行,或使用fg将其恢复到前台。

# 挂起当前任务
Ctrl+Z

在这里插入图片描述

# 将任务恢复到前台
fg %1

在这里插入图片描述

3. 会话(Session)

概念:
会话是一个或多个相关联的作业的集合。会话是由一个终端发起的一组作业的集合,可以包括多个进程组。

例子: 当用户登录到系统时,通常会创建一个新的会话。用户在终端中执行的所有作业都属于同一个会话。

# 创建新会话
ssh user@hostname

在这个例子中,用户通过SSH连接到远程主机,创建了一个新的会话。在这个会话中,用户可以启动不同的作业,每个作业可能包含一个或多个进程组。

小结

  • 进程组是相关进程的集合,共享同一个进程组ID。
  • 作业是一个或多个相关联的进程组的集合,通常表示一个用户发起的命令或脚本。
  • 会话是一个或多个相关联的作业的集合,由一个终端发起。

2. 守护进程

2.1 什么是守护进程

守护进程(Daemon)是在后台运行的系统进程,它们独立于用户会话并且没有直接的控制终端。它们通常在系统启动时启动,并在系统关闭时终止。守护进程通常用于执行系统级任务,如日志记录、网络服务等。

2.2 查看守护进程

ps aux | grep [守护进程名称]

过滤出数据库守护进程

在这里插入图片描述

字段名称含义
USER (UID):进程属于哪个用户。
PID:进程ID,唯一标识一个进程。
USER (UID):进程属于哪个用户。
%CPU:进程占用的CPU使用率。
%MEM:进程占用的内存使用率。
RSS (resident set size):进程占用的物理内存大小(以KB为单位)。
TTY:进程关联的终端。
STAT (process status):进程状态,如R(运行)、S(睡眠)、Z(僵尸)等。
TIME:进程累计占用CPU的时间。
COMMAND:启动进程的命令。

2.2 创建守护进程

一个守护进程通常通过编写一个程序来实现。以下是创建一个简单的守护进程的一般步骤:

  1. Fork 出一个子进程并结束父进程,使得子进程成为孤儿进程。
  2. 在子进程中调用 setsid(),创建一个新的会话,并成为该会话的组长,脱离终端控制。
  3. 再次 fork 一个子进程并结束父进程,以确保守护进程不是会话组长,这样它就不会重新获取控制终端。
  4. 修改工作目录,关闭文件描述符等,使得守护进程更独立和安全。
  5. 重定向标准输入、输出和错误流到/dev/null。

在这里插入图片描述

#include <fcntl.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>int main()
{// 1、设置文件掩码为0umask(0);// 2、fork后终止父进程,子进程创建新会话if (fork() > 0) {// fatherexit(0);}setsid();// 3、忽略SIGCHLD信号signal(SIGCHLD, SIG_IGN);// 4、再次fork,终止父进程,保持子进程不是会话首进程,从而保证后续不会再和其他终端相关联// (不是必须的,防御性编程)if (fork() > 0) {// fatherexit(0);}// 5、更改工作目录为根目录(可选的选项)chdir("/");// 6、将标准输入、标准输出、标准错误重定向到/dev/null(可选的选项)close(0);int fd = open("/dev/null", O_RDWR);dup2(fd, 1);dup2(fd, 2);while (1);return 0;
}

在这里插入图片描述

状态说明
Ss:会话领导。
Ss+:会话领导,且在前台运行。
S:睡眠状态
  1. 运行后发现该进程的TPGID为-1,TTY显示的是?,也就意味着该进程已经与终端去关联了。

  2. 该进程的PID与其PGID和SID是不同的,也就是说该进程既不是组长进程也不是会话首进程。

  3. 该进程的SID与bash进程的SID是不同的,即它们不属于同一个会话。

  4. 该进程的工作目录已经成功改为了根目录。
    在这里插入图片描述

  5. 该进程的标准输入、标准输出以及标准错误也成功重定向到了/dev/null,在这里插入图片描述

2.3 示例:日志守护进程

在这里插入图片描述

  • 在本目录下创建一个日志守护进程,用于捕捉其他进程产生的错误
  • 再在本目录下写一段简单的c代码,这段c代码将产生野指针问题
  • 将这段代码运行后的错误信息通过守护进程输出到到守护进程创建的log文件中
#include <stdio.h>  
#include <stdlib.h>  
#include <unistd.h>  
#include <sys/types.h>  
#include <sys/stat.h>  
#include <sys/wait.h>
#include <fcntl.h>  
#include <signal.h>  
#include <string.h>#define MAX_ERROR_LENGTH 256  int main() {  pid_t pid;  int log_fd;  char error_buffer[MAX_ERROR_LENGTH];  char log_file[MAX_ERROR_LENGTH];  // 创建子进程  pid = fork();  // 子进程退出  if (pid == 0) {  exit(EXIT_SUCCESS);  }  // 父进程创建新会话  if (setsid() < 0) {  perror("setsid failed");  exit(EXIT_FAILURE);  }  // 打开日志文件  sprintf(log_file, "error_%d.log", getpid()); // 根据进程ID创建日志文件名  log_fd = open(log_file, O_CREAT | O_WRONLY | O_APPEND, S_IRUSR | S_IWUSR);  if (log_fd < 0) {  perror("open failed");  exit(EXIT_FAILURE);  }  // 重定向标准错误输出到日志文件  if (dup2(log_fd, STDERR_FILENO) < 0) {  perror("dup2 failed");  exit(EXIT_FAILURE);  }  // 关闭日志文件描述符,因为dup2已经复制了一个副本,所以不需要再使用它了。  close(log_fd);  // 捕捉其他进程产生的错误信息,并打印到日志文件中。  signal(SIGPIPE, SIG_IGN); // 忽略SIGPIPE信号,防止子进程在写日志文件时退出。  while (1) { // 循环等待错误信息打印。  // 这里通过wait()函数等待子进程结束,并获取其退出状态和错误信息。  int status;  wait(&status); // 等待子进程结束。  if (WIFEXITED(status)) { // 如果子进程正常退出,获取其退出状态。  printf("Child process exited with status %d.\n", WEXITSTATUS(status)); // 打印退出状态。  } else if (WIFSIGNALED(status)) { // 如果子进程被信号杀死,获取其被杀死的信号。  printf("Child process killed by signal %d.\n", WTERMSIG(status)); // 打印被杀死的信号。  } else { // 其他情况。  printf("Unknown exit status for child process.\n"); // 打印未知的退出状态。  }  // 清空错误信息缓冲区。  memset(error_buffer, 0, MAX_ERROR_LENGTH);  // 从子进程的stdout和stderr中读取错误信息。  read(STDIN_FILENO, error_buffer, MAX_ERROR_LENGTH - 1); // 从stdin中读取错误信息。  read(STDERR_FILENO, error_buffer + strlen(error_buffer), MAX_ERROR_LENGTH - strlen(error_buffer) - 1); // 从stderr中读取错误信息。  // 将错误信息写入日志文件中。  write(log_fd, error_buffer, strlen(error_buffer)); // 将错误信息写入日志文件。  fflush(stdout); // 将输出刷新到标准输出缓冲区,防止在程序退出后丢失输出信息。  }  return 0; // 程序正常退出,返回0表示成功结束进程。  
}
#include <stdio.h>
#include <stdlib.h>void cause_wild_pointer() {int *ptr = NULL;*ptr = 42;  // 这里会引发野指针问题
}int main() {cause_wild_pointer();return 0;
}

在这里插入图片描述
这里可以看到守护进程完成了错误的捕捉(TODO

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

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

相关文章

计算机组成原理第4章-(Cache)【下】

高速缓冲存储器 我们首先要搞清楚&#xff0c;为什么要引入高速缓冲存储器呢&#xff1f; 首先&#xff0c;在多体并行存储系统中&#xff0c;I/O设备对于主存的使用权大于CPU对于主存的使用权&#xff0c;这也就造 成了CPU可能在一段时间内不能使用主存&#xff0c;从而浪费…

045.Python包和模块_初识包和模块

我 的 个 人 主 页&#xff1a;&#x1f449;&#x1f449; 失心疯的个人主页 &#x1f448;&#x1f448; 入 门 教 程 推 荐 &#xff1a;&#x1f449;&#x1f449; Python零基础入门教程合集 &#x1f448;&#x1f448; 虚 拟 环 境 搭 建 &#xff1a;&#x1f449;&…

【工具使用-有道云笔记】如何在有道云笔记中插入目录

一&#xff0c;简介 本文主要介绍如何在有道云笔记中插入目录&#xff0c;方便后续笔记的查看&#xff0c;供参考。 二&#xff0c;具体步骤 分为两个步骤&#xff1a;1&#xff0c;设置标题格式&#xff1b;2&#xff0c;插入标题。非常简单~ 2.1 设置标题格式 鼠标停在标…

解决PyCharm打开出现Cannot load settings from file错误

解决PyCharm打开出现Cannot load settings from file错误 背景 pycharm打开过的项目&#xff0c;关闭项目或者IDE后再次打开&#xff0c;右下角会出现cannot load settings from file&#xff0c;如图1所示。 图1 随后&#xff0c;我浏览许多国内外的论坛&#xff0c;帖子&…

linux中playbook的控制语句

本章主要介绍 playbook中的控制语句。 使用 when 判断语句 block-rescue判断 循环语句 一个play中可以包含多个task&#xff0c;如果不想所有的task全部执行&#xff0c;可以设置只有满足某个 条件才执行这个task&#xff0c;不满足条件则不执行此task。本章主要讲解when 和 …

web前端html笔记2

新增状态标签<meter><progress> <meter> 属性 值 描述 high 数值 规定高值 low 数值 规定低值 max 数值 规定最大值 min 数值 规定最小值 optimum 数值 规定最优值 value 数值 规定当前值 <body> <meter high"50" …

三个月真的可以学会自动化测试吗?不是骗局把?

三个月可以学会&#xff0c;但是想要达到精通还需更多的时间。 目前行业内对于自动化测试的还是存在很多误区的&#xff0c;管理层为了自动化而去自动化&#xff0c;学习者学到最后只会用一些工具。 关于学习路线&#xff0c;我放在后面讲&#xff0c;在此之前必须先聊一下行…

Jmeter多种定时器实现方法解析

1、固定定时器&#xff08;Constant Timer&#xff09; 用法(场景)&#xff1a;更真实的模拟用户场景&#xff0c;需要设置等待时间&#xff0c;或是等待上一个请求的时间才执行&#xff0c;给 sampler 之间的思考时间 备注&#xff1a;如果需要每个步骤均延迟&#xff0c;则…

Postman报:400 Bad Request

● 使用Postman发送Post请求报400&#xff0c;入参为JSON&#xff1b; 二、分析 1、Postman请求并没有请求到后台Api&#xff08;由于语法错误&#xff0c;服务器无法理解请求&#xff09;&#xff1b; 2、入参出错范围&#xff1a;cookie、header、body、form-data、x-www-f…

OpenCV-Python(18):图像梯度

目录 背景介绍及应用 学习目标 原理 Sobel算子和Scharr算子 Laplacian 算子 代码示例 重要提醒 背景介绍及应用 图像的梯度是指图像中每个像素点的强度变化情况。计算图像的梯度可以帮助我们了解图像中物体的边界和纹理等信息。梯度在计算机视觉和图像处理领域有着广泛…

Android开发——添加图片

1、首先选择一张需要的图片&#xff0c;通过左侧的Resource Manage选择“”并选择Import Drawables 选择一张图片 并调整以下两个内容 这两个内容的作用借用谷歌官方的Android开发教程的内容&#xff1a; *Android 设备具有不同的屏幕尺寸&#xff08;手机、平板电脑和电视等…

Keil5软件仿真 定时器互补通道 波形输出(Logic Analyzer)

步骤一&#xff1a;管脚配置确认。 ①配置定时器的管脚模式为复用推挽输出模式&#xff08;GPIO_MODE_AF_PP&#xff09;&#xff01;&#xff01;&#xff01;&#xff0c;注意&#xff1a;复用开漏模式软件仿真时无波形。 步骤二&#xff1a;编译程序。 ①点击编译按钮。 …