以前一直知道使用 Ctrl+Z 挂起前台进程来阻止进程运行,以后能够再经过 shell 的做业控制 (jobs / fg N) 来将后台进程切换为前台,从而继续运行。html
最近学到一种新的方法,对于不停有 console 输出的前台进程,可使用 Ctrl+S 来 STOP 一个进程的输出,从而暂停进程。linux
以后能够经过 Ctrl+Q 或输入任意字符来重启 (START) 进程输出,从而继续运行。git
看到这个方法,当即想到写个脚本验证一下:github
deadloop.shshell
1 #! /bin/sh 2 var=1 3 while : 4 do 5 echo this is $var 6 var=$(($var+1)) 7 usleep 100000 8 done
这个脚本每 100 毫秒输出一条日志 “this is N”,其中 N 为日志序号,能够帮咱们肯定在一次暂停与重启之间,是否有输出丢失。bash
运行过程当中按下 Ctrl+S,输出果真暂停了:运维
>./deadloop.sh this is 1 this is 2 this is 3 this is 4
再按下 Ctrl+Q 则输出继续,直到按下 Ctrl+Z 挂起进程:工具
>./deadloop.sh this is 1 this is 2 this is 3 this is 4 this is 5 this is 6 this is 7 this is 8 this is 9 this is 10 this is 11 ^Z [1]+ Stopped ./deadloop.sh >
首先能够看到重启后的输出序号与重启前的能够接上,因此中间并无输出丢失,也就是说是进程被暂停了,而不仅是输出中止了。oop
其次在按下 Ctrl+Z 时终端会回显 ^Z,而按下 Ctrl+S 或 Ctrl+Q 时,终端没有任何回显。post
因而重点对比按下 Ctrl+S 时与 Ctrl+Z 时进程的状态,来看这两种暂停方式的区别。
经过 ps 命令查看下两种暂停时进程的状态:
Ctrl+S
>ps xfo pid,ppid,pgid,sid,tpgid,suid,euid,user,stat,tty,command
PID PPID PGID SID TPGID SUID EUID USER STAT TT COMMAND 6653 6652 2786 2786 -1 500 500 yunhai S ? \_ gnome-pty-helper 6655 6652 6655 6655 6655 500 500 yunhai Ss+ pts/0 \_ /bin/bash 12539 6652 12539 12539 16673 500 500 yunhai Ss pts/1 \_ /bin/bash 16673 12539 16673 12539 16673 500 500 yunhai S+ pts/1 | \_ /bin/sh ./deadloop.sh 12797 6652 12797 12797 13349 500 500 yunhai Ss pts/2 \_ /bin/bash 15959 6652 15959 15959 16766 500 500 yunhai Ss pts/3 \_ /bin/bash 16766 15959 16766 15959 16766 500 500 yunhai R+ pts/3 \_ ps xfo pid,ppid,pgid,sid,tpgid,suid,euid,user,stat,tty,command
Ctrl+Z
>ps xfo pid,ppid,pgid,sid,tpgid,suid,euid,user,stat,tty,command PID PPID PGID SID TPGID SUID EUID USER STAT TT COMMAND 6653 6652 2786 2786 -1 500 500 yunhai S ? \_ gnome-pty-helper 6655 6652 6655 6655 6655 500 500 yunhai Ss+ pts/0 \_ /bin/bash 12539 6652 12539 12539 16717 500 500 yunhai Ss pts/1 \_ /bin/bash 16673 12539 16673 12539 16717 500 500 yunhai T pts/1 | \_ /bin/sh ./deadloop.sh 16688 16673 16673 12539 16717 500 500 yunhai T pts/1 | | \_ usleep 100000 16717 12539 16717 12539 16717 500 500 yunhai R+ pts/1 | \_ ps xfo pid,ppid,pgid,sid,tpgid,suid,euid,user,stat,tty,command 12797 6652 12797 12797 13349 500 500 yunhai Ss pts/2 \_ /bin/bash 15959 6652 15959 15959 15959 500 500 yunhai Ss+ pts/3 \_ /bin/bash
能够看到最大的不一样是,经过 Ctrl+Z 中止的进程状态为挂起 ('T'),而经过 Ctrl+S 中止的进程状态为运行 ('S+')。
另外一方面,咱们启动 stap 探测进程间信号的收发,能够在 Ctrl+Z 中止进程时收到如下的输出:
22 events/3 16688 usleep 20 SIGTSTP
22 events/3 16673 deadloop.sh 20 SIGTSTP
16673 deadloop.sh 12539 bash 17 SIGCHLD
16688 usleep 16673 deadloop.sh 17 SIGCHLD
也就是说能够观察到向前台进程发送的 SIGTSTP 信号。而在使用 Ctrl+S 时并没有特别的信号被侦测到 (仅 usleep 进程结束时向父进程发送的 SIGCHILD)。
注意:此处的 SIGCHLD 并不表示 deadloop.sh 与 usleep 结束,而是挂起时向父进程发送的通知。关于这一点,能够参考我以前写的一篇文章:
在暂停期间,经过 pstack 命令查看两种方式暂停的进程堆栈信息:
Ctrl+S
>pstack 16673
#0 0x00119424 in __kernel_vsyscall ()
#1 0x007a7cd3 in __write_nocancel () from /lib/libc.so.6
#2 0x007411b4 in _IO_new_file_write () from /lib/libc.so.6
#3 0x00742a90 in _IO_new_do_write () from /lib/libc.so.6
#4 0x00741c80 in _IO_new_file_overflow () from /lib/libc.so.6
#5 0x00744b2a in __overflow () from /lib/libc.so.6
#6 0x0073e0b5 in putc () from /lib/libc.so.6
#7 0x080aebb0 in echo_builtin ()
#8 0x08070c51 in ?? ()
#9 0x08072e41 in ?? ()
#10 0x08073aa0 in execute_command_internal ()
#11 0x080747a4 in execute_command ()
#12 0x08076d89 in ?? ()
#13 0x08073a02 in execute_command_internal ()
#14 0x080747a4 in execute_command ()
#15 0x08076d89 in ?? ()
#16 0x08073a02 in execute_command_internal ()
#17 0x080747a4 in execute_command ()
#18 0x080750e4 in ?? ()
#19 0x08073bc4 in execute_command_internal ()
#20 0x080747a4 in execute_command ()
#21 0x08060857 in reader_loop ()
#22 0x0805fed9 in main ()
Ctrl+Z
>pstack 16673
#0 0x00119424 in __kernel_vsyscall ()
#1 0x00776673 in __waitpid_nocancel () from /lib/libc.so.6
#2 0x080830f2 in ?? ()
#3 0x0808432e in wait_for ()
#4 0x08074635 in execute_command_internal ()
#5 0x08076dcd in ?? ()
#6 0x08073a02 in execute_command_internal ()
#7 0x080747a4 in execute_command ()
#8 0x080750e4 in ?? ()
#9 0x08073bc4 in execute_command_internal ()
#10 0x080747a4 in execute_command ()
#11 0x08060857 in reader_loop ()
#12 0x0805fed9 in main ()
前者中止在了 write 系统调用,后者中止在了 waitpid 系统调用。
因此前者应该是在输出时被暂停的,然后者是在等待 usleep 子进程返回时被挂起的。
你们能够体会一下这两处方式在细微处的差异。
最后,可使用 Ctrl+S 中止前台进程的前提是 必需打开终端的 IXON 标志,使用以前的小工具:
能够查看终端的输入 flag 是否已经打开了这个标志:
>./term input flag 0x00006f02 BRKINT ICRNL IMAXBEL IXANY IXON output flag 0x00000005 ONLCR OPOST control flag 0x000004bf CREAD CSIZE CS6 CS7 CS8 HUPCL local flag 0x00008a3b ECHO ECHOE ECHOK ICANON IEXTEN ISIG
通常终端都是打开的。若是再打开 IXANY 标志位,则使用任意键均可以重启被中止的输出,而不必定要使用 Ctrl+Q。
最后,还有一个隐藏的前提,就是被暂停的进程在前台有频繁的输出,不然 Ctrl+S 也无用武之地。
总结一下,今天学到一个新的方法去暂停运行中的前台进程,可能对于运维老鸟来讲已是手到擒来,对我倒是彻底的新鲜,
因此花了些时间研究下,感受 linux 博大精深,不起眼处可能就藏着一些好东西,值得挖掘!