你無法用ctrl c 殺死的進(jìn)程

有如下shell 腳本 pingoo.sh

#!/bin/bash
while true
do     
    ping 127.0.0.1
done;
$> pingoo.sh

鍵盤鍵入 ctrl +c(^c)
ooooops!
你會(huì)發(fā)現(xiàn)無論你按多少次都無法終止該腳本的運(yùn)行.

使用ps 命令找到進(jìn)程樹

$> ps -j f
 PID  PGID   SID TTY      STAT   TIME COMMAND
21994 21994 21994 pts/29   Ss     0:00 /bin/bash
28833 28833 21994 pts/29   S+     0:00  \_ /bin/bash ./pingoo.sh
28834 28833 21994 pts/29   S+     0:00      \_ ping 127.0.0.1

STAT 進(jìn)程狀態(tài)標(biāo)志:
S: 可中斷的sleep,
s: session leader;
+: 前臺(tái)進(jìn)程;

首先要明確一個(gè)重要假設(shè):當(dāng)用戶按下Ctrl c 時(shí), 他是要終止前臺(tái)進(jìn)城組的運(yùn)行, 出發(fā)進(jìn)程注冊(cè)了自定義的信號(hào)處理函數(shù);

但是如果子進(jìn)程capture sigint, 則 shell 會(huì)假設(shè)用戶通過發(fā)送sigint 使子進(jìn)程做特定工作(比如ping的統(tǒng)計(jì)信息), 也就說用戶發(fā)送sigint的目的并不是結(jié)束前臺(tái)進(jìn)程組, 而是觸發(fā)特定動(dòng)作;ping 就是這類安裝了自定義的信號(hào)處理函數(shù)的進(jìn)程;

當(dāng)你按下ctrl c后發(fā)生了什么?

ctrl +c 被終端驅(qū)動(dòng)程序解釋為 sigint 信號(hào), 由kernel 發(fā)送給 前臺(tái)進(jìn)程組 . ping 進(jìn)程 capture 該信號(hào), 調(diào)用信號(hào)處理函數(shù)。 同時(shí), ping的父進(jìn)程也收到sigint信號(hào)(父進(jìn)程處理interruptible sleep 狀態(tài), 也就出出于wait系統(tǒng)調(diào)用中), 父進(jìn)程被迫退出wait系統(tǒng)調(diào)用,檢查退出原因(是否是EINTR,也就是中斷的系統(tǒng)調(diào)用),然后通過WIFSIGNALED宏可以判斷子進(jìn)程是否注冊(cè)了信號(hào)處理函數(shù) 。如果 注冊(cè)了信號(hào)處理函數(shù),capture sigint, 父進(jìn)程繼續(xù)運(yùn)行(當(dāng)前例子是開始下一次循環(huán)); 如果是子進(jìn)程是因收到sigint信號(hào)終止的(按照default 方式處理sigint), 父進(jìn)程會(huì)終止運(yùn)行.

當(dāng)用戶鍵入ctrl+c后,tty driver 產(chǎn)生SIGINT信號(hào)給前臺(tái)進(jìn)程, pingoo.sh和其子進(jìn)程 都看見sigint信號(hào),(他們是前臺(tái)進(jìn)程組成員)。 ping.sh作為父進(jìn)程處于waitpid blocking狀態(tài), 收到sigint 后waitpid 立即返回 -1, 設(shè)置errorno為 EINTR。同時(shí) bash要求child 立即死亡(也就是sigint的默認(rèn)動(dòng)作 terminate)。最后設(shè)置 child_caught_sigint = 0, bash 隨后會(huì)根據(jù)該flag 退出;

如果child capture signal,設(shè)置 child_caught_sigint = 1; bash 根據(jù)該flag 不退出, 因?yàn)樽舆M(jìn)程capture sigint后, 觸發(fā)信號(hào)處理函數(shù)。 其信號(hào)處理函數(shù)需要做特定處理,如果子進(jìn)程退出,則 父進(jìn)程會(huì)受到 sigchld 信號(hào),父進(jìn)程會(huì)認(rèn)為子進(jìn)程正常退出, 循環(huán)繼續(xù)。

源代碼解析:

bash 4.3.3: jobs.c

  1. waitpid
    pid<-1 等待進(jìn)程組識(shí)別碼為 pid 絕對(duì)值的任何子進(jìn)程。
    pid=-1 等待任何子進(jìn)程,相當(dāng)于 wait()。
    pid=0 等待進(jìn)程組識(shí)別碼與目前進(jìn)程相同的任何子進(jìn)程。
    pid>0 等待任何子進(jìn)程識(shí)別碼為 pid 的子進(jìn)程。

Process-Completion-Status

  1. WIFSIGNALED:
    This macro returns a nonzero value if the child process terminated because it received a signal that was not handled.
  2. WTERMSIG:
    If WIFSIGNALED is true of <var style="background-color: inherit;">status</var>, this macro returns the signal number of the signal that terminated the child process
  /* If waitpid returns -1/EINTR and the shell saw a SIGINT, then we
     assume the child has blocked or handled SIGINT.  In that case, we
     require the child to actually die due to SIGINT to act on the
     SIGINT we received; otherwise we assume the child handled it and
     let it go. */
// status = waitpid(pid), pid <0 
      if (pid < 0 && errno == EINTR && wait_sigint_received)
    child_caught_sigint = 1;  //waitpid被 sigint中斷,立即 假設(shè)此時(shí) 子進(jìn)程也 capture signit 

      if (pid <= 0)
    continue;    /* jumps right to the test */

      /* If the child process did die due to SIGINT, forget our assumption
     that it caught or otherwise handled it. */
      if (WIFSIGNALED (status) && WTERMSIG (status) == SIGINT)
        child_caught_sigint = 0; 

  if (JOBSTATE (job) == JDEAD)
    {
      /* If we're running a shell script and we get a SIGINT with a
     SIGINT trap handler, but the foreground job handles it and
     does not exit due to SIGINT, run the trap handler but do not
     otherwise act as if we got the interrupt. */
// wait_sigint_received :  wait 阻塞中收到sigint,
// child_caught_sigint : 子進(jìn)程 capture sigint
// IS_FOREGROUND : 前臺(tái)進(jìn)程組
// interactive_shell : 交互shell?
// signal_is_trapped : 
      if (wait_sigint_received && interactive_shell == 0 &&
      child_caught_sigint && IS_FOREGROUND (job) &&
      signal_is_trapped (SIGINT))
    {
      int old_frozen;
      wait_sigint_received = 0;
      last_command_exit_value = process_exit_status (child->status);

      old_frozen = jobs_list_frozen;
      jobs_list_frozen = 1;
      tstatus = maybe_call_trap_handler (SIGINT); // 
      jobs_list_frozen = old_frozen;
    }

如何終止該腳本的運(yùn)行呢:

$> kill -9 28833 28834

一定要先殺死父進(jìn)程

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 計(jì)算機(jī)系統(tǒng)漫游 代碼從文本到可執(zhí)行文件的過程(c語言示例):預(yù)處理階段,處理 #inlcude , #defin...
    willdimagine閱讀 3,831評(píng)論 0 5
  • Linux 進(jìn)程管理與程序開發(fā) 進(jìn)程是Linux事務(wù)管理的基本單元,所有的進(jìn)程均擁有自己獨(dú)立的處理環(huán)境和系統(tǒng)資源,...
    JamesPeng閱讀 2,587評(píng)論 1 14
  • 進(jìn)程間的通信主要分為本機(jī)器進(jìn)程間的通信和不同機(jī)器間進(jìn)程的通信。本文主要描述本機(jī)進(jìn)程間的通信。 一、傳統(tǒng)Linux的...
    一葉之界閱讀 496評(píng)論 0 2
  • 昨晚又沒忍住發(fā)火了。做了一份數(shù)學(xué)檢測(cè),沒有不會(huì)的題,但還有不少錯(cuò)的題。能不上火么。寫作業(yè)不認(rèn)真不專心我真是被打敗了...
    三五班楊子賢媽媽閱讀 205評(píng)論 0 0
  • 文『漠黎煙火』 我看到宣傳頁面寫的是:太快了,我的2017就這么走了,我很懷念它。實(shí)話說,這種話,放在2017的前...
    漠黎煙火閱讀 431評(píng)論 0 9

友情鏈接更多精彩內(nèi)容