进程丢失的办法
https://www.cnblogs.com/xybaby/p/8098229.html
关注他的博文
https://lwn.net/Articles/317814/
https://stackoverflow.com/questions/726690/what-killed-my-process-and-why
文中还有两个工具没有测试使用
最近线上遇到一个很抓狂的问题,线上跑的服务端进程突然没有了。虽然及时发现重启解决问题,但是必须要查查为什么进程没了。
1. 进程终止的原因
一共有 8 种方式让进程终止,其中有 5 种是正常终止
从 main 函数返回
调用 exit
调用 _exit 或者 _Exit
最后一个线程从其启动例程返回
从最后一个线程调用 pthread_exit
异常终止有 3 种调用 abort
接到一个信号
最后一个线程对取消请求作出响应
我们跑在线上的服务进程都是不会退出的,正常情况下业务是不会让其退出的。在上面说到的异常情况中,没有业务会去主动调用 abort 函数,其实调用 abort 就是发送进程不可忽略的信号 SIGABRT 信号。最后一个线程对取消请求作出响应这种情况业务一般也不会自己设置。因此经过排查,其实线上的服务进程终止就只能是收到了一个信号,要么是这个信号进程没有处理执行了默认退出的动作,要么是进程本身不具备处理这个信号的权限。
其中有两个信号比较特殊,SIGKILL、SIGSTOP。这两个信号不能被忽略,因为他们向内核和超级用户提供了使进程终止或者停止的可靠方法。这两个信号也不能捕捉。还有一类信号例如:SIGABRT、SIGBUS等,虽然可以捕捉甚至忽略,但是通常不建议这么干,由硬件异常产生的信号(如非法内存引用或者除以0,甚至硬件故障) ,进程继续运行的行为是未知的。
而重点是我们需要知道这个信号是其他进程发出的,还是自己给自己发出的退出信号。
因此,我们现在很清楚,解决进程悄无声息的终止的原因,就是进程收到了一个让其退出的信号,找到谁发的这个信号,然后弄清楚它为什么要发。
2. 信号
我们经常使用的 kill ,起始就是给进程发送信号 SIGKILL 。再比如写 c++ 程序最常遇到的段错误,就是内核给进程发送了例如 SIGSEGV 信号,代表无效的内存引用。
举个例子:
1 | #include <unistd.h> |
我写了一段代码,然后编译,放在后台运行,并且使用 strace 查看监控该进程。
1 | gcc -o test test.c |
1 | 09:31:30.821272 +++ killed by SIGKILL +++ |
最后显示出两行信息,收到了 SIGKILL 信号,于是进程默认退出了。好,明白了信号的简单原理。我们再来探究一个问题,此时我们虽然知道了收到了那个信号。但是不知道谁发的这个信号,还有他为什么要发这个信号。接下来我们探究一下原因。
3.谁发的信号
先来介绍一种非常常见的系统性问题引起的 kill 掉进程。
3.1 系统 OOM
1 | Major distribution kernels set the default value of /proc/sys/vm/overcommit_memory to zero, which means that processes can request more memory than is currently free in the system. This is done based on the heuristics that allocated memory is not used immediately, and that processes, over their lifetime, also do not use all of the memory they allocate. Without overcommit, a system will not fully utilize its memory, thus wasting some of it. Overcommiting memory allows the system to use the memory in a more efficient way, but at the risk of OOM situations. Memory-hogging programs can deplete the system's memory, bringing the whole system to a grinding halt. This can lead to a situation, when memory is so low, that even a single page cannot be allocated to a user process, to allow the administrator to kill an appropriate task, or to the kernel to carry out important operations such as freeing memory. In such a situation, the OOM-killer kicks in and identifies the process to be the sacrificial lamb for the benefit of the rest of the system. |
linux 系统为了充分利用内存,在进程启动时,系统不会立即分配给进程内存,而是等进程需要内存的时候再来分配。换句话说,如果进程在启动时系统就分配好进程需要的内存,那么好处是:我们可以实时知道系统内存被使用了多少,后面启动的进程如果系统内存不足就不能启动。但是坏处是:系统分配给进程的内存可能进程暂时没有用到,让这块内存空闲着,浪费系统资源。于是才用进程先启动,等使用内存的时候再来分配。这就导致了占用内存的进程可能会耗尽系统的内存,使整个系统陷入停顿;而且这个行为是不可预知的,当系统内存被耗尽的时候,Linux 会采用 OOM-KILL 的方法,根据 oom_adj 来 kill 掉一个或多个进程,保证整个系统的稳定。(也有书上说,发生OOM 的时候会kill 掉占用内存最大的进程,源码没看,后面看了补上)
这是一种常见的进程被系统干掉的例子,如果出现这种情况,一般会在 /var/log 下的 message、syslog 或其他文件中留下痕迹,也可以使用 dmesg 来查看
1 | dmesg -T | grep -E -i -B100 'killed process' |
因此,建议如果是比较重要的进程,那么就给这个进程以保护机制,就如上面提到的设置 oom_adj,oom_adj 的值为 -17—+15,值越大发生OOM 的时候更容易被 kill 掉,如果某个进程 oom_adj 设置为 -17,那么就说明这个进程就被认为是不能被 OOM-killing 的。
3.2 查看日志
last 可以看到谁登陆到了系统,history 可以看到系统的操作记录。
工具 systemtap 是一款内核调试工具,几乎可以监控任何系统调用。
工具 audit 可以详细监控用户的行为,详细到查看或修改了某个文件。这些都可以在日志中查看到。
这两个工具后续会补上其使用方法
3.3 其他思路
再执行 linux 命令或者执行脚本的时候一定要清楚脚本或者命令的作用,例如:下面
1 | kill -9 `ps aux | grep python | awk '{print $2}'` |
杀掉了所有的 python 进程
比如 top -c 默认按照 CPU 使用量排序,所以CPU 100% 的进程在最前面,当按下 K 键时就给进程发了信号,默认信号是 SIGTERM(15),而且history 看日志的时候也只是一个 top 命令。
还有看看定时事件 crontab,是否有定时事件在搞鬼。