Go語言學習之signal(二)
阿新 • • 發佈:2018-12-22
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接收通道中。