[apue] 使用 Ctrl+S中止輸出而不用掛起前臺進程

以前一直知道使用 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 中止進程時收到如下的輸出:

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 也無用武之地。

 

總結一下,今天學到一個新的方法去暫停運行中的前臺進程,可能對於運維老鳥來講已是手到擒來,對我倒是徹底的新鮮,

因此花了些時間研究下,感受 linux 博大精深,不起眼處可能就藏着一些好東西,值得挖掘!

相關文章
相關標籤/搜索