本文主要是介绍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:进程 IDpgid:进程组 IDppid:父进程 IDcmd:启动命令--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命令分别创建了661和662的进javascript程
- 这些子进程和主进程都属于
PGID为659的进程组
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 -113 | SIGTERM (15) | 请求正常终止整个进程组 | ✅ 是的 |
| kill -9 -113 | SIGKILL (9) | 强制立即终止整个进程组 | ❌ 否 |
总结
这篇关于Linux kill正在执行的后台任务 kill进程组使用详解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!