golang 编程练习项目_Golang实现守护进程

a001841157dfda12bd9665a8ab712547.png

黑客技术 点击右侧关注,了解黑客的世界! bd36d5dae6b2ef582f93528929b460a7.png

39d6caafde415077aafccd9cf13b3210.png

Linux编程 点击右侧关注,免费入门到精通! bd36d5dae6b2ef582f93528929b460a7.png

https://mp.weixin.qq.com/s/bJWEg0A_t6e9k43YUnWrJA

因为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 daemonimport ("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 := falsefor 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”参数给子进程.

【无门槛免费领】

535G超强程序员编程

0基础从入门到精通自学视频教程!

c5d037c56834cfdf1b4c2902d0490dad.png

64d689044849d687174947900813818c.png 2559e91c14953ab930c11db6832e115b.png

5603bc873bb275864b48de2a885eff26.png 642e214fd173673e477e11268d9e8429.png 31509c1912daa365d67966c941ccb276.png

e83f7a14becac576590c4f5357e6ceed.png 6ad8beb36149841892d9c0def3a1126f.png 47821aceab830d471791977aab6d571a.png

a57d51ee5e32afbf58f0dbc7576c190c.png 40bae6fd6586c8db57c0f45adbe7136b.png e83f7a14becac576590c4f5357e6ceed.png

5f488924f219cd9f667f587d3118337e.png f143a38acc9910a13a8b3aa9406fa58f.png 4fc6cbe6dadb23b4958f4329f6efdeef.png 4a3d51aa192a74cac2c821e4de214bec.png

f1fc9e78cda1fa854699d04dfae70646.png万水千山总是情,点个 “ 在看” 行不行
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值