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

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

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

大概情况就是这样的(仅做问题浮现)

go 复制代码
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()
}

命令行启动后再被kill掉过后,监听1999端口的进程就停不下来了,由于业务其实很多个这样的子进程成了僵尸进程。我当时第一反应不就是以前c fork一个子进程来当守护进程然后主程序退出的操作。

其实有种不讲武德的操作可以管理这种僵尸进程,当我拿出cgo助攻一小段,阁下又该如何应对

go 复制代码
package main

import (
	"flag"
	"fmt"
	"log"
	"net"
	"os"
	"os/exec"
	"os/signal"
	"syscall"
	"time"
	"unsafe"
)

//#include <unistd.h>
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
	}
}

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

相关推荐
煤泥做不到的!10 分钟前
挑战一个月基本掌握C++(第十一天)进阶文件,异常处理,动态内存
开发语言·c++
F-2H12 分钟前
C语言:指针4(常量指针和指针常量及动态内存分配)
java·linux·c语言·开发语言·前端·c++
aherhuo15 分钟前
基于openEuler22.09部署OpenStack Yoga云平台(一)
linux·运维·服务器·openstack
WebDeveloper200118 分钟前
如何使用美国域名中心US Domain Center和WordPress创建商业网站
运维·服务器·css·网络·html
檀越剑指大厂42 分钟前
【Linux系列】Shell 脚本中的条件判断:`[ ]`与`[[ ]]`的比较
linux·运维·服务器
bryant_meng1 小时前
【python】OpenCV—Image Moments
开发语言·python·opencv·moments·图片矩
若亦_Royi2 小时前
C++ 的大括号的用法合集
开发语言·c++
慕城南风2 小时前
Go语言中的defer,panic,recover 与错误处理
golang·go
资源补给站3 小时前
大恒相机开发(2)—Python软触发调用采集图像
开发语言·python·数码相机
2301_819287123 小时前
ce第六次作业
linux·运维·服务器·网络