打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
lostwa''s blog

前言

在断开 ssh 连接后执行的任务就会收到 SIGHUP ,默认情况下就会退出,除非添加 nohup 或者 setsid 之类的命令,下面简单分析 SIGHUP 的产生过程以及这类命令的工作原理。

基础知识

参考《unix环境高级编程》第九章 进程关系

会话、进程组、进程之间关系图

  • 进程组(process group)包含一个或多个进程,每个进程组可能有一个组长进程(process group leader),pgid = pid, shell 中作业操作(Job control)以进程组为单位。

  • 会话(session)包含一个或者多个进程组的。

控制终端与会话、前/后台进程组、进程之间的关系

  • 会话首进程(session leader),创建该会话的的进程,其中 sid = pid,一个会话可以包含多个进程组。

  • 控制终端(controlling terminal),指登录到该会话的终端设备(tty)或网络登录的伪终端设备(pseudo-tty),终端断开产生的信号(SIGHUP)会发送给会话首进程。

  • 控制进程(controlling process),与控制终端连接的会话首进程(session leader)。

  • 会话中的进程组分为一个前台进程组(foreground process group)和一个或多个后台进程组(backgrond process group),终端中的操作信号均是发送给的前台进程组的所有进程。

sshd 服务器

sshd 服务的进程组结构

伪终端(pty)分为主设备(ptm)和从设备(pts),并且成对出现,主从设备之间连接在可以简单看作双向管道。终端行规程(line discipline)主要用于翻译特殊字符成特定操作,如 CTRL-C 转化成中断。

sshd 子进程将 ssh 客户端的输入转发到 pty 另一端作为终端输入,同时将终端输出转发给 ssh 客户端作为标准输出, ssh 客户端作为标准输入输出的控制终端

ssh-client <-> [socket] <-> sshd <-> [ptm] <-> [pts] <-> shell

多个 ssh 客户端之间发送信息可以向对方的 pts 设备直接写数据,对应 ssh 客户端则会收到消息

shell[root@CentOS6 ~]# echo "Hello pts0" >/dev/pts/0

ssh 连接下的会话、进程、进程组信息

[root@CentOS6 ~]# sleep 60 &[1] 1673[root@CentOS6 ~]# sleep 70 | sleep 71 & [2] 1675[root@CentOS6 ~]# ps -o user,tty,sid,pid,ppid,pgid,cmd  USER     TT         SID   PID  PPID  PGID CMDroot     pts/0     1519  1519  1517  1519 -bashroot     pts/0     1519  1673  1519  1673 sleep 60root     pts/0     1519  1674  1519  1674 sleep 70root     pts/0     1519  1675  1519  1674 sleep 71root     pts/0     1519  1676  1519  1676 ps -o user,tty,sid,pid,ppid,pgid,cmd
  1. 当前 ssh 连接会话为 1519

  2. -bash 进程为 1519 会话控制进程(pid = sid)

  3. sleep 60 属于进程组 1673,sleep 70sleep 71 属于进程组 1474

  4. 执行的 sleep 命令都属于 1519 会话,终端设备都为 pts/0

过程分析

我们开启三个终端连接,第一个用于测试终端断开,第二个用于跟踪终端进程信号,第三个用于跟踪终端中作业进程信号。

终端1(这里省略了其它无关进程信息)

[root@CentOS6 ~]# ps -o pid,ppid,pgid,sid,cmd  PID  PPID  PGID   SID CMD 1042     1  1042  1042 /usr/sbin/sshd22668  1042 22668 22668 sshd: root@pts/0 22670 22668 22670 22670 -bash22720 22670 22720 22670 sleep 2h

其中 pid 1042 为 sshd 服务器后端进程,pid 22668 为 ssh-client 连接启动的 sshd 子进程,pid 22670 为登录的 shell 进程,也是该连接会话(pid=sid=22670)的控制进程,所以当网络连接断开,系统将发送SIGHUP信号给该进程。pid 22720 为在终端 1 中运行的后台作业,它与终端进程属于同一个会话。

终端2,通过 strace 命令跟踪会话控制进程 -bash 的信号

[root@CentOS6 ~]# strace -ttT -e trace=signal -p 22670 Process 22670 attached

终端3,跟踪后台作业进程 sleep 2h 的信号

[root@CentOS6 ~]# strace -ttT -e trace=signal -p 22720Process 22720 attached

断开终端1连接,会话控制进程将收到系统发送的 SIGHUP,同时根据 bash manualbash 退出之前将收到 SIGHUP 将转发给作业列表中的所有作业进程

The shell exits by default upon receipt of a SIGHUP. Before exiting, an interactive shell resends the SIGHUP to all jobs, running or stopped. Stopped jobs are sent SIGCONT to ensure that they receive the SIGHUP. To prevent the shell from sending the SIGHUP signal to a particular job, it should be removed from the jobs table with the disown builtin (see Job Control Builtins) or marked to not receive SIGHUP using disown -h.

终端2上可以看到,bash 进程收到系统发送的 SIGHUP 并向进程组发送SIGHUP,最后向自身发送 SIGHUP 退出

[root@CentOS6 ~]# strace -ttT -e trace=signal -p 22670Process 22670 attached15:30:09.872582 --- SIGHUP {si_signo=SIGHUP, si_code=SI_KERNEL, si_value={int=0, ptr=0x100000000}} ---  //收到系统 SIGHUP15:30:09.872799 --- SIGCONT {si_signo=SIGCONT, si_code=SI_KERNEL, si_value={int=0, ptr=0x100000000}} ---15:30:09.874105 kill(4294944576, SIGHUP) = 0 <0.000035> // 向进程组 22720 发送 SIGHUP15:30:09.874200 rt_sigprocmask(SIG_BLOCK, [CHLD TSTP TTIN TTOU], [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], 8) = 0 <0.000039>15:30:09.874537 rt_sigprocmask(SIG_SETMASK, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], NULL, 8) = 0 <0.000141>15:30:09.874735 --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_KILLED, si_pid=22720, si_status=SIGHUP, si_utime=0, si_stime=0} --- //子进程退出15:30:09.874988 rt_sigreturn()          = 0 <0.000034>15:30:09.875136 rt_sigaction(SIGHUP, {SIG_DFL, [], SA_RESTORER, 0x7fadec1c2660}, {0x452300, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], SA_RESTORER, 0x7fadec1c2660}, 8) = 0 <0.000040>15:30:09.875243 kill(22670, SIGHUP)     = 0 <0.000035>  //向自身进程发送 SIGHUP15:30:09.875328 rt_sigreturn()          = -1 EIO (Input/output error) <0.000032>15:30:09.875425 --- SIGHUP {si_signo=SIGHUP, si_code=SI_USER, si_pid=22670, si_uid=0} --- //收到自己的 SIGHUP15:30:09.875714 +++ killed by SIGHUP +++

kill(4294944576, SIGHUP)kill(-22720, SIGHUP),表示向 222720 进程组发送 SIGHUP,参考 man kill

If pid is less than -1, then sig is sent to every process in the process group whose ID is -pid.

终端3,后台作业进程也收到 bash(si_pid=22670)发送的 SIGHUP 后退出

[root@CentOS6 ~]# strace -ttT -e trace=signal -p 22720Process 22720 attached15:30:09.874246 --- SIGHUP {si_signo=SIGHUP, si_code=SI_USER, si_pid=22670, si_uid=0} ---15:30:09.874470 +++ killed by SIGHUP +++

另外 bash 自身进程退出是否会发送SIGHUP ,这个取决于 huponexit 选项设置

If the huponexit shell option has been set with shopt (see The Shopt Builtin), Bash sends a SIGHUP to all jobs when an interactive login shell exits.

在我们测试环境中(CentOS6.5)中 huponexit 选项默认为关闭,所以 bash 自身的退出不会产生 SIGHUP

[root@CentOS6 ~]# shopt huponexit   huponexit       off

所以当我们通过 exit 退出 bash 时,后台任务并不会结束

[root@CentOS6 ~]# sleep 1h & [1] 5343[root@CentOS6 ~]# exitlogout

新打开一个终端,可以看到作业仍在运行

[root@CentOS6 ~]# pgrep sleep5343

disown VS nohup VS setsid

这是我们常用让程序在终端断开或退出后也能保持后台运行的几个命令,这些命令执行后都能让程序保持运行,但是它们内部工作原理并不一样。

disown ,参考 bash manual 中的介绍,disown 只是将程序移出 shell 的作业列表,从而不会向其发送SIGHUP ,当我们手动发送一个 SIGHUP 给该进程,进程同样会退出。

终端1,在 sleep 1h 命令后添加 disown 将其移出作业列表,执行 jobs 命令无法看到。

sleep 1h & disown  [1] 22763[root@CentOS6 ~]# jobs[root@CentOS6 ~]# kill -SIGHUP 22763

终端2,执行 kill 命令后程序收到 SIGHUP 退出。

[root@CentOS6 ~]# strace -ttT -e trace=signal -p 22763Process 22763 attached16:20:04.453605 --- SIGHUP {si_signo=SIGHUP, si_code=SI_USER, si_pid=22687, si_uid=0} ---16:20:04.453874 +++ killed by SIGHUP +++

nohup 则是通过忽略掉收到 SIGHUP 信号来保持进程运行,参考 coreutils 中部分代码,会先忽略 SIGHUP 再通过 exec 调用执行命令。

....signal (SIGHUP, SIG_IGN);char **cmd = argv + optind;execvp (*cmd, cmd);...

终端1,通过 nohup 执行 sleep 1h 命令,进程可以在作业列表中查看到。通过 psignored 列可以看到进程忽略了 SIGHUP (SIGHUP 数值表示为 1,参考 singal(7))。

[root@CentOS6 ~]# nohup sleep 1h &[1] 22767[root@CentOS6 ~]# nohup: ignoring input and appending output to `nohup.out'[root@CentOS6 ~]# jobs[1]+  Running                 nohup sleep 1h &[root@CentOS6 ~]# ps -o pid,ppid,pgid,sid,ignored,cmd -p 22767   PID  PPID  PGID   SID          IGNORED CMD22767 22687 22767 22687 0000000000000001 sleep 1h

终端2,断开终端1连接和手动发送 SIGHUP,进程都能收到 SIGHUP ,但并不会退出。

[root@CentOS6 ~]# strace -ttT -e trace=signal -p 22767Process 22767 attached16:53:42.935575 --- SIGHUP {si_signo=SIGHUP, si_code=SI_USER, si_pid=22687, si_uid=0} ---16:54:22.096888 --- SIGHUP {si_signo=SIGHUP, si_code=SI_USER, si_pid=22865, si_uid=0} ---^CProcess 22767 detached

setsid 通过创建一个新的会话来执行命令,让进程与终端进程处于不同会话,这样也不会收到 shell 转发的SIGHUP 信号,但是未屏蔽 SIGHUP ,进程如果收到 其它发送的 SIGHUP 仍然会退出。

setsid runs a program in a new session. The command calls fork(2) if already a process group leader. Otherwise, it executes a program in the current process. This default behavior is possible to override by the --fork option.

终端1 ,通过 setsid 执行的 sleep 1h 进程会话 ID 变成了22794,也不在 bash 的作业列表里了。

[root@CentOS6 ~]# setsid sleep 1h &[1] 22793[root@CentOS6 ~]# jobs[root@CentOS6 ~]# ps -o pid,ppid,pgid,sid,ignored,cmd  PID  PPID  PGID   SID          IGNORED CMD 1042     1  1042  1042 0000000000001000 /usr/sbin/sshd22685  1042 22685 22685 0000000000001000 sshd: root@pts/1 22687 22685 22687 22687 0000000000384004 -bash22794     1 22794 22794 0000000000000000 sleep 1h

bash 创建程序时会为其设置新的进程组(job 管理基于进程组),新创建的 setsid 程序(pid 22793)为 process group leader, fork 一个子进程(pid 22794)来执行 sleep 1h 命令,并调用 setsid() 为其设置了新的会话(sid 22794)。创建好后父进程退出(为什么 jobs 命令看不到),子进程变成孤儿进程,被 init (pid 1)接管。

终端2,断开终端1连接,进程未收到任何信号。

[root@CentOS6 ~]# strace -ttT -e trace=signal -p 22794Process 22794 attached

终端2,手动发送 SIGHUP 后,进程收到 SIGHUP 信号并退出。

[root@CentOS6 ~]# strace -ttT -e trace=signal -p 22794Process 22794 attached17:48:57.162818 --- SIGHUP {si_signo=SIGHUP, si_code=SI_USER, si_pid=22812, si_uid=0} ---17:48:57.163155 +++ killed by SIGHUP +++

参考

《UNIX环境高级编程》

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
linux 技巧:使用 screen 管理你的远程会话
Linux命令nohup和screen命令的区别
进程的状态的概述 - js大联盟网 - js学习者的网上家园
在脚本中使用 trap
Linux程序后台运行实践 | 《Linux就该这么学》
你是怎么在 Linux 干掉进程的?
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服