Golang 中创建守护进程的正确姿势

一直对守护进程有点迷糊, Srs的守护进程是通过两次fork实现的。
C/C++一般的做法是fork一次, 然后调用setsid() 实现daemon, golang呢?

守护进程的特点

守护进程: 我的理解是脱离控制台的进程。
一般我们执行程序都是在shell/bash下启动程序, 如果不以守护进程方式启动,该程序的父进程一般就是bash或shell, 通过ps -ef | grep xxx, 看第三列的内容, 可以看到当前进程的父进程。

golang中启动daemon其实很简单, 话不多说上代码:


func main() {
      // when os.Getppid() != 1, not in daemon
	   if os.Getppid() != 1 {
	   
		// create log file: when run in daemon success, but log file has not init, stdout/stderr will dup2 the log
		// 这里为什么这样做是因为: 如果我们daemon启动成功了, 父进程会返回, 子进程运行时脱离控制台, 当我们直接向控制台再次打印日志时, 会被定向到/dev/null, 所以如果有什么错误信息, 我们是看不到的。所以最好的做法时, 先创建一个文件, 将子进程输出到控制台的日志dup2 到该日志文件
	   		createLogFile := func (fileName string) (fd *os.File, err error) {
			dir := path.Dir(fileName)
			if _, err = os.Stat(dir); err != nil && os.IsNotExist(err) {
				if err = os.MkdirAll(dir, 0755); err != nil {
					logs.Error("Start-Daemon: create dir: %s failed, err is: %v", dir, err)
					return
				}
			}

			if fd, err = os.Create(fileName); err != nil {
				logs.Error("Start-Daemon: create log file: %s failed, err is: %v", fileName, err)
				return
			}

			return
		}

        // logFd 的close放在子进程, 其实这里不知道会不会有问题。 fork时, 肯定会传递logFd到子进程, 但是通过StartProcess不晓得会不会?
		logFd, err := createLogFile(config.FileName)
		if err != nil {
			return
		}
		defer logFd.Close()
		
		// start Daemon
	    cmdName := os.Args[0]
       // 为什么这样直接就能创建守护进程? 按理说, 应该 设置os.ProcAttr中Sys(*syscall.SysProcAttr)的Setsid为true才对?
       /*
		type SysProcAttr struct {
			Chroot       string         // Chroot.
			Credential   *Credential    // Credential.
			Ptrace       bool           // Enable tracing.
			Setsid       bool           // Create session.
			Setpgid      bool           // Set process group ID to Pgid, or, if Pgid == 0, to new pid.
			Setctty      bool           // Set controlling terminal to fd Ctty (only meaningful if Setsid is set)
			Noctty       bool           // Detach fd 0 from controlling terminal
			Ctty         int            // Controlling TTY fd
			Foreground   bool           // Place child's process group in foreground. (Implies Setpgid. Uses Ctty as fd of controlling TTY)
			Pgid         int            // Child's process group ID if Setpgid.
			Pdeathsig    Signal         // Signal that the process will get when its parent dies (Linux only)
			Cloneflags   uintptr        // Flags for clone calls (Linux only)
			Unshareflags uintptr        // Flags for unshare calls (Linux only)
			UidMappings  []SysProcIDMap // User ID mappings for user namespaces.
			GidMappings  []SysProcIDMap // Group ID mappings for user namespaces.
			// GidMappingsEnableSetgroups enabling setgroups syscall.
			// If false, then setgroups syscall will be disabled for the child process.
			// This parameter is no-op if GidMappings == nil. Otherwise for unprivileged
			// users this should be set to false for mappings work.
			GidMappingsEnableSetgroups bool
			AmbientCaps                []uintptr // Ambient capabilities (Linux only)
		}
        */
	    newProc, err := os.StartProcess(cmdName, os.Args, &os.ProcAttr{Files: []*os.File{logFd, logFd, logFd}})
	    if err != nil {
		    logs.Error("Start-Deamon: start process: %s failed, err is: %v", cmdName, err)
		    return
	    }

	    logs.Trace("Start-Deamon: run in daemon success, pid: %v", newProc.Pid)
	    return
	}

	for {
		// will write "test" to file 
		fmt.Prinln("test")
		time.Sleep(time.Duration(3) * time.Second)
	}
}

你可能感兴趣的:(Golang)