linux 找出谁杀了进程

article/2025/10/14 2:44:56

目录

Linux Signal

到底是什么信号

OOM

谁发的信号

systemtap

audit

案例与总结


 服务端的程序,一般都希望比较长时间的运行,比如7*24小时。不过,程序也需要更新,比如修Bug,比如增加新功能,比如修复增加新功能引入的bug。

  一个大型系统一般包含多个进程,同一个服务也可能是有多个进程组成,那么可以将这组进程逐步更新:先让一部分进程停止提供服务,等待已有的请求都完毕之后,重启这些服务,然后再更新替他进程。

  即使,我们需要关闭所有服务,也需要优雅(graceful)地停止这些进程。所谓的优雅,就是保证已有的请求都能处理完,需要持久化的状态、数据都保存成功,然后再结束进程,一般来说,可以通过发送信号或者通过哨兵(sentinel)来结束。

  只要是预期内的进程结束,那么都是ok的。而预期之外的进程结束往往令程序员抓狂,线上服务器的问题往往意味着分分钟几位数的损失、KPI、年终奖......想想就很恐怖。

Linux Signal

  一个进程的异常终止,通常有两种情况,一种是crash,另一种是被kill掉了。

  crash是指程序出现了自己无法解决的异常情况,只能终止,比如Python语言抛出了一个未被捕获的异常,就会结束程序。对于C、C++,最有名的就是段错误(segmentation fault),如果在Linux下面,那么会生成coredump,程序员通过gdb(有可能)可以分析出crash的原因。当然,要生成coredump也是需要正确的设置,可以通过ulimit(ulimit -c)查看或者设置。

  而进程被kill掉,就是其他进程给目标进程发送了信号(signal),当然也可以是自己给自己发的信号,而目标进程没有正确处理这些信号,或者根本没有机会(权力)处理这些信号,那么目标进程就有可能会终止。

  信号是Unix-like系统进程间通信(IPC)的一种方式,这种通知是异步的,信号是一种软中断,操作系统会将目标进程的正常执行流程暂停,然后处理信号。如果目标进程注册了相应的信号处理函数(signal handler),那么就会调用这个signal handler,否则会执行默认的信号处理函数。

  不同的操作系统,支持的信号可能略有差异,可以使用kill -l 查看系统所有的信号。下面是Linux上常见的信号以及处理机制

  信号    值 处理动作 发出信号的原因 
  ---------------------------------------------------------------------- 
  SIGHUP  1   A   终端挂起或者控制进程终止 
  SIGINT   2   A   键盘中断(如break键被按下) 
  SIGQUIT   3   C   键盘的退出键被按下 
  SIGILL    4   C   非法指令 
  SIGABRT  6   C   由abort(3)发出的退出指令 
  SIGFPE    8   C   浮点异常 
  SIGKILL  9   AEF  Kill信号 
  SIGSEGV   11   C  无效的内存引用 
  SIGPIPE  13   A  管道破裂: 写一个没有读端口的管道 
  SIGALRM 14    A  由alarm(2)发出的信号 
  SIGTERM 15    A  终止信号 
  SIGUSR1  30,10,16 A  用户自定义信号1 
  SIGUSR2  31,12,17 A  用户自定义信号2 
  SIGCHLD  20,17,18 B    子进程结束信号 
  SIGCONT  19,18,25       进程继续(曾被停止的进程) 
  SIGSTOP  17,19,23 DEF 终止进程 
  SIGTSTP  18,20,24 D 控制终端(tty)上按下停止键 
  SIGTTIN   21,21,26 D 后台进程企图从控制终端读 
  SIGTTOU 22,22,27 D 后台进程企图从控制终端写 

  处理动作一项中的字母含义如下 

  A  缺省的动作是终止进程
  B  缺省的动作是忽略此信号
  C  缺省的动作是终止进程并进行内核映像转储(dump core)
  D  缺省的动作是停止进程
  E  信号不能被捕获
  F  信号不能被忽略

  如果默认处理动作是C(coredump),那么就会生成coredump,然后终止进程,在上一篇文章《啊,我的程序为啥卡住啦》中,提到用kill -11 pid 来终止、调试卡住的程序,这个11就是指信号SIGSEGV

  注意SIGKILL SIGSTOP这两个信号,既不可以被捕获,也不能被忽略,就是说收到这两个信号,程序就会不留痕迹地终止。

到底是什么信号

  从上面,我们可以看到,有很多信号都可以终止进程,如果我们没有针对某种信号指定处理函数,那么我们怎么知道进程是被哪一个进程kill掉了呢,那就是strace。

  我们以一段简单的Python代码为例:

# -*- coding: utf-8 -*-def func():while True:passif __name__ == '__main__':func()

  运行该代码: 

python run_forever.py &
[1] 1035

  可以看到pid是1035,我们使用strace查看该进程:$ strace -T -tt -e trace=all -p 1035, 然后另起终端kill这个Python进程: $kill -9 1035。在strace可以看到:

$ strace -T -tt -e trace=all -p 1035
Process 1035 attached
16:19:52.092202 +++ killed by SIGKILL +++

  也可以使用其他信号kill,比如,重新运行Python程序,进程为32599, kill -10 32599,那么strace的结果是

$strace -T -tt -e trace=all -p 32599
Process 32599 attached
16:09:20.264993 --- SIGUSR1 {si_signo=SIGUSR1, si_code=SI_USER, si_pid=29373, si_uid=3010} ---
16:09:20.265656 +++ killed by SIGUSR1 +++

  对比kill -9, kill -10多了一行信息,这是非常有用的信息,因为有了两个非常重要的字段,si_pid、 si_uid

pid_t si_pid; // sending process ID
pid_t si_uid; // Real user ID of sending process

  即si_pid说明是哪一个进程发送的信息,si_uid是哪一个用户发出的信息,使用id si_uid就能看出详细的用户信息。这样就直接定位了谁发送的信息。

  不过,很多时候,进程被干掉,都是使用了kill -9(SIGKILL),那么即使使用strace也只是知道被干掉了,也没法知道被谁干掉了。而且,除非一个程序经常被kill掉,否则很少有人有strace长期监控这个进程。

OOM

  关于进程收到了SIGKILL信号,有一种不得不提的情况,那就是OOM(out of memory),简单来说,就是当Linux系统内存不足,在大量使用swap之后,会kill掉内存占用最大的进程。这应该算操作系统系统自身的一种保护机制,以防更多的进程不能正常工作。关于OOM killer,网上有详尽的资料介绍,在这里只是简单看看现象。

  下面是一个简单的Python程序,该程序会无限的分配内存,直到内存不足:

# -*- coding: utf-8 -*-def func():l = [1]while True:l.append(l * 2)if __name__ == '__main__':func()

   运行该程序,然后用strace追踪,前面都是正常的内存分配相关的系统调用,直到最后

16:02:19.192409 mprotect(0x7f4aa886b000, 552960, PROT_READ|PROT_WRITE) = 0 <0.000014>

16:02:19.744000 mmap(NULL, 552960, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = -1 ENOMEM (Cannot allocate memory) <0.000108>

16:02:19.755545 mprotect(0x7f4aa88f2000, 552960, PROT_READ|PROT_WRITE) = 0 <0.000019>

16:02:23.404805 mmap(NULL, 552960, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0

16:02:25.325272 +++ killed by SIGKILL +++

[1]+ Killed python will_be_killed.py

   stackoverflow上who-killed-my-process-and-why一文指出,由于outofmemory被kill掉的进程,会在/var/log下的某个文件中留下最终的遗迹,在笔者使用的debian系统中,可以通过dmesg查看:

dmesg -T | grep -E -i -B100 'killed process'

  日志如下:

[月 12日 24 16:02:24 2017] [ pid ] uid tgid total_vm rss nr_ptes swapents oom_score_adj name

[月 12日 24 16:02:24 2017] [ 172] 0 172 10553 722 23 1375 0 systemd-journal

[月 12日 24 16:02:24 2017] [ 181] 0 181 10201 398 22 134 -1000 systemd-udevd

[月 12日 24 16:02:24 2017] [ 518] 0 518 8738 361 21 70 0 cron

[月 12日 24 16:02:24 2017] [ 519] 0 519 4756 314 13 45 0 atd

[月 12日 24 16:02:24 2017] [ 520] 0 520 13795 412 31 143 -1000 sshd

[月 12日 24 16:02:24 2017] [ 525] 0 525 4964 161 14 65 0 systemd-logind

[月 12日 24 16:02:24 2017] [ 536] 105 536 10531 172 26 98 -900 dbus-daemon

[月 12日 24 16:02:24 2017] [ 580] 0 580 1064 362 8 34 0 acpid

[月 12日 24 16:02:24 2017] [ 602] 0 602 4926 373 13 37 0 agetty

[月 12日 24 16:02:24 2017] [ 605] 0 605 4881 373 14 37 0 agetty

[月 12日 24 16:02:24 2017] [ 643] 108 643 8346 283 22 134 0 ntpd

[月 12日 24 16:02:24 2017] [ 889] 104 889 12794 369 26 158 0 exim4

[月 12日 24 16:02:24 2017] [11640] 0 11640 4188 1118 15 0 0 atop

[月 12日 24 16:02:24 2017] [29370] 0 29370 14434 420 30 177 0 sshd

[月 12日 24 16:02:24 2017] [29372] 3010 29372 14434 189 29 147 0 sshd

[月 12日 24 16:02:24 2017] [29373] 3010 29373 7813 491 20 587 0 bash

[月 12日 24 16:02:24 2017] [30731] 0 30731 14434 429 30 177 0 sshd

[月 12日 24 16:02:24 2017] [30733] 3010 30733 14434 328 29 154 0 sshd

[月 12日 24 16:02:24 2017] [30734] 3010 30734 7810 432 19 606 0 bash

[月 12日 24 16:02:24 2017] [30746] 3010 30746 13967 408 30 102 0 su

[月 12日 24 16:02:24 2017] [30747] 0 30747 7389 422 19 194 0 bash

[月 12日 24 16:02:24 2017] [31688] 0 31688 13967 408 31 101 0 su

[月 12日 24 16:02:24 2017] [31689] 3010 31689 7808 482 19 566 0 bash

[月 12日 24 16:02:24 2017] [32128] 3010 32128 7761 445 19 32 0 top

[月 12日 24 16:02:24 2017] [32132] 3010 32132 2357921 1868878 4581 467803 0 python

[月 12日 24 16:02:24 2017] [32133] 3010 32133 1255 152 7 57 0 strace

[月 12日 24 16:02:24 2017] Out of memory: Kill process 32132 (python) score 957 or sacrifice child

[月 12日 24 16:02:24 2017] Killed process 32132 (python) total-vm:9431684kB, anon-rss:7473936kB, file-rss:1576kB 

  由于进程使用内存最多,而且没有设置保护机制,所以32122这个python进程就被kill掉了。

  所以,如果机器内存使用率较高,那么当进程消失的时候不妨用dmesg看看。

谁发的信号

  更普遍的,即使我们知道进程被SIGKILL干掉了,这没有什么用。关键是得找出谁发送的这个信号,是故意的还是意外,也许是新手写的脚本误伤,也许是老手故意搞破坏。

  最简单的,那就是查看last与history,last看看谁登陆到了系统,然后再用history看看相关的操作记录。可是,操作记录是可以被清除的,history -c可以清除本终端上的操作记录,也可以直接清空、删除.bash_history,也许有更高级的手段来禁止清空操作记录,但到底是道高一尺魔高一丈,还是魔高一尺道高一丈,我也就不清楚了。

systemtap

  在who-killed-my-process-and-why中,指出了其中的一种办法,使用systemtap,笔者也是第一次听说这个家伙,据说非常强大。官方给出了捕获信号的示例代码。

probe begin
{printf("%-8s %-16s %-5s %-16s %6s %-16s\n","SPID", "SNAME", "RPID", "RNAME", "SIGNUM", "SIGNAME")
}probe signal.send 
{if (sig_name == @1 && sig_pid == target())printf("%-8d %-16s %-5d %-16s %-6d %-16s\n", pid(), execname(), sig_pid, pid_name, sig, sig_name)
}

  将上述代码保存为sigmon.stp文件,然后查看待监控的进程的pid,使用下述命令监控发送到该进程的SIGKILL信号

stap -x pid sigmon.stp SIGKILL

   不过这种方法,我并没有实验成功,systemtap的安装太麻烦了。。。

audit

  在Who sends a SIGKILL to my process mysteriously on ubuntu server中,提到了另外一个更加简单的方法,那就是使用audit

  安装很简单:sudo apt-get install auditd

  启动服务并查看状态: service auditd start & service auditd status

  然后通过auditctrl添加规则: auditctl -a exit,always -F arch=b64 -S kill -F a1=9

  启动然后kill掉Python程序

$ python run_forever.py &
[1] 24067
$ kill -9 24067

  使用ausearch搜索结果: ausearch -sc kill。结果如下 

time->Mon Dec 25 19:52:55 2017
type=PROCTITLE msg=audit(1514202775.088:351): proctitle="bash"
type=OBJ_PID msg=audit(1514202775.088:351): opid=24067 日uid=-1 ouid=3010 oses=-1 ocomm="python"
type=SYSCALL msg=audit(1514202775.088:351): arch=c000003e syscall=62 success=yes exit=0 a0=5e03 a1=9 a2=0 a3=7ffc0d9f7b90 items=0 ppid=1349 pid=1350  uid=3010 gid=3010 euid=3010 suid=3010 fsuid=3010 egid=3010 sgid=3010 fsgid=3010 tty=pts0 comm="bash" exe="/bin/bash" key=(null)

   可以看到,信号的目标进程是一个python程序,pid是24067,启动该进程的用户的id是3010。kil进程的信号被用户3010在pid为1350的bash中发出。

案例与总结

  我遇到过的,进程悄无声息消失的情况,有以下几种

  (1)进程确实是crash了,不过用于core file size设置的问题,没有生成coredump,这里可以通过ulimit -c确认

  (2)oom,代码bug导致进程占用内存过多,被操作系统干掉

  (3)进程被父进程,或者监控进程(watchdog)给kill掉,这个在使用框架的时候容易出现

  (4)进程被误杀,诸如这样的脚本 kill -9 `ps aux | grep python | awk '{print $2}'`, 杀掉所有的python进程,这是非常粗暴的方法,非常容易误杀

  (5)top,top命令也能杀掉进程,are you kidding me? No

  

  如上图所示,进程9603是一个Python程序,top -c默认按照CPU使用量排序,所以这个CPU 100%的进程在最前面。当按下K键的时候,就会给进程发信号,default pid就是在第一行的进程,当然这里也可以输入pid。直接回车,结果如下图

  

  可以看到,默认的信号是SIGTERM(15),也可以输入信号。在敲回车之后,这个进程就被kill了

  即使查看history命令,也只有一个top命令,完全看不出什么,所以使用top命令也要小心啊,捕获SIGTERM也是一个不错的主意。

  当然,做好权限控制,就能减少进程被意外Kill,特别是在线上服务器,适当的监控也是必要的。

  那么当进程消失的时候,可以按照下列步骤排查

  (1)看日志

  (2)查看有没有coredump,查看ulimit -c

  (3)看系统内存使用量,用dmesg看是不是OOM

  (4)看last与history,crontab

  (5)也许一切都在运维的掌控之中 


http://chatgpt.dhexx.cn/article/7meL1YpN.shtml

相关文章

Linux操作系统之批量杀死进程

前言 在Linux操作系统中&#xff0c;一般常用的杀死进程的命令是 kill 、 pkill 、 killall &#xff0c;根据杀死单个进程拓展至批量杀死进程。 1、查看指定名称的进程&#xff0c;如下查看运行wps程序的进程&#xff1a; ps -ef | grep wps | grep -v grep 结果如下&…

在 VUE中,动态加载JS文件

需求 在vue组件中需要调用的函数方法名是相同的&#xff08;接口相同&#xff09;&#xff0c;但是按照页面不同需要导入不同JS文件 。如下&#xff1a; 然而上面这种写法肯定是行不通的&#xff0c;但表达的需求很明确。根据menuId的不同从JS文件中获取方法 解决方案 promi…

原生js实现动态加载js文件?

一、写在前面 今天拼多多笔试题&#xff0c;题目如下&#xff1a; 实现一个动态加载函数function loadScript(src, attrs)返回Promise, 其中 src是脚本地址&#xff0c;attrs是脚本属性。二、具体实现 <script>function loadScript(src, attrs) {return new Promise((r…

JavaScript网页实例:在网页里动态加载JavaScript

把一些逻辑独立的JavaScript脚本文件单独加载&#xff0c;是一种常见的JavaScript动态加载技术。这样做的好处有很多&#xff0c;比如可以减少不必要的JavaScript脚本文件的加载&#xff0c;以提高网页浏览速度。 补充代码&#xff0c;要求用户在网页中点击【动态加载】按钮后…

html动态加载js方法,动态引入js四种方法总结

这次给大家带来动态引入js四种方法总结,动态引入js四种方法的注意事项有哪些,下面就是实战案例,一起来看一下。 index.html test.jsalert("hello! I am test.js"); var str="1"; dynamic.js//第一种方式:直接document.write 但这样会把当前的页面全覆写…

动态加载js文件

1、使用场景 例如本人当前需求——给当前的管理系统的bootstarpTable的提示语修改各个国家的语言&#xff08;语言提示根据当前管理系统的语言环境&#xff09;。 也就是根据如图所示的语言&#xff0c;来变换bootstarptable的提示语&#xff0c; 类似这种提示语。 看看是怎么做…

js中动态加载js

下面介绍一种JS代码优化的一个小技巧&#xff0c;通过动态加载引入js外部文件来提高网页加载速度 【基本优化】 将所有需要的<script>标签都放在</body>之前&#xff0c;确保脚本执行之前完成页面渲染而不会造成页面堵塞问题&#xff0c;这个大家都懂的。 【合…

统计学 假设检验 P值

统计学 假设检验 P值 什么是P值 案例分析 来自总体方差检测的题目 p值的计算 P值

统计假设检验中的P值及置信区间理解

置信区间&#xff0c;就是一种区间估计。 例如&#xff0c;使用95%假设区间估计&#xff0c; 正式的期望无法获取&#xff0c;可用期望的均值替代 显著性P-value 假设&#xff1a;说你的硬币是公平的 检验假设&#xff1a;扔十次&#xff0c;看实验的结果是不是和假设相符 …

统计|假设检验中的P值(pvalue)如何看/怎样理解

本博文源于《商务统计》&#xff0c;之前在做matlab编程和spss统计分析中&#xff0c;对p值的理解就是懵懵懂懂&#xff0c;无法确定出真正含义。今天就以新生儿的例子来观看p值是如何看的&#xff0c;并且了解假设检验的5个步骤。 例子&#xff1a;新生儿 假设检验的一般步骤…

假设检验:使用p值来接受或拒绝你的假设

作者|GUEST 编译|VK 来源|Analytics Vidhya 介绍 检验是统计学中最基本的概念之一。不仅在数据科学中,假设检验在各个领域都很重要。想知道怎么做?让我们举个例子。现在有一个lifebuoy沐浴露。 沐浴露厂商声称,它杀死99.9%的细菌。他们怎么能这么说呢?必须有一种测试技术…

假设检验中的显著性水平与p值

假设检验的原理 假设检验是根据样本信息&#xff0c;提出对于总体信息的假设&#xff0c;并且对假设的正确性进行推断。 判断原假设的方法 推断的依据是假设成立发生的概率&#xff0c;并且设置显著性水平 α \alpha α (取值一般为0.01&#xff0c;0.05&#xff0c;0.1)。…

统计学假设检验中 p 值的含义具体是什么?

一、作者&#xff1a;李可乐 链接&#xff1a;https://www.zhihu.com/question/23149768/answer/23745483 来源&#xff1a;知乎 著作权归作者所有。商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处。 就从打赌开始说起。 一日闲机无聊&#xff0c;我与楼主会饮于…

统计推断——假设检验中 p 值的含义具体是什么?

「假设检验」&#xff0c;顾名思义&#xff0c;就是通过概率统计的知识来判断一个命题&#xff08;如「抛掷一枚硬币出现正反面的概率是均匀的」&#xff0c;如「值大于0.75」&#xff09;的真伪性。 这个命题便称作「零假设&#xff0c;null hypothesis」&#xff0c;我们通常…

统计——假设检验与p值

假设检验&#xff0c;是基于给定的样本对假设做出判定&#xff0c;分为参数假设检验&#xff08;假设可以用一个参数的集合表示&#xff09;和非参数假设检验&#xff08;比如&#xff1a;假设总体服从某种分布&#xff09;。 假设检验的步骤&#xff1a; 1、建立假设&#xff…

STM32———高级定时器的死区时间计算方法

STM32———高级定时器的死区时间计算方法 1.定时器的时钟分频因子和预分频系数的区别&#xff1a; 1.1 时钟分频因子(Clock division),决定定时器的工作时钟频率. tDTSfDTS ; tCK_INTfCK_INT 1.2 预分频系数决定计数器的工作时钟的&#xff0c;配合自动重装载值Period完成…

暂时性死区以及函数作用域

暂时性死区 暂时性死区也就是变量声明到声明完成的区块&#xff0c;这个区块是一个封闭的作用域&#xff0c;直到声明完成。 如果在变量声明之前使用该变量&#xff0c;那么该变量是不可用的&#xff0c;也就被称为暂时性死区。 var 没有暂时性死区&#xff0c;因为var存在变…

STM32-互补输出带死区和刹车断路笔记

互补输出带死区控制 比如说&#xff0c;高级控制定时器&#xff08;TIM1 和 TIM8&#xff09;可以输出两路互补信号&#xff0c;并管理输出的关断与接通瞬间。这段时间通常称为死区&#xff0c;由于硬件设备的延迟和一些设备转换的用时&#xff0c;这时候进行操作可能会导致比…

es6中的暂存性死区

暂存性死区 暂存性死区是相对于某一个变量来说的&#xff0c;就是在定义该变量之前的区域就是暂存性死区 const i 1{//死区开始console.log(i) //死区里边拿不到外边的i&#xff0c;也拿不到本代码块内的i//死区结束const i 2 console.log(i) //直到这里才能正常使用 i }来看…