Linux kill正在执行的后台任务 kill进程组使用详解

2025-11-11 22:50

本文主要是介绍Linux kill正在执行的后台任务 kill进程组使用详解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《Linuxkill正在执行的后台任务kill进程组使用详解》文章介绍了两个脚本的功能和区别,以及执行这些脚本时遇到的进程管理问题,通过查看进程树、使用`kill`命令和`lsof`命令,分析了子...

零. 用到的命令

# 查看当前session会话正在后台执行的job
jobs -l
# 查看当前脚本的进程信息
ps -ef | grep -a "[m]y_script_child_process.sh"
ps -o pid,pgid,ppid,cmd --forest
# 以树状图的形式表示指定的进程号
pstree -p 659
# 杀死659的进程号
kill -TERM 659
# 杀死659的进程组
kill -TERM -659
# 查看bigfile.csv被哪个进程占用
lsof bigfile.csv
# 用setsid启动新会话,确保子进程和父进程用同一个进程组
setsid nohup bash my_script_child_process.sh > /dev/null 2>&1 &

一. 待执行的脚本

有如下2个脚本

apluser@FengYeHong-HP:0727$ ls -l
-rw-r--r-- 1 apluser apluser 194 Jul 27 11:01 my_script_child_process.sh
-rw-r--r-- 1 apluser apluser 180 Jul 27 11:42 my_script_no_child_process.sh

两个脚本的作用相同,都是要花时间生成一个csv文件之后,然后睡眠10秒,然后打印内容到控制台

  • my_script_child_process.sh:在脚本中使用了(),在脚本启动时,会创建一个子进程
  • my_script_no_child_process.sh:没有使用(),在脚本启动时,不会创建一个子进程
apluser@FengYeHong-HP:0727$ cat my_script_child_process.sh
(
  echo "No,姓名,auid,地址"
  seq 1 40000000 | awk 'BEGIN{OFS=","} {
      printf "%d,user_name_%09d,auid_%09d,地球%d\n", $1, $1, $1, $1
  }'
) > bigfile.csv
sleep 10
echo "执行完成"
apluser@FengYeHong-HP:0727$ cat my_script_no_child_process.sh
echo "No,姓名,auid,地址"
seq 1 40000000 | awk 'BEGIN{OFS=","} {
  printf "%d,user_name_%09d,auid_%09d,地球%d\n", $1, $1, $1, $1
}' > bigfile.csv
sleep 10
echo "执行完成"

二. 执行含子进程的脚本,并kill

2.1 进程查看

通过&的方式在后台执行脚本,并使用jobs -l查看正在执行的job的状态

  • 可以看到该脚本目前处于Running的状态
  • 脚本对应的进程号为:659
apluser@FengYeHong-HP:0727$ bash my_script_child_process.sh &
[1] 659
apluser@FengYeHong-HP:0727$ jobs -l
[1]+   659 Running                 bash my_script_child_process.sh &

通过ps -ef的方式查看脚本的进程,因为使用()在脚本内部开启了子进程,所以我们会发现有2个关联进程

apluser@FengYeHong-HP:0727$ ps -ef | grep -a "[m]y_script_child_process.sh"
# UID(用户)  PID(进程 ID) PPID(父进程 ID) CPU 占用率  启动时间  TTY(终端) 占用 CPU 时间  启动的命令
apluser       659            517              0          15:18     pts/0      00:00:00      bash my_script_child_process.sh
apluser       660            659              0          15:18     pts/0      00:00:00      bash my_script_child_process.sh

查看进程的父子层级关系(树状结构)以及每个进程的 ID 信息

  • pid:进程 ID
  • pgid:进程组 ID
  • ppid:父进程 ID
  • cmd:启动命令
  • --forest:以 树状结构 显示进程的父子层级关系
apluser@FengYeHong-HP:0727$ ps -o pid,pgid,ppid,cmd --forest
  PID  PGID  PPID CMD
  517   517   516 -bash
  659   659   517  \_ bash my_script_child_process.sh
  660   659   659  |   \_ bash my_script_child_process.sh
  661   659   660  |       \_ seq 1 40000000
  662   659   660  |       \_ awk BEGIN{OFS=","} {       printf "%d,user_name_%09d,auid_%09d,地球%d\n", $1, $1, $1, $1   }
  665   665   517  \_ ps -o pid,pgid,ppid,cmd --forest

图形化地查看进程树(659是主进程)

659的主进程中,创建了660的子进程

通过()创建的660的子进程内部

  • 又通过awk和seq命令分别创建了661662的进javascript
  • 这些子进程和主进程都属于PGID659的进程组
apluser@FengYeHong-HP:0727$ pstree -p 659
bash(659)───bash(660)─┬─awk(662)
                      └─seq(661)

2.2 遇到的问题

在我们执行kill命令之前,生成的文件大小如下

apluser@FengYeHong-HP:0727$ ls -l bigfile.csv
-rw-r--r-- 1 apluser apluser 732053526 Jul 27 15:19 bigfile.csv

通过kill命令杀死PID为659的主进程

apluser@FengYeHong-HP:0727$ kill -TERM 659
apluser@FengYeHong-HP:0727$
[1]+  Terminated              bash my_script_child_process.sh

kill命令执行之后,再次观察文件,发现文件并没有停止增长,反而继续增大

apluser@FengYeHong-HP:0727$ ls -l bigfile.csv
-rw-r--r-- 1 apluser apluser 960819222 Jul 27 15:19 bigfile.csv

此时通过lsof命令查看bigfile.csv文件被哪些进程所占用,得到如下结果

  • 可以看到主线程被kill掉之后,子线程依然活跃,所以文件 bigfile.csv 仍然在持续写入
  • 在主线程被kill掉之后,awk 的进程(例如 PID 662)已经独立运行,此时的进程被称为孤儿进程
  • 除非我们显式地kill掉,否则孤儿进程会一直执行到结束
apluser@FengYeHong-HP:0727$ lsof bigfile.csv
COMMAND PID    USER       FD   TYPE  DEVICE   SIZE/OFF    NODE    NAME
bash    660    apluser    1w   REG   8,48     1222258710  42569   bigfile.csv
awk     662    apluser    1w   REG   8,48     1222266902  42569   bigfile.csv

2.3 解决

2.3.1 kill进程的时候,kill进程组

我们在【2.1 进程查看】的时候,查看过脚本相关的进程的详细信息

apluser@FengYeHong-HP:0727$ ps -o pid,pgid,ppid,cmd --forest
  PID  PGID  PPID CMD
  517   517   516 -bash
  659   659   517  \_ bash my_script_child_process.sh
  660   659   659  |   \_ bash my_script_child_process.sh
  661   659   660  |       \_ seq 1 40000000
  662   659   660  |       \_ awk BEGIN{OFS=","} {       printf "%d,user_name_%09d,auid_%09d,地球%d\n", $1, $1, $1, $1   }
  665   665   517  \_ ps -o pid,pgid,ppid,cmd --forest

下面的这行命令,kill的是PID为659的主进程,并不会对子进程产生影响

kill -TERM 659

要想同时杀死主进程的关联子进程,kill的对象不是PID,而应该是PGID,即进程组,只要所有的子进程和主进程同属于一个进程组,kill掉进程组就可杀死主进程的关联子进程。

# 注意,使用【-】来指定kill的是进程组
kill -TERM -659

2.3.2 不使用() 或者 使用{}

不使用()

apluser@FengYeHong-HP:0727$ cat my_script_no_child_process.sh
echo "No,姓名,auid,地址"
seq 1 40000000 | awk 'BEGIN{OFS=","} {
  printf "%d,user_name_%09d,auid_%09d,地球%d\n", $1, $1, $1, $1
}' > bigfile.csv
sleep 10
echo "执行完成"
apluser@FengYeHong-HP:0727$ bash my_script_no_child_process.sh &
[1] 687
  • 可以看到,只有一个线程存在
apluser@FengYeHong-HP:0727$ ps -ef | grep -a "[m]y_script_no_child_process.sh"
apluser    687   517  0 16:21 pts/0    00:00:00 bash my_script_no_child_process.sh

使用{}

apluser@FengYeHong-HP:0727$ cat my_script_no_child_process.sh
{
    echo "No,姓名,auid,地址";
    seq 1 40000000 | awk 'BEGIN{OFS=","} {
        printf "%d,user_name_%09d,auid_%09d,地球%d\n", $1, $1, $1, $1
    }'
} > bigfile.csv
sleep 10
echo "执行完成"
apluser@FengYeHong-HP:0727$ bash my_script_no_child_process.sh &
[1] 702
  • 可以看到,只有一个线程存在
apluser@FengYeHong-HP:0727$ ps -ef | grep -a "[m]y_script_no_child_process.sh"
apluser    702   517  0 16:33 pts/0    00:00:00 bash my_script_no_child_process.sh

2.3.3 使用setsid启动新会话

通过bash my_script_child_process.sh &启动的脚本,其中的子进程和父进China编程程有可能不在同一个进程组里面,可通过setsid解决该问题。

setsid 的作用:

  • 启动一个新进程,并创建新的会话(SID)和新的进程组(PGID),新进程成为这个会话的首进程(session leader)。
  • 然后这个新进程(比如 PID 776)执行 bash my_script_child_process.sh,这就成为了脚本的顶层的bash进程。
  • 所有其fork 出来的子进程(包括 seq、awk 等),默认都会继承这个 PGID 和 SID,除非子进程中自己调用了 setsid 或 setpgid 修改了所属组/会话。

启动脚本

  • 注意:启动脚本时出现的808进程号是setsid命令本身的进程号
  • 808进程号并不是my_script_child_process.sh脚本的进程号
apluser@FengYeHong-HP:0727$ cat my_script_child_process.sh
(
  echo "No,姓名,auid,地址"
  seq 1 40000000 | awk 'BEGIN{OFS=","} {
      printf "%d,user_name_%09d,auid_%09d,地球%d\n", $1, $1, $1, $1
  }'
) > bigfile.csv
sleep 10
echo "执行完成"
apluser@FengYeHong-HP:0727$ setsid nohup bash my_script_child_process.sh > /dev/null 2>&1 &
[1] 808
apluser@FengYeHong-HP:0727$
[1]+  Done                  setsid nohup bash my_script_child_process.sh > /dev/null 2>&1
apluser@FengYeHong-HP:0727$

查看my_script_child_process.sh脚本的进程号

apluser@FengYeHong-HP:0727$ ps -ef | grep -a "[m]y_script_child_process.sh"
apluser    809   516  0 17:03 ?        00:00:00 bash my_script_child_process.sh
apluser    810   809  0 17:03 ?        00:00:00 bash my_script_child_process.sh

kill脚本进程组前后的文件变化

# kill之前查看问价大小
apluser@FengYeHong-HP:0727$ ls -l bigfile.csv
-rw-r--r-- 1 apluser apluser 352866326 Jul 27 17:03 bigfile.csv
# kill进程组
apluser@FengYeHong-HP:0727$ kill -TERM -809
# kill之后查看文件大小,没有再发生变化
apluser@FengYeHong-HP:0727$ ls -l
-rw-r--r-- 1 apluser apluser 692047894 Jul 27 17:03 bigfile.csv
apluser@FengYeHong-pythonHP:0727$ ls -l
-rw-r--r-- 1 apluser apluser 692047894 Jul 27 17:03 bigfile.csv

三. kill的相关知识

3.1kill -TERM -113和kill -TERM 113的不同之处

  • kill -TERM 113

只对 一个进程(PID = 113)发出终止信号。

它不会影响这个进程的任何子进程,除非你手动对每个子进程都发送信号。

这是最常用、最基本的 kill 形式。

  • kill -TERM -113

前面带了 -,表示你要杀的是 进程组(process group)。

所以这个命令会向 进程组 ID 为 113 的所有进程(包括前台/后台作业中的多个子进程)发送 SIGTERM。

命令意思
kill -TERM 113向 PID 为 113 的单个进程发送 SIGTERM
kill -TERM -113向 进程组 ID 为 113 的整个进程组发送 SIGTERM

3.2kill -TERM -113和kill -9 -113的区别

  • kill -TERM -113:给整个进程组发送终止请求(温和)

这样可以让进程有机会做清理工作(比如关闭文件、释放资源)

kill -9 -113:给整个进程组发送立即终止(粗暴),发出的是 SIGKILL,无法被捕获或http://www.chinasem.cn忽略,进程会被直接杀死,可能导致:

  • 文件写到一半
  • 临时资源未释放
  • 数据库等状态不一致
命令信号类型行为可被拦截或清理?
kill -TERM -113SIGTERM (15)请求正常终止整个进程组✅ 是的
kill -9 -113SIGKILL (9)强制立即终止整个进程组❌ 否

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持China编程(www.chinasem.cn)。

这篇关于Linux kill正在执行的后台任务 kill进程组使用详解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1156131

相关文章

Java中流式并行操作parallelStream的原理和使用方法

《Java中流式并行操作parallelStream的原理和使用方法》本文详细介绍了Java中的并行流(parallelStream)的原理、正确使用方法以及在实际业务中的应用案例,并指出在使用并行流... 目录Java中流式并行操作parallelStream0. 问题的产生1. 什么是parallelS

MySQL数据库双机热备的配置方法详解

《MySQL数据库双机热备的配置方法详解》在企业级应用中,数据库的高可用性和数据的安全性是至关重要的,MySQL作为最流行的开源关系型数据库管理系统之一,提供了多种方式来实现高可用性,其中双机热备(M... 目录1. 环境准备1.1 安装mysql1.2 配置MySQL1.2.1 主服务器配置1.2.2 从

Linux join命令的使用及说明

《Linuxjoin命令的使用及说明》`join`命令用于在Linux中按字段将两个文件进行连接,类似于SQL的JOIN,它需要两个文件按用于匹配的字段排序,并且第一个文件的换行符必须是LF,`jo... 目录一. 基本语法二. 数据准备三. 指定文件的连接key四.-a输出指定文件的所有行五.-o指定输出

Linux jq命令的使用解读

《Linuxjq命令的使用解读》jq是一个强大的命令行工具,用于处理JSON数据,它可以用来查看、过滤、修改、格式化JSON数据,通过使用各种选项和过滤器,可以实现复杂的JSON处理任务... 目录一. 简介二. 选项2.1.2.2-c2.3-r2.4-R三. 字段提取3.1 普通字段3.2 数组字段四.

MyBatis常用XML语法详解

《MyBatis常用XML语法详解》文章介绍了MyBatis常用XML语法,包括结果映射、查询语句、插入语句、更新语句、删除语句、动态SQL标签以及ehcache.xml文件的使用,感兴趣的朋友跟随小... 目录1、定义结果映射2、查询语句3、插入语句4、更新语句5、删除语句6、动态 SQL 标签7、ehc

详解SpringBoot+Ehcache使用示例

《详解SpringBoot+Ehcache使用示例》本文介绍了SpringBoot中配置Ehcache、自定义get/set方式,并实际使用缓存的过程,文中通过示例代码介绍的非常详细,对大家的学习或者... 目录摘要概念内存与磁盘持久化存储:配置灵活性:编码示例引入依赖:配置ehcache.XML文件:配置

Java 虚拟线程的创建与使用深度解析

《Java虚拟线程的创建与使用深度解析》虚拟线程是Java19中以预览特性形式引入,Java21起正式发布的轻量级线程,本文给大家介绍Java虚拟线程的创建与使用,感兴趣的朋友一起看看吧... 目录一、虚拟线程简介1.1 什么是虚拟线程?1.2 为什么需要虚拟线程?二、虚拟线程与平台线程对比代码对比示例:三

从基础到高级详解Go语言中错误处理的实践指南

《从基础到高级详解Go语言中错误处理的实践指南》Go语言采用了一种独特而明确的错误处理哲学,与其他主流编程语言形成鲜明对比,本文将为大家详细介绍Go语言中错误处理详细方法,希望对大家有所帮助... 目录1 Go 错误处理哲学与核心机制1.1 错误接口设计1.2 错误与异常的区别2 错误创建与检查2.1 基础

k8s按需创建PV和使用PVC详解

《k8s按需创建PV和使用PVC详解》Kubernetes中,PV和PVC用于管理持久存储,StorageClass实现动态PV分配,PVC声明存储需求并绑定PV,通过kubectl验证状态,注意回收... 目录1.按需创建 PV(使用 StorageClass)创建 StorageClass2.创建 PV

Python版本信息获取方法详解与实战

《Python版本信息获取方法详解与实战》在Python开发中,获取Python版本号是调试、兼容性检查和版本控制的重要基础操作,本文详细介绍了如何使用sys和platform模块获取Python的主... 目录1. python版本号获取基础2. 使用sys模块获取版本信息2.1 sys模块概述2.1.1