之前一直知道使用 Ctrl+Z 挂起前台进程来阻止进程运行,之后可以再通过 shell 的作业控制 (jobs / fg N) 来将后台进程切换为前台,从而继续运行。

最近学到一种新的方法,对于不停有 console 输出的前台进程,可以使用 Ctrl+S 来 STOP 一个进程的输出,从而暂停进程。

之后可以通过 Ctrl+Q 或输入任意字符来重启 (START) 进程输出,从而继续运行。

看到这个方法,立即想到写个脚本验证一下:

deadloop.sh

 #! /bin/sh
var=
while :
do
echo this is $var
var=$(($var+))
usleep
done

这个脚本每 100 毫秒输出一条日志 “this is N”,其中 N 为日志序号,可以帮我们确定在一次暂停与重启之间,是否有输出丢失。

运行过程中按下 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
>

首先可以看到重启后的输出序号与重启前的可以接上,所以中间并没有输出丢失,也就是说是进程被暂停了,而不只是输出停止了。

其次在按下 Ctrl+Z 时终端会回显 ^Z,而按下 Ctrl+S 或 Ctrl+Q 时,终端没有任何回显。

于是重点对比按下 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 停止进程时收到以下的输出:

stap_signal.sh

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 结束,而是挂起时向父进程发送的通知。关于这一点,可以参考我之前写的一篇文章:

[apue] 等待子进程的那些事儿

在暂停期间,通过 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 标志,使用之前的小工具:

[apue] 一个查看当前终端标志位设置的小工具

可以查看终端的输入 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 也无用武之地。

例如,使用下面的命令启动脚本:

>./deadloop.sh > deadloop.log

则无法通过 Ctrl+S 的方法暂停这个进程。

总结一下,今天学到一个新的方法去暂停运行中的前台进程,可能对于运维老鸟来说已经是手到擒来,对我却是完全的新鲜,

所以花了些时间研究下,感觉 linux 博大精深,不起眼处可能就藏着一些好东西,值得挖掘!

[apue] 使用 Ctrl+S停止输出而不用挂起前台进程的更多相关文章

  1. Xcode 8 控制台输出大量不用的log的问题解决&&NSLog失效的解决

    Product-->Scheme-->editeScheme中:Auguments中Environment Variable中 Scheme中添加环境变量 "OS_ACTIVIT ...

  2. shell模拟ctrl c停止

    kill命令可以带信号号码选项,也可以不带. 如果没有信号号码,kill命令就会发出终止信号(15),这个信号可以被进程捕获,使得进程在退出之前可以清理并释放资源. 也可以用kill向进程发送特定的信 ...

  3. java 循环时候当达到这个类型的极值时 会停止输出

  4. apue 外传

    先上目录 chapter 3 [apue] dup2的正确打开方式 chapter 10 [apue] 等待子进程的那些事儿 chapter 14 [apue] 使用文件记录锁无法实现父子进程交互执行 ...

  5. 高性能Linux服务器 第10章 基于Linux服务器的性能分析与优化

    高性能Linux服务器 第10章    基于Linux服务器的性能分析与优化 作为一名Linux系统管理员,最主要的工作是优化系统配置,使应用在系统上以最优的状态运行.但硬件问题.软件问题.网络环境等 ...

  6. linux下常见的性能分析工具

    转载于:http://bian5399.blog.51cto.com/3848702/834715 性能调优的主要目的是使系统能够有效的利用各种资源,最大的发挥应用程序和系统之间的性能融合,使应用高效 ...

  7. (转)Linux 系统性能分析工具图解读(一、二)

    Linux 系统性能分析工具图解读(一.二) 原文:http://oilbeater.com/linux/2014/09/08/linux-performance-tools.html 最近看了 Br ...

  8. python编程:从入门到实践----基础知识>第4章练习

    4-1 比萨 :想出至少三种你喜欢的比萨,将其名称存储在一个列表中,再使用for 循环将每种比萨的名称都打印出来. a.修改这个for 循环,使其打印包含比萨名称的句子,而不仅仅是比萨的名称.对于每种 ...

  9. PythonCrashCourse 第四章习题

    Python 从入门到实践第四章习题 4.1想出至少三种你喜欢的比萨,将其名称存储在一个列表中,再使用for 循环将每种比萨的名称都打印出来 修改这个for 循环,使其打印包含比萨名称的句子,而不仅仅 ...

随机推荐

  1. js中的函数重载

    函数重载与js 什么是函数重载 重载函数是函数的一种特殊情况,为方便使用,C++允许在同一范围中声明几个功能类似的同名函数,但是这些同名函数的形式参数(指参数的个数.类型或者顺序)必须不同,也就是说用 ...

  2. 移动端android上line-height不居中的问题的解决

    废话不多话,直接上代码,如下: .btn { width: 1.5rem; max-width: 100px; text-align: center; height: .56rem; font-wei ...

  3. ASP.NET MVC 实现页落网资源分享网站+充值管理+后台管理(开篇)

    源码下载地址:http://www.yealuo.com/Sccnn/Detail?KeyValue=c891ffae-7441-4afb-9a75-c5fe000e3d1c     这是一个比较简单 ...

  4. CodeForces 1096D(线性dp)

    传送门 •题意 给出一个长度为n的字符串s,对于每个$s_{i}$有$a_{i}$的价值 让你删除最小的价值,使得字符串中不存在$hard$这个子序列 •思路 设dp[1]是不存在以$h$为前缀的最小 ...

  5. 托管exe文件的加载和执行

    托管exe文件被启动的时候,首先被PE Loader载入.PE Loader载入exe文件之后,会分析PE文件头的data directory table,如果CLR_Header内的值不为0,表示该 ...

  6. "技术框架太多,多的眼花缭乱,如何在众多选择中找到自己的方向?

    "技术框架太多,多的眼花缭乱,如何在众多选择中找到自己的方向?",经常有人这么问我. 咱们从开源项目说起,可以从两个维度来对开源项目进行分类,一方面是编程语言,另一方面是应用领域. ...

  7. 搭建个人/企业私有云盘-seafile

    一.安装依赖组件 安装前的准备工作安装 Seafile 服务器之前,请确认已安装以下软件MariaDB 或者 MySQL 服务器 (MariaDB 是 MySQL 的分支),python 2.7 (从 ...

  8. grep工具

    全面搜索正则表达式(Global search regular expression(RE) ,GREP)是一种强大的文本搜索工具,它能使用正则表达式搜索文本,并把匹配的行打印出来. Unix/Lin ...

  9. Qt4.5 QFrame(相当于Delphi里的TPanel,有各种凹凸方式)

    QFrame类是有框架的窗口部件的基类. QPopupMenu使用这个来把菜单“升高”,高于周围屏幕.QProgressBar有“凹陷”的外观.QLabel有平坦的外观.这些有框架的窗口部件可以被改变 ...

  10. STM32 命名方法

    1.STM32型号的说明:以STM32F103RBT6这个型号的芯片为例,该型号的组成为7个部分,其命名规则如下: STM32  ST公司生产的Cortex-M内核的32位微控制器 F F代表产品类型 ...