Wednesday, October 28, 2009

Recipe 20.1. Running a Daemon Process on Unix










Recipe 20.1. Running a Daemon Process on Unix





Problem


You want to run a process in the background with minimal interference from users and the operating system.




Solution


In Ruby 1.9, you can simply call Process.daemon to turn the current process into a daemon. Otherwise, the most reliable way is to use the
Daemonize
module. It's not available as a gem, but it's worth downloading and installing, because it makes it easy and reliable to write a daemon:



#!/usr/bin/ruby -w
#
daemonize_daemon.rb
require 'tempfile'
require 'daemonize'
include Daemonize # Import Daemonize::daemonize into this namespace

puts 'About to daemonize.'
daemonize # Now you're a daemon process!
log = Tempfile.new('daemon.log')
loop do
log.puts "I'm a daemon, doin' daemon things."
log.flush
sleep 5
end



If you run this code at the command line, you'll get back a new prompt almost immediately. But there will still be a Ruby process running in the background, writing to a temporary file every five seconds:



$ ./daemonize_daemon.rb
About to daemonize.
$ ps x | grep daemon
4472 ? S 0:00 ruby
daemonize_daemon.rb
4474 pts/2 S+ 0:00 grep daemon

$ cat /tmp/daemon.log4472.0
I'm a daemon, doin' daemon things.
I'm a daemon, doin' daemon things.
I'm a daemon, doin' daemon things.



Since it runs an infinite loop, this daemon process will run until you kill it:



$ kill 4472

$ ps x | grep daemon
4569 pts/2 S+ 0:00 grep daemon



A different daemon might run until some condition is met, or until it receives a Unix signal, or a "stop" message through some interface.




Discussion


A daemon process is one that runs in the
background, without any direct user interface at all. Servers are usually
daemon processes, but you might also write a daemon to do monitoring or task scheduling.


Rather than replacing your process with a daemon process, you may want to spawn a daemon while continuing with your original work. The best strategy for this is to spawn a subprocess with Kernel#fork.


Ruby's fork implementation takes a code block to be run by the subprocess. The code defined after the block is run in the original process. So pass your daemonizing code into fork, and continue with your work in the main body of the code:



#!/usr/bin/ruby -w
# daemon_spawn.rb
require 'tempfile'
require '
daemonize'
include Daemonize

puts "About to daemonize."
fork do
daemonize
log = Tempfile.new('daemon.log')
loop do
log.puts "I'm a daemon, doin' daemon things."
log.flush
sleep 5
end
end

puts 'The subprocess has become a daemon.'
puts "But I'm going to stick around for a while."
sleep 10
puts "Okay, now I'm done."



The
Daemonize
code fits in a single file, and it's licensed under the same terms as Ruby. If you don't want to require your users to download and install it, you can just include it with your program. Because the code is short, you can even copy-and-paste the code into a file in your own program.


However, there's also some (less fancy) daemonizing code in the Ruby 1.8 standard library. It's the WEBrick::Daemon class.



#!/usr/bin/ruby
# webrick_daemon.rb
require 'tempfile'
require 'webrick'

puts 'About to
daemonize.'
WEBrick::Daemon.start do
log = Tempfile.new('daemon.log')
loop do
log.puts "I'm a daemon, doin' daemon things."
log.flush
sleep 5
end
end



It's worth examining the simpler daemonizing code in WEBrick::Daemon so that you can see what's going on. Here's the method in question:



def Daemon.start
exit!(0) if fork
Process::setsid
exit!(0) if fork
Dir::chdir("/")
File::umask(0)
STDIN.reopen("/dev/null")
STDOUT.reopen("/dev/null", "w")
STDERR.reopen("/dev/null", "w")
yield if block_given?
end



A
daemonizer works by forking a new process, letting the original one die, and closing off some of the resources that were available to the original.


Process::setsid disconnects the daemon from the terminal that spawned it. This is why, when your process becomes a daemon process, you get your command line back immediately. We close the original standard input, output, and error and replace them with null streams. We set the working directory and file umask to sensible defaults, regardless of what the daemon inherited from the parent. Then we run the daemon code.


Daemonize::daemonize also sets up signal handlers, calls srand so that the daemon process has a new random number seed, and (optionally) closes any open file handles left around by the original process. It can also retry the fork if it fails because the operating system is running too many processes to create another one.


The fork method, and methods like
daemonize
that depend on it, are only available on Unix-like systems. On Windows, the win32-process extension provides Windows implementations of methods like fork. The win32-process implementation of fork isn't perfect, but it's there if you need it. For cross-platform code, we recommend you spawn a thread and run your daemon code in the thread.




See Also


  • The
    Daemonize
    package (http://grub.ath.cx/daemonize/
    )

  • If you want to run an Internet server, you might want to use gserver from Ruby's standard library; see Recipe 14.14, "Writing an Internet Server"

  • A service is the Windows equivalent of a daemon process; see Recipe 20.2, "
    Creating a Windows Service"

  • Recipe 20.3, "Doing Two Things at Once with Threads"

  • Both win32-process and win32-service were written by Daniel J. Berger; you can download them from his win32utils project at http://rubyforge.org/projects/win32utils/

  • Get win32-process from http://rubyforge.org/projects/win32utils/













No comments:

Post a Comment