1. 程式人生 > >Go語言學習之signal(二)

Go語言學習之signal(二)

package main

import (
    "bytes"
    "errors"
    "fmt"
    "io"
    "os"
    "os/exec"
    "os/signal"
    "runtime/debug"
    "strconv"
    "strings"
    "sync"
    "syscall"
    "time"
)

func main() {
    go func() {
        time.Sleep(5 * time.Second)
        sendSignal()
    }()
    handleSignal()
}

func
handleSignal() { /* 這裡先後呼叫了兩次signal.Notify函式, 第一次呼叫時設定的訊號集合中包含SIGINT,SINGQUIT兩個訊號 第二次呼叫時只設定了SIGQUIT一個訊號 如果當前程序接收的時SIGQUIT訊號,則分別傳送給sigRecv1和sigRecv2兩個通道 如果接收到的時SIGINT訊號,則只發送給sigRecv1這一個通道 */ sigRecv1 := make(chan os.Signal, 1) sigs1 := []os.Signal{syscall.SIGILL, syscall.SIGQUIT} fmt.Printf("Set notfication for %s...[sigRecv1]\n"
, sigs1) signal.Notify(sigRecv1, sigs1...) sigRecv2 := make(chan os.Signal, 1) sigs2 := []os.Signal{syscall.SIGQUIT} fmt.Printf("Set notification for %s...[sigRecv2]\n", sigs2) signal.Notify(sigRecv2, sigs2...) /* sync,WaitGroup型別值wg的Add方法。添加了一個型別值為2的差量,然後在每段併發程式後面, 分別呼叫了wg的Done方法,該發放可以視為使差量減1, 在最後,還應該呼叫wg的Wait方法,該方法 會一直阻塞, 知道差量變為0 */
var wg sync.WaitGroup wg.Add(2) go func() { for sig := range sigRecv1 { fmt.Printf("Received a signal from sigRecv1: %s\n", sig) } fmt.Printf("End. [sigRecv1]\n") wg.Done() }() go func() { for sig := range sigRecv2 { fmt.Printf("Received a signal from sigRecv2: %s\n", sig) } fmt.Printf("End. [sigRecv2]\n") wg.Done() }() fmt.Println("Wait for 2 seconds... ") time.Sleep(2 * time.Second) fmt.Printf("Stop notification...") signal.Stop(sigRecv1) close(sigRecv1) fmt.Printf("done. [SigRecv1]\n") wg.Wait() } func sendSignal() { defer func() { if err := recover(); err != nil { fmt.Printf("Fatal Error: %s\n", err) debug.PrintStack() } }() cmds := []*exec.Cmd{ exec.Command("ps", "aux"), exec.Command("grep", "signal"), exec.Command("grep", "-v", "grep"), exec.Command("grep", "-v", "go run"), exec.Command("awk", "{print $2}"), } output, err := runCmds(cmds) if err != nil { fmt.Printf("Command Execution Error:", err) return } pids, err := getPids(output) if err != nil { fmt.Printf("PID Parsing Error: %s\n", err) return } fmt.Printf("Target PID(s):\n%v\n", pids) for _, pid := range pids { proc, err := os.FindProcess(pid) if err != nil { fmt.Printf("Process Finding Error: %s\n", err) return } sig := syscall.SIGQUIT fmt.Printf("Send signal '%s' to the process (pid=%d)...\n", sig, pid) err = proc.Signal(sig) if err != nil { fmt.Printf("Signal Sending Error:%s\n", err) return } } } func getPids(strs []string) ([]int, error) { var pids []int for _, str := range strs { pid, err := strconv.Atoi(strings.TrimSpace(str)) if err != nil { return nil, err } pids = append(pids, pid) } return pids, nil } func runCmds(cmds []*exec.Cmd) ([]string, error) { if cmds == nil || len(cmds) == 0 { return nil, errors.New("The cmd slice is incvalid!") } first := true var output []byte var err error for _, cmd := range cmds { fmt.Printf("Run command:%v\n", getCmdPlaintext(cmd)) if !first { var stdinBuf bytes.Buffer stdinBuf.Write(output) cmd.Stdin = &stdinBuf } var stdoutBuf bytes.Buffer cmd.Stdout = &stdoutBuf if err = cmd.Start(); err != nil { return nil, getError(err, cmd) } if err = cmd.Wait(); err != nil { return nil, getError(err, cmd) } output = stdoutBuf.Bytes() fmt.Printf("Output: \n%s\n", string(output)) if first { first = false } } var lines []string var outputBuf bytes.Buffer outputBuf.Write(output) for { line, err := outputBuf.ReadBytes('\n') if err != nil { if err == io.EOF { break } else { return nil, getError(err, nil) } } lines = append(lines, string(line)) } return lines, nil } func getCmdPlaintext(cmd *exec.Cmd) string { var buf bytes.Buffer buf.WriteString(cmd.Path) for _, arg := range cmd.Args[1:] { buf.WriteRune(' ') buf.WriteString(arg) } return buf.String() } func getError(err error, cmd *exec.Cmd, extraInfo ...string) error { var errMsg string if cmd != nil { errMsg = fmt.Sprintf("%s [%s %v]", err, (*cmd).Path, (*cmd).Args) } else { errMsg = fmt.Sprintf("%s", err) } if len(extraInfo) > 0 { errMsg = fmt.Sprintf("%s (%v)", errMsg, extraInfo) } return errors.New(errMsg) }

程式碼包os/signal中Notify函式用來當作業系統向當前程序傳送指定訊號時發出通知

func Notify(c chan<-os.Signal, sig ...os.Signal)

其中第一個引數是通道型別的,該引數的型別的chan<-os.Signal,這表示引數c是一個傳送通道,在Notify函式中,只能向它傳送os.Signal型別的值(以下簡稱訊號值),而不能從中接收訊號值。signal.Notify函式會把當前程序中接收到的指定訊號放入引數c代表的通道型別值中,這樣該函式的呼叫方就可以從這個signal接收通道中按順序獲取作業系統發來的訊號並進行相應的處理。

第二個引數是可變長的引數,這意味著我們在呼叫signal.Notify函式時,可以在第一個引數值之後再附加任意個os.Signal型別的值,os/signal包中的程式會把它封裝成syscall.Signal型別的值並放入到signal接收通道中。