Golang实现守护进程
因为Golang没有Linux的fork()系统调用, 所以实现守护进程要使用一些小技巧. Golang为*nix(unix/linux/FreeBSD...)系统提供了syscall.ForkExec()调用, 这个调用跟fork()调用不一样, syscall.ForkExec需要提供一个要执行的程序路径. syscall.ForkExec()原型如下:
func ForkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error)
第一参数是要执行程序的路径, 第二个参数是执行程序时提供的参数列表, 第三参数提供执行程序时的一些属性.
另外需要注意的是, syscall.ForkExec()调用会创建一个新进程然后再执行第一个参数的程序. 所以调用这个函数之后会有两个进程: 一个是调用syscall.ForkExec()的父进程, 另外一个是被新创建的子进程, 子进程会执行参数中的程序.
有了syscall.ForkExec()调用之后, 我们就可以实现一个守护进程功能了. 一般守护进程的流程如下:
1. fork一个新的子进程.
2. 为子进程创建一个新的回话.
3. 把标准输入输出指向null设备.
4. 退出父进程.
用C语言来实现代码如下:
-
void daemonize(void)
{
-
int fd;
-
if
(fork()
!=
0) exit(0);
-
setsid();
/* 创建新回话 */
-
if
((fd = open("/dev/null", O_RDWR,
0))
!=
-1)
{
dup2(fd, STDIN_FILENO);
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
-
if
(fd > STDERR_FILENO) close(fd);
-
}
}
那么用Golang如何实现呢? 因为Golang的syscall.ForkExec()函数需要指定要执行的程序. 所以不能像C语言一样分叉执行代码. 这时我们可以通过一个小技巧来实现父子进程执行不同的代码, 这个技巧就是通过参数来实现.
我们可以在执行子进程程序时传递一个特有的参数来区分当前进程是否子进程, 例如我们可以传递”--daemon”参数. 因为父进程没有接收到”--daemon”参数, 所以被认为是父进程, 而子进程收到”--daemon”参数, 所以知道是子进程. 代码实现如下:
package daemon
-
import
(
-
"errors"
-
"os"
-
"runtime"
-
"syscall"
)
-
const daemonFlagName =
"--daemon"
-
func initDaemonRuntime()
{
-
// 创建新回话
_, err := syscall.Setsid()
-
if err !=
nil
{
-
return
-
}
-
// 把标准输入输出指向null
-
fd, err := os.OpenFile("/dev/null", os.O_RDWR,
0)
-
if err !=
nil
{
-
return
-
}
-
_ = syscall.Dup2(int(fd.Fd()),
int(os.Stdin.Fd()))
-
_ = syscall.Dup2(int(fd.Fd()),
int(os.Stdout.Fd()))
-
_ = syscall.Dup2(int(fd.Fd()),
int(os.Stderr.Fd()))
-
if fd.Fd()
> os.Stderr.Fd()
{
_ = fd.Close()
-
}
}
-
func Daemon()
(int, error)
{
-
if runtime.GOOS ==
"windows"
{
-
return
-1, errors.New("unsupported windows operating system")
-
}
-
isDaemon :=
false
-
for i :=
1; i < len(os.Args); i++
{
-
if os.Args[i]
== daemonFlagName {
-
isDaemon =
true
-
}
-
}
-
if isDaemon {
// daemon process
initDaemonRuntime()
-
return
0,
nil
-
}
procPath := os.Args[0]
-
// 添加"--daemon"参数
-
args := make([]string,
0, len(os.Args)+1)
args = append(args, os.Args...)
args = append(args, daemonFlagName)
-
attr :=
&syscall.ProcAttr{
-
Env: os.Environ(),
-
Files:
[]uintptr{os.Stdin.Fd(), os.Stdout.Fd(), os.Stderr.Fd()},
-
}
pid, err := syscall.ForkExec(procPath, args, attr)
-
if err !=
nil
{
-
return
-1, err
-
}
-
return pid,
nil
}
在Daemon()函数中, 首先判断是否有”--daemon”参数, 如果有这个参数说明是子进程, 那么就初始化子进程的运行环境, 然后返回. 如果没有”--daemon”参数, 说明是父进程, 那么就调用syscall.ForkExec()函数来执行当前程序. 执行程序之前记得要添加”--daemon”参数给子进程.
- 如何利用ETW(Event Tracing for Windows)记录日志
- 如何利用ETW(Event Tracing for Windows)记录日志
- 如何利用ETW(Event Tracing for Windows)记录日志
- ASP.NET MVC以ValueProvider为核心的值提供系统: DictionaryValueProvider
- ASP.NET MVC如何实现自定义验证(服务端验证+客户端验证)
- .NET Core的文件系统[2]:FileProvider是个什么东西?
- Python多线程怎样优雅的响应中断异常
- .NET Core的文件系统[3]:由PhysicalFileProvider构建的物理文件系统
- .NET Core的文件系统[4]:由EmbeddedFileProvider构建的内嵌(资源)文件系统
- 学习July博文总结——支持向量机(SVM)的深入理解(下)
- 在gridview和datagrid里设置列宽
- ASP.NET MVC的Model元数据与Model模板:将”ListControl”引入ASP.NET MVC
- .NET Core的文件系统[5]:扩展文件系统构建一个简易版“云盘”
- 全球15%工作将被自动化,中国1亿人将面临失业
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法