Skip to content

cmd - 程序是怎么被终止的

::: tip 场景

当我们在ssh控制终端执行一个长时间运行的命令时,发现当ssh断开连接后,如果再连接进去,就发现这个命令已经不再执行了!

:::

为什么

在登录会话中,shell通常是终端的控制进程。大多数shell程序在交互式运行时会为SIGHUP信号建立一个处理器。这个处理器会处理shell,但在终止之前会向由shell创建的各个进程组(包括前台和后台进程组)发送一个 SIGHUP 信号。(在SIGHUP信号之后可能会发送一个SIGCONT信号,这依赖于shell本身以及任务当前是否处于停止状态)。至于这些组中的进程如何响应SIGHUP信号则需要根据应用程序的具体需求,如果不采取特殊的动作,那么默认情况下会终止进程。

::: warning TLDR

程序被SIGHUP信号终止了!

:::

如何避免

nohup

对于单独程序,可以使用nohup命令来避免该程序给SIGHUP杀死。

nohup,故名思议就是忽略SIGHUP信号,一般搭配& 一起使用,&表示将此程序提交为后台作业或者说后台进程组。执行下面的命令

nohup long_time_run_command &

nohup与&启动的程序, 在终端还未关闭时,完全不像传统的守护进程,因为其不是会话首进程且持有终端,只是其忽略了SIGHUP信号

从nohup源码就可以看到,其实nohup只做了3件事情

  1. dofile函数将输出重定向到nohup.out文件
  2. signal函数设置SIGHUP信号处理函数为SIG_IGN宏(指向sigignore函数),以此忽略SIG_HUP信号
  3. execvp函数用新的程序替换当前进程的代码段、数据段、堆段和栈段。

execvp 函数执行后,新程序(并没有fork进程)会继承一些调用进程属性,比如:进程id、会话id,控制终端等

在终端关闭后,nohup起到类似守护进程的效果,但是跟传统的守护进程还是有区别的

1、nohup创建的进程工作目录是你执行命令时所在的目录

2、0 1 2 标准输入 标准输出 标准错误 指向nohup.out文件

3、nohup创建的进程组中,除首长进程的父进程id变为1之外,其余进程依然保留原来的会话id、进程组id、父进程id,都保持不变

screen

对于批量命令,可以使用screen开启一个终端会话,可以在一个ssh连接上就可以管理多个不同的后台运行会话,并且可以实现,当ssh断开连接后,也可以恢复之前的会话。

TLDR

Hold a session open on a remote server. Manage multiple windows with a single SSH connection.See also tmux and zellij.More information: https://manned.org/screen.

 - Start a new screen session:
   screen

 - Start a new named screen session:
   screen -S {{session_name}}

 - Start a new daemon and log the output to screenlog.x:
   screen -dmLS {{session_name}} {{command}}

 - Show open screen sessions:
   screen -ls

 - Reattach to an open screen:
   screen -r {{session_name}}

 - Detach from inside a screen:
   Ctrl + A, D

 - Kill the current screen session:
   Ctrl + A, K

 - Kill a detached screen:
   screen -X -S {{session_name}} quit

碎碎念

当使用SSH连接服务器,当数据量较大,运行时间较长时,直接跑命令,窗口或许异常终止,不得不重新跑,还是学下screen命令吧。可以使用Linux下的screen命令,即使网络连接中断,用户也不会失去对已经打开的命令行会话的控制。

1、安装screen:sudo apt install screen

2、运行screen -ls可以看到现有的screen的会话。

3、在screen里运行我们需要的脚本,那么运行完后怎么回到原来的命令行呢(暂时离开,保留screen会话中的任务或程序)?先按CTRL+a,然后再按d。这个就是dettach的意思啦,离开screen的session。

4、恢复screen会话

当回来时可以再执行执行:screen -r lnmp 即可恢复到离开前创建的lnmp会话的工作界面。如果忘记了,或者当时没有指定会话名,可以执行:screen -ls screen会列出当前存在的会话列表,

5、怎么回到这个screen的会话中呢?运行screen -r {session name}

6、分屏: Screen分屏,有的时候我们需要边观察一个命令的输出边执行一些操作,开两个CLI是一种解决方式,用screen分屏的功能也能做到。

a、首先在screen的session中按CTRL+a,X,这个时候就会出现第二个屏。

b、然后按CTRL+a,tab可以在两个屏幕之间自由切换。

c、切换到下个屏幕后,没有命令输入的提示符啊,怎么建立呢?按CTRL+a c

7、可以在detach的模式下跑screen的命令,我经常用这个命令去在后台持续抓包。

就是在主tty中,运行 screen -dm {你要跑的命令}

看下图,我运行screen -dm tcpdump,然后再去查看screen的 session时,发现多了一个,但是我并没有进入screen的会话中,也就是并没有attach进去。

8、kill掉一个screen

(1)、使用screen名字,kill掉。

  screen -S session_name -X quit

(2)、激活screen:

 screen -r session_name

 并利用exit退出并kiil掉session。

9、关闭screen的会话 执行:exit ,会提示:[screen is terminating],表示已经成功退出screen会话。

10、远程演示

首先演示者先在服务器上执行 screen -S test 创建一个screen会话,观众可以链接到远程服务器上执行screen -x test 观众屏幕上就会出现和演示者同步。

11、常用快捷键

Ctrl+a c :在当前screen会话中创建窗口 Ctrl+a w :窗口列表 Ctrl+a n :下一个窗口 Ctrl+a p :上一个窗口 Ctrl+a 0-9 :在第0个窗口和第9个窗口之间切换

12、创建一个新窗口 screen -S name 敲击screen直接启动screen,使用这条命令可以为screen起一个名字

13、清楚dead会话,若因为某一个原因其中一个会话死掉,这时screen -list会显示该会话成为dead状态,使用screen -wipe命令清楚该会话

14、用 screen -ls, 显式当前状态为Attached, 但当前没有用户登陆此会话。screen此时正常状态应该为(Detached)

此时用screen -r {session-id},怎么也登不上。最后找到解决方法:screen -D -r {session-id}

-D -r 先踢掉前一用户,再登陆。