Linux命令拾遗-我的进程消失了

原创:打码日记(微信公众号ID:codelogs),欢迎分享,转载请保留出处。

简介

程序员但凡工作时间久一点,总会遇到一些诡异的事情,比如每当你下班时,服务就挂,然后业务同学就各种找过来了,似乎业务与服务程序就离不开你一样。
而当你登录机器去排查问题时,又发现机器上连进程都没了,心里咯噔一下慌了神,进程咋就消失了?

后台任务

刚接触Linux的同学,可能都不知道Shell里面前台任务与后台任务的概念,先介绍一下这个,如下:

Linux命令拾遗-我的进程消失了
  1. 命令直接执行时,启动的是前台任务,而在命令后面添加 &符号,可让进程启动为后台任务。
  2. 使用jobs可以查询当前shell下启动的所有后台任务。
  3. 如果进程是前台任务,按下 Ctrl+z可让其变成后台任务,但任务同时会变成暂停状态。
  4. 使用 bg可以使暂停状态的任务变成运行状态。
  5. 使用 fg可以使后台任务变成前台任务,如果任务是暂停状态,会使其变为运行状态。

看个例子:

启动前台进程,然后按Ctrl+z使其变后台进程
$ java -jar app.jar
^Z
[1]+  Stopped                 java -jar app.jar

查询后台进程,可发现它是Stopped状态
$ jobs -l
[1]+ 19316 Stopped                 java -jar app.jar

使用bg后,可以发现任务变Running状态了
$ bg %1
[1]+ java -jar app.jar &
$ jobs
[1]+  Running                 java -jar app.jar &

再使用fg,会发现任务变成前台任务了
$ fg %1

直接使用&符号,直接就是后台任务并且Running状态
$ java -jar app.jar &
[1] 19620
$ jobs
[1]+  Running                 java -jar app.jar &

nohup与disown

然而,如果你使用上面的方式启动进程,当你下班关掉电脑时,ssh终端会失去连接,你的java进程就会被杀死。
因为在shell里面启动的进程,都是shell这个进程的子进程,ssh失去连接时,shell进程会给它的子进程发SIGHUP信号,这会杀死在shell中启动的子进程,包括后台进程也不例外。

使用nohup命令可以解决这个问题,通过nohup命令启动的进程,会忽略SIGHUP信号,从而让进程能活下来,如下:

$ nohup java -jar app.jar &

如果之前没有使用nohup启动进程,可以使用 disown命令使得进程忽略SIGHUP信号,如下:

  1. 使用 Ctrl+z使之到后台运行,同时进程会变成暂停状态。
  2. 使用 jobs -l查询刚才转到后台任务的jobid,比如是1,使用 bg %1使之变成运行状态。
  3. 使用 disown -h %1使这个后台任务忽略 SIGHUP信号。

除了nohup外,还可以使用tmux、screen等伪终端软件,一样可以避免终端断开时进程被杀死,如下:

ubuntu安装tmux
$ sudo apt install tmux

新建一个伪终端会话,会话名为app
$ tmux new -s app

启动进程,注意现在已经在tmux伪终端里面了,这和平时操作没什么两样
如果要退出伪终端,先按Ctrl + b再按d,不要使用Ctrl+c
$ java -jar app.jar

列出所有伪终端会话
$ tmux ls

重新attach进入app这个会话,这样就可以看任务运行情况了
$ tmux attach -t app

除了安全启动后台进程外,tmux还可以实现多窗口、分屏等高级功能,感兴趣可以 man tmux查看。

kill与信号

我们知道kill可以杀死进程,但其实从原理上来讲,进程并不是被kill杀死的,kill只是给进程发送了一个信号,进程若不捕获信号,内核会执行默认处理程序杀死进程。
通过 kill -l可以查看kill命令能够发送的信号,如下:

$ kill -l
 1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL       5) SIGTRAP
 6) SIGABRT      7) SIGBUS       8) SIGFPE       9) SIGKILL     10) SIGUSR1
11) SIGSEGV     12) SIGUSR2     13) SIGPIPE     14) SIGALRM     15) SIGTERM
16) SIGSTKFLT   17) SIGCHLD     18) SIGCONT     19) SIGSTOP     20) SIGTSTP
21) SIGTTIN     22) SIGTTOU     23) SIGURG      24) SIGXCPU     25) SIGXFSZ
26) SIGVTALRM   27) SIGPROF     28) SIGWINCH    29) SIGIO       30) SIGPWR
31) SIGSYS      34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3
38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7
58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
63) SIGRTMAX-1  64) SIGRTMAX

前面是信号编号,后面是信号名称,可以发现所谓 kill -9其实就是向进程发送 SIGKILL信号,也可以写成 kill -SIGKILL
如下是常见信号出现的场景:

信号 默认行为 场景 SIGHUP 终止进程 终端挂断,比如远程ssh断网 SIGINT 终止进程 来自键盘的中断, Ctrl+c

就是发出此信号 SIGQUIT 终止进程 来自键盘的退出, Ctrl+\

发此信号 SIGKILL 终止进程 杀死程序, kill -9

发此信号 SIGPIPE 终止进程 向一个没有读用户的管道做写操作,比如Socket网络连接,远端关闭连接后,本端还继续写 SIGTERM 终止进程 软件终止信号, kill

默认发此信号 SIGCHLD 终止进程 一个子进程停止或终止 SIGCONT 终止进程 如果进程停止,继续该进程 SIGTSTP 终止进程 来自终端的停止信号, Ctrl+z

发此信号

oom导致的进程消失

除了上面的情况外,oom(内存溢出)也是一种常见的进程消失原因,程序中申请了大量的内存,内存不足导致进程死亡,分如下两种情况:

  1. jvm内存不足导致进程死亡

对于java这样的程序,程序使用内存超过 -Xmx阈值,jvm会自动退出,并在标准输出流中留下 java.lang.OutOfMemoryError异常。

因此启动java进程时,最好使用重定向将标准输出与标准错误保存到日志文件中,然后就可以通过如下方式确认是否oom了:

启动进程时,将标准输出与标准错误保存到日志文件中
$ nohup java -jar app.jar >stdout.log 2>stderr.log &

搜索是否存在OutOfMemoryError异常
$ grep -A5 'OutOfMemoryError' stdout.log stderr.log
stdout.log:java.lang.OutOfMemoryError: Java heap space
stdout.log-     at java.base/java.lang.StringCoding.decodeUTF8_0(StringCoding.java:753) ~[na:na]
stdout.log-     at java.base/java.lang.StringCoding.decodeUTF8(StringCoding.java:712) ~[na:na]
stdout.log-     at java.base/java.lang.StringCoding.decode(StringCoding.java:318) ~[na:na]
stdout.log-     at java.base/java.lang.String.(String.java:592) ~[na:na]
stdout.log-     at java.base/java.lang.String.(String.java:614) ~[na:na]
  1. oom-killer机制导致进程死亡

这是不熟悉Linux的同学比较容易忽略的一个点,Linux系统如果内存不足了,会使用oom-killer机制找一个内存占用大的进程牺牲掉,而一般Java进程占用内存都挺大,所以它经常被牺牲。

而oom-killer杀死进程时,会打印一些信息在dmesg日志中,因此,可以通过如下方式确认进程消失是否是被oom-killer杀死:

$ dmesg -T | grep -A10 -i "kill"
[Fri Dec 17 22:35:47 2021] Memory cgroup out of memory: Killed process 3102186 (java) total-vm:10487368kB, anon-rss:287124kB, file-rss:0kB, shmem-rss:0kB, UID:0 pgtables:3764kB oom_score_adj:0
[Fri Dec 17 22:35:47 2021] oom_reaper: reaped process 3102186 (java), now anon-rss:0kB, file-rss:0kB, shmem-rss:0kB

注:dmesg相当于是内核日志,内核会记录一些关键信息在其中,如果排查某些问题没有头绪时,养成习惯常规性看一下dmesg的内容,说不定能发现啥呢!

本系列文章索引
Linux命令拾遗-入门篇
Linux命令拾遗-文本处理篇
Linux命令拾遗-软件资源观测
Linux命令拾遗-硬件资源观测
Linux命令拾遗-剖析工具
Linux命令拾遗-动态追踪工具
Linux命令拾遗-理解系统负载
Linux命令拾遗-top中的%nice是啥
Linux命令拾遗-网络抓包工具

往期内容

Linux命令拾遗-入门篇
原来awk真是神器啊
Linux文本命令技巧(上)
Linux文本命令技巧(下)
字符编码解惑

Original: https://www.cnblogs.com/codelogs/p/16060697.html
Author: 扣钉日记
Title: Linux命令拾遗-我的进程消失了

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/581387/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球