• go 程序被意外kill后出现僵尸进程解决方案


    go 管理自身子进程(防止僵尸进程出现)

    写这篇文章是因为最近有同事竟然会知道异步启动子进程,不会关闭,最后导致导致僵尸进程出现,而且由于子进程会随着业务的使用越开越多,主进程一旦被kill掉就会不得不手动一个一个kill。
    大概情况就是这样的(仅做问题浮现)

    package main
    
    import (
    	"flag"
    	"fmt"
    	"log"
    	"net"
    	"os"
    	"os/exec"
    	"os/signal"
    	"syscall"
    )
    
    func child() {
    	li, err := net.Listen("tcp", "127.0.0.1:1999")
    	if err != nil {
    		log.Fatalln(err)
    	}
    	ch := make(chan os.Signal, 1)
    	signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
    	<-ch
    	li.Close()
    }
    func main() {
    	fmt.Println(os.Getpid())
    	ischild := flag.Bool("child", false, "child")
    	flag.Parse()
    	if *ischild {
    		child()
    		return
    	}
    	cmd := exec.Command("./demo", "--child")//随着业务进展,这个是长期运行的,会起很多个
    	cmd.Start()
    	cmd.Wait()
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    命令行启动后再被kill掉过后,监听1999端口的进程就停不下来了,由于业务其实很多个这样的子进程成了僵尸进程。我当时第一反应不就是以前c fork一个子进程来当守护进程然后主程序退出的操作。
    其实有种不讲武德的操作可以管理这种僵尸进程,当我拿出cgo助攻一小段,阁下又该如何应对

    package main
    
    import (
    	"flag"
    	"fmt"
    	"log"
    	"net"
    	"os"
    	"os/exec"
    	"os/signal"
    	"syscall"
    	"time"
    	"unsafe"
    )
    
    //#include 
    import "C"
    
    func Fork() int32 {
    	return int32(C.fork())
    }
    func child() {
    	li, err := net.Listen("tcp", "127.0.0.1:1999")
    	if err != nil {
    		log.Fatalln(err)
    	}
    	ch := make(chan os.Signal, 1)
    	signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
    	<-ch
    	li.Close()
    }
    //简简单单实现一个子进程管理器,走unix socket 流。你可以用其它ipc方式
    func process_manage() {
    	lis, err := net.ListenUnix("unix", &net.UnixAddr{Name: "man.sock"})
    	if err != nil {
    		log.Fatalln("listen man.sock failed " + err.Error())
    	}
    	var (
    		size int
    		buff []byte = make([]byte, unsafe.Sizeof(size))
    
    		con net.Conn
    		pid *int = (*int)(unsafe.Pointer(&buff[0]))
    	)
    	var pidlist []int
    	for err == nil {
    		con, err = lis.Accept()
    		for err == nil {
    			_, err = con.Read(buff)
    			if err == nil {
    				if *pid != 0 {
    					pidlist = append(pidlist, *pid)
    				}
    			}
    		}
    	}
    	lis.Close()
    	for _, cpid := range pidlist {
    		err = syscall.Kill(cpid, syscall.SIGINT)
    		if err != nil {
    			fmt.Fprintln(os.Stderr, "send to pid", cpid, "failed", err)
    		}
    	}
    }
    func main() {
    	ischild := flag.Bool("child", false, "child")
    	flag.Parse()
    	if *ischild {
    		child()
    		return
    	}
    	switch Fork() {
    	case 0:
    		process_manage()
    		return
    	case -1:
    		log.Fatalln("crate child process failed")
    		return
    	default:
    		fmt.Println(os.Getpid())
    		time.Sleep(time.Millisecond * 300)
    		con, err := net.Dial("unix", "man.sock")
    		if err != nil {
    			log.Fatalln("dial man.sock failed " + err.Error())
    		}
    		var size int
    		var buff []byte = make([]byte, unsafe.Sizeof(size))
    		cmd := exec.Command("./demo", "--child")
    		cmd.Start()
    		var pidptr *int = (*int)(unsafe.Pointer(&buff[0]))
    		*pidptr = cmd.Process.Pid
    		_, err = con.Write(buff)
    		if err != nil {
    			fmt.Fprintln(os.Stderr, "write to daemon failed", err)
    		}
    		cmd.Wait()
    		return
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100

    程序每异步开启一个子进程命令就把pid传送给我们的守护进程,若主进程被kill了,主进程和守护进程之间连接就会断,守护进程将给所有开启的子进程发送SIGINT信号,推荐SIGINT,SIGTERM。这两个可以捕获,大家也都知道这两个信号。这里我图方便和守护进程之间通信直接用的unix socket流,你也可以用其它ipc
    请添加图片描述

  • 相关阅读:
    Mapper.xml文件如何写集合遍历语句
    关于架构极客大学java进阶训练营
    技术管理进阶——管理者如何做绩效沟通及把控风险
    linux服务器磁盘满了怎么办
    HTB-Toolbox
    JSP response,request操作中(中文乱码)-如何解决呢?
    module内包含第三方aar包,完整打包上传maven的方法
    NLP(3)--GAN
    AI带你省钱旅游!精准预测民宿房源价格!
    LCR 180.文件组合
  • 原文地址:https://blog.csdn.net/A_super_C/article/details/136491315