Go语言学习之signal(二)

来源:互联网 发布:mac万得股票机构 编辑:程序博客网 时间:2024/06/15 02:31
package mainimport (    "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接收通道中。