如何仅使用Lua模块直接实现Lua脚本的守护进程化?
Lua脚本守护进程化纯Lua实现方案
注:以下方案均不依赖systemd、start-stop-daemon等外部工具,仅通过Lua模块实现。
方案1:使用新版luaposix实现(最通用)
很多开发者找不到setsid是因为该接口不在posix.unistd子模块,而是在luaposix的顶层posix命名空间下,v33及以上版本的luaposix均默认支持该接口,可直接通过luarocks安装最新版本:luarocks install luaposix
完整实现代码如下:
local posix = require 'posix' local unistd = require 'posix.unistd' local fcntl = require 'posix.fcntl' local function daemonize() -- 第一次fork,父进程直接退出,子进程后台运行 local pid = unistd.fork() if pid < 0 then error("首次fork失败") end if pid > 0 then os.exit(0) end -- 调用setsid新建会话,脱离原控制终端、进程组、会话组 local sid = posix.setsid() if sid < 0 then error("setsid调用失败") end -- 第二次fork,子进程不再是会话首进程,避免后续重新获取控制终端 pid = unistd.fork() if pid < 0 then error("二次fork失败") end if pid > 0 then os.exit(0) end -- 修改umask,避免继承父进程的权限限制 posix.umask(0) -- 切换工作目录到根目录,避免占用挂载点导致无法卸载 unistd.chdir("/") -- 重定向标准输入、输出、错误流到/dev/null local devnull_fd = fcntl.open("/dev/null", fcntl.O_RDWR) unistd.dup2(devnull_fd, 0) unistd.dup2(devnull_fd, 1) unistd.dup2(devnull_fd, 2) if devnull_fd > 2 then unistd.close(devnull_fd) end end -- 调用后进入守护进程模式 daemonize() -- 此处写入你的业务逻辑即可 while true do -- 示例:每10秒执行一次任务 unistd.sleep(10) end
方案2:基于FFI调用系统接口实现(适合LuaJIT环境)
如果你的环境使用LuaJIT,或者安装了luaffi模块,不需要依赖任何第三方Lua库,直接通过FFI调用系统函数即可实现守护进程化,代码如下:
local ffi = require "ffi" -- 声明需要用到的系统函数原型 ffi.cdef[[ int setsid(void); int fork(void); int chdir(const char *path); int umask(int mask); int open(const char *pathname, int flags, int mode); int dup2(int oldfd, int newfd); int close(int fd); void exit(int status); ]] local function daemonize() local O_RDWR = 2 -- 第一次fork local pid = ffi.C.fork() if pid < 0 then error("首次fork失败") end if pid > 0 then ffi.C.exit(0) end -- 调用setsid local sid = ffi.C.setsid() if sid < 0 then error("setsid调用失败") end -- 第二次fork pid = ffi.C.fork() if pid < 0 then error("二次fork失败") end if pid > 0 then ffi.C.exit(0) end ffi.C.umask(0) ffi.C.chdir("/") local devnull_fd = ffi.C.open("/dev/null", O_RDWR, 0) ffi.C.dup2(devnull_fd, 0) ffi.C.dup2(devnull_fd, 1) ffi.C.dup2(devnull_fd, 2) if devnull_fd > 2 then ffi.C.close(devnull_fd) end end daemonize() -- 业务逻辑 while true do ffi.C.sleep(10) end
备选方案:极简C绑定实现
如果上述两种方案都不适用,你不需要完整复刻luadaemon,只需要写10行以内的C代码绑定setsid接口即可,编译后生成的so模块可直接在Lua中调用,实现成本极低。
内容的提问来源于stack exchange,提问作者Daniel
相关产品推荐
相关产品推荐

