Go创建daemon程序

综合编程 2016-06-23

daemon的概念

守护进程(daemon)就是一直在后台运行的进程,它没有控制终端,无法和前台的用户交互。当我们打开一个终端时会创建一个session会话(shell),从用户登录开始到用户退出为止,这段时间内在该终端执行的进程都属于这一个会话。一个会话一般包含一个前台进程组、一个后台进程组和一个会话首进程(shell程序本身)。 例如用以下命令启动5个进程:

$ proc1 | proc2 &

$ proc3 | proc4 | proc5

proc1和proc2属于同一个后台进程组,proc3、proc4、proc5属于同一个前台进程组,Shell进程本身属于一个单独的进程组。这些进程组的控制终端相同,它们属于同一个session。 "后台任务"与"前台任务"的本质区别只有一个:是否继承标准输入。后台任务不再继承当前 session 的标准输入(stdin),你无法向后台任务输入指令了,但是后台任务继承了标准输出(stdout)和标准错误(stderr)后台任务的所有输出依然会同步地在命令行下显示

当终端关闭或者检测到网络连接断开时会将挂断信号(SIGHUP)发送给终端控制进程(会话期首进程,shell进程)。如果会话期首进程接收到SIGHUP信号后会终止,会同时给前台进程组发送SIGHUP信号(进程接收到SIGHUP信号默认处理是退出),shell的 huponexit
参数

(shopt | grep huponexit)

决定了shell退出时是否发送SIGHUP信号给后台进程组。

如何实现守护进程

守护进程要与从启动它的父进程(一般是shell程序)的运行环境隔离开来,需要处理的内容大致包括会话、控制终端、进程组、文件描述符、文件权限掩码以及工作目录等。 ``` 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;

}

```

go语言如何实现守护进程

目前Go程序还不能实现daemon,因为go程序在启动时runtime可能会创建多个线程(用于内存管理,垃圾回收,goroutine管理等),而fork并不能处理好拥有多个线程的进程。

`
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()
}
}

`

使用Supervisord

参考资料

Linux 守护进程的实现
Linux 守护进程的启动方法

您可能感兴趣的

GopherChina 2018 keynote 点评 作为一名参加了两届GopherChina的「老人」,今年为了去沟里吃樱桃,就没去现场凑热闹了。不过,会议的keynote是绝不会错过的。AstaXie也在会议结束后的第一时间放出了 会议的ppt . 看了一下,里面的ppt并不完整,缺了第二天的第一个keynote. 手上有这个资源的同学可以分享我...
Go的context的问题 2017-05-29 最近被由context引发的一个bug坑得不轻,所以反思一下Go的context的问题。 context是隐式的约束,没有检测 如果我们写一个函数,比如: func f(a int, b []byte) { } 我们知道它需要哪些参数,编译器是会帮我做检查的...
Using LZW data compression in Go // LZW Data Compression in Golang package main import "fmt"func compressLZW(testStr string) []int { code := 256 dictionary := m...
SQL2Struct:一款根据sql语句自动生成golang结构体的chrome插件... 最近在用golang写api,用到gorm包进行数据库操作, gorm 是golang中非常流行的一个orm包,使用gorm进行数据库操作前,一般需要先用一个golang结构体对数据表字段进行映射,于是我们经常需要根据数据表中的字段名和类型来手动在go代码中写struct,有时候数据表字段很...
Go 1.11: WebAssembly for the gophers In February 2017, the issue for WebAssembly support was opened at golang/go by Brad Fitzpatrick member of the Go team. Four...