iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 25
2

前言

昨天已經成功讓 Shell 接到 Signal 而且不被打死,今天要來看看怎麼把 Signal 送給正在執行的 Process

程式流程

因為使用者按下 <Ctrl>-C 時不見得有程式正在執行,像昨天的範例就是在等待使用者輸入的情況下收到 SIGINT

所以根據收到 SIGINT 的當下有沒有程式在執行,可以分成兩種情況:

  • 如果收到 SIGINT 時 有 Process 正在執行 ,那就把 SIGINT 轉給那個 Process 讓他停止,就像是前天在 zsh 中把正在執行的 ping google.com 停掉

  • 如果收到 SIGINT 的當下 沒有程式在跑 ,那就直接忽略 SIGINT,並且顯示出一個新的 prompt(如下圖 zsh 的做法)


實作

會用到的 function

把正在執行的程式記起來

為了要判斷收到 Signal 的當下有沒有指令正在跑,必須在執行指令之前把指令變數紀錄起來

// 宣告一個變數 currentCmd 來記錄目前正在執行的指令
var currentCmd *exec.Cmd

func executeInput(input string) error {
    // ...

    cmd := exec.Command(args[0], args[1:]...)

    cmd.Stdin = inputStream
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr

    /* 以上是之前寫的程式碼 */


    // 在執行之前把要執行的指令記起來
    // 表示目前正在執行這個指令
    currentCmd = cmd
  
    err := cmd.Run()
  
    // 執行結束之後就把 currentCmd 設為 nil
    // 表示目前沒有在執行指令
    currentCmd = nil

    return err
}

收到 Signal 時的動作

在 handleSignals 裡面判斷目前有沒有指令在執行,並執行相對應的動作

func main(){
    // ...
    
    handleSignals := func() {
        for {
            sig := <-signalCh

            // 判斷收到 Signal 時 currentCmd 是不是 nil
            // 也就是當下有沒有指令在執行
            if currentCmd != nil {
                // 如果收到 Signal 時有指令在跑
                // 那就把 Signal 轉送給那個 Process
                currentCmd.Process.Signal(sig)
            } else {
                // 如果收到 Signal 時沒有指令在跑
                // 那就顯示一個新的 prompt
                fmt.Println()
                showPrompt()
            }
        }
    }

    go handleSignals()
}

Demo

  • 在 Gosh 裡面按 <Ctrl>-C 停掉正在執行的 ping google.com

  • 若沒有指令正在執行,則顯示出一行新的 prompt

小結

View commit on Github

今天終於把轉發 Signal 的任務完成了~雖然這邊只示範轉發 SIGINT,但其他 signal 也是類似的做法,有興趣的話可以自己做做看~

延伸閱讀


上一篇
Day24-Signal 訊號(二)
下一篇
Day26-& 背景執行
系列文
Gosh!原來用 Go 寫一個 Unix Shell 這麼簡單30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言