在linux下这件事实在是太容易了。
先建个Daemon.rb
require 'fileutils'
module Daemon
WorkingDirectory = File.expand_path(File.dirname(__FILE__))
class Base
def self.pid_fn
File.join(WorkingDirectory, "#{name}.pid")
end
def self.daemonize
Controller.daemonize(self)
end
end
module PidFile
def self.store(daemon, pid)
File.open(daemon.pid_fn, 'w') {|f| f << pid}
end
def self.recall(daemon)
IO.read(daemon.pid_fn).to_i rescue nil
end
end
module Controller
def self.daemonize(daemon)
case !ARGV.empty? && ARGV[0]
when 'start'
start(daemon)
when 'stop'
stop(daemon)
when 'restart'
stop(daemon)
start(daemon)
else
puts "Invalid command. Please specify start, stop or restart."
exit
end
end
def self.start(daemon)
fork do
Process.setsid
exit if fork
PidFile.store(daemon, Process.pid)
Dir.chdir WorkingDirectory
File.umask 0000
STDIN.reopen "/dev/null"
STDOUT.reopen "/dev/null", "a"
STDERR.reopen STDOUT
trap("TERM") {daemon.stop; exit}
daemon.start
end
end
def self.stop(daemon)
if !File.file?(daemon.pid_fn)
puts "Pid file not found. Is the daemon started?"
exit
end
pid = PidFile.recall(daemon)
FileUtils.rm(daemon.pid_fn)
pid && Process.kill("TERM", pid)
end
end
end
这是一个典型的后台进程控制类,它创建一个pid,然后使用fork产生进程,将输入输出重定向。当然也可以输出到日志文件里。
然后要做的就很简单了,先继承,然后建立一个start和stop方法,在start方法里一定要阻塞进程。在stop方法里销毁初始化的东西。
class Robot < Daemon::Base
.........
def self.start
init_all()
while true
sleep(10)
end
end
def self.stop
destory_all()
end
.........
end
最常见的阻塞办法就是无限循环,当然了,一定要记得sleep,否则会占用100%CPU。
或者是用其他阻塞办法,反正不能结束掉。
这样使用 ruby robot.rb start 启动后就会启动一个守护进程,然后可以用ps看到守护进程活着,并且可以看到robot.rb所在目录会生成一个.pid文件,这个就是存储pid的内容。
使用 ruby robot.rb stop 关闭进程。它会发送关闭信息,同时删除pid文件。
注意在进程里使用路径一定要用绝对路径,使用相当路径是不可靠的,或者是使用本文件的相对路径,然后计算出绝对路径,例如:
WORKING_DIRECTORY = File.expand_path(File.dirname(__FILE__))
CONFIG_DIRECTORY=File.join(WORKING_DIRECTORY,"../config")
XMPP_ACCOUNT_FILE=File.join(CONFIG_DIRECTORY,"xmpp_account.yml")
DB_CONFIG_FILE=File.join(CONFIG_DIRECTORY,"database.yml")
MSN_ACCOUNT_FILE=File.join(CONFIG_DIRECTORY,"msn_account.yml")