Go创建daemon程序

jasonfang 8年前
   <h2>daemon的概念</h2>    <p>守护进程(daemon)就是一直在后台运行的进程,它没有控制终端,无法和前台的用户交互。当我们打开一个终端时会创建一个session会话(shell),从用户登录开始到用户退出为止,这段时间内在该终端执行的进程都属于这一个会话。一个会话一般包含一个前台进程组、一个后台进程组和一个会话首进程(shell程序本身)。 例如用以下命令启动5个进程:</p>    <p>$ proc1 | proc2 &</p>    <p>$ proc3 | proc4 | proc5</p>    <p>proc1和proc2属于同一个后台进程组,proc3、proc4、proc5属于同一个前台进程组,Shell进程本身属于一个单独的进程组。这些进程组的控制终端相同,它们属于同一个session。 "后台任务"与"前台任务"的本质区别只有一个:是否继承标准输入。后台任务不再继承当前 session 的标准输入(stdin),你无法向后台任务输入指令了,但是后台任务继承了标准输出(stdout)和标准错误(stderr)后台任务的所有输出依然会同步地在命令行下显示</p>    <p>当终端关闭或者检测到网络连接断开时会将挂断信号(SIGHUP)发送给终端控制进程(会话期首进程,shell进程)。如果会话期首进程接收到SIGHUP信号后会终止,会同时给前台进程组发送SIGHUP信号(进程接收到SIGHUP信号默认处理是退出),shell的 <strong>huponexit</strong> 参数</p>    <pre>  <code class="language-go">(shopt | grep huponexit)</code></pre>    <p>决定了shell退出时是否发送SIGHUP信号给后台进程组。</p>    <h2>如何实现守护进程</h2>    <p>守护进程要与从启动它的父进程(一般是shell程序)的运行环境隔离开来,需要处理的内容大致包括会话、控制终端、进程组、文件描述符、文件权限掩码以及工作目录等。</p>    <pre>  <code class="language-go">void init_daemon()  {      pid_t pid;      int i = 0;  // 1. 创建子进程,父进程退出,父进程退出子进程变成孤儿进程,孤儿进程由init进程(pid为1)收养      if ((pid = fork()) == -1) {          printf("Fork error !\n");          exit(1);      }      if (pid != 0) {          exit(0);        // 父进程退出      }    // 2. 子进程调用**setsid**创建新会话,成为成为会话首进程,并且子进程成为一个新进程组的组长进程,而新的会话也脱离了控制终端的控制。  //(组长进程调用 setsid会出错,所以第一步fork出子进程,fork出的子进程一定不会是组长进程)      setsid();          //3. 子进程变成无终端的会话首进程,但是它仍然可以重新申请打开一个控制终端。可以通过再次创建子进程结束当前进程,使进程不再是会话首进程来禁止进程重新打开控制终端。      if ((pid = fork()) == -1) {          printf("Fork error !\n");          exit(-1);      }      if (pid != 0) {          exit(0);          }      //4. 改变当前目录为根目录,重设文件权限掩码,关闭文件描述符      //因为这几样东西都是继承自父进程的      chdir("/tmp");      // 改变工作目录      umask(0);           // 重设文件掩码      for (; i < getdtablesize(); ++i) {         close(i);        // 关闭打开的文件描述符      }        return;  }  </code></pre>    <h2>go语言如何实现守护进程</h2>    <p>目前Go程序还不能实现daemon,因为go程序在启动时runtime可能会创建多个线程(用于内存管理,垃圾回收,goroutine管理等),而fork并不能处理好拥有多个线程的进程。</p>    <pre>  <code class="language-go">d := flag.Bool("d", false, "Whether or not to launch in the background(like a daemon)")  if *d {      cmd := exec.Command(os.Args[0],          "-close-fds",      )      serr, err := cmd.StderrPipe()      if err != nil {          log.Fatalln(err)      }      err = cmd.Start()      if err != nil {          log.Fatalln(err)      }      s, err := ioutil.ReadAll(serr)      s = bytes.TrimSpace(s)      if bytes.HasPrefix(s, []byte("addr: ")) {          fmt.Println(string(s))          cmd.Process.Release()      } else {          cmd.Process.Kill()      }  }  </code></pre>    <p>`</p>    <h2>参考资料</h2>    <p><a href="http://www.open-open.com/lib/view/open1466731715992.html" rel="nofollow,noindex">Linux 守护进程的实现</a></p>    <p><a href="http://www.open-open.com/lib/view/open1456658189000.html">Linux 守护进程的启动方法</a></p>    <p> </p>    <p>来自: <a href="/misc/goto?guid=4959674786771836575" rel="nofollow">http://shanks.leanote.com/post/Go创建daemon程序</a></p>    <p> </p>