Go到目前还没有解决成为守护进程(Daemonize)的问题吧?各位是怎么解决的?

这是StackOverFlow上写的,

http://stackoverflow.com/questions/10027477/golang-fork-process/10070021#10070021

还有官网的

http://code.google.com/p/go/issues/detail?id=227

我自己试图抄了一段代码,可以工作,但问题在于我在子进程里面调用了一下time.Sleep后,子进程就不动了。用strace看到子进程卡在epoll调用上。

请问大家是怎么解决这个问题的?就用很土的nohup ./go-exec &这种shell来实现吗?

package main

import (
  "log"
  "os"
  "runtime"
  "errors"
  "syscall"
)


func daemon(chdir bool, closeStd bool) (err error){
  var ret, ret2 uintptr
  var er syscall.Errno

  darwin := runtime.GOOS == "darwin"

  // already a daemon
  if syscall.Getppid() == 1 {
    return
  }

  // fork off the parent process
  ret, ret2, er = syscall.RawSyscall(syscall.SYS_FORK, 0, 0, 0)
  if er != 0 {
    err = errors.New("fork fail")
    return
  }

  // failure
  if ret2 < 0 {
    err = errors.New("fork fail")
    os.Exit(-1)
  }
  // handle exception for darwin
  if darwin && ret2 == 1 {
    ret = 0
  }

  // if we got a good PID, then we call exit the parent process.
  if ret > 0 {
    os.Exit(0)
  }

  /* Change the file mode mask */
  _ = syscall.Umask(0)

  // create a new SID for the child process
  s_ret, s_errno := syscall.Setsid()
  if s_errno != nil {
    log.Printf("Error: syscall.Setsid errno: %d", s_errno)
  }
  if s_ret < 0 {
    err = errors.New("setsid fail")
    return
  }

  if chdir {
    os.Chdir("/")
  }

  if closeStd {
    f, e := os.OpenFile("/dev/null", os.O_RDWR, 0)
    if e == nil {
      fd := f.Fd()
      syscall.Dup2(int(fd), int(os.Stdin.Fd()))
      syscall.Dup2(int(fd), int(os.Stdout.Fd()))
      syscall.Dup2(int(fd), int(os.Stderr.Fd()))
    }
  }

  return
}
阅读 23.8k
4 个回答

go 程序里面因为使用了线程,不能够简单的通过 fork 来实现子进程,否则会出现各种错乱。我记得官方文档里面有提到这个事情的,不过一时找不到链接了。

我自己也是使用 supervisord 来管理 go 进程的。如果需要 go 进程之间通信,推荐使用 socket file 来跨进程通信,不要直接共享 fd,并不方便。

另外,并不是说 go 程序完全做不到共享 fd,我以前曾在 go 里实现过一个安全的通过 fork/exec 跨进程共享 fd 的小框架。代码没有开源,无法直接贴出,抱歉。

我的实现里有两个关键点:

  • 父进程一定要在 fork 之后立即 exec,这样才能保证 go 不会出现莫名奇妙的错误。详见 os package 的 StartProcess() 实现。
  • 通过 fork/exec 创建的子进程是可以共享父进程所有 fd 的,但问题是如何告诉子进程它是子进程,而且怎么和父进程通信。我的做法是通过约定好的特殊环境变量来传递信息,为了信息不被伪造,这里面还做了一些加密。一旦确定父子关系和用来做通信的 fd(通常是一个 pipe),剩下的事情就水到渠成了。

前面用nohup
后来一直用screen或者tmux

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏