收录日期:2019/04/26 16:17:03 时间:2010-06-18 11:06:02 标签:ruby,signal-processing

Given a very simple ruby script:

child = fork do
  system 'sleep 10000'
end

5.times do
  sleep 1
  puts "send kill to #{child}"
  Process.kill("QUIT", child)
end

QUIT signal is just lost. Where does it go? Something with default handler which just ignores it?

How to send signal to all processes created by that fork? Is it possible to do that without searching for all child processes?

The problem is that the system call creates yet another child process running the given command in a subshell, so there are actually three processes running in your example. Additionally, the Ruby Kernel#system command is implemented via the standard C function system(3), which calls fork and exec to create the new process and (on most systems) ignores SIGINT and SIGQUIT, and blocks SIGCHLD.

If you simply call sleep(10000) instead of system("sleep 10000") then things should work as you expect. You can also trap SIGQUIT in the child to handle it gracefully:

child = fork do
  Signal.trap("QUIT") { puts "CHILD: ok, quitting time!"; exit }
  sleep(10000)
end

If you really need to use a "system" call from the child process then you might be better off using an explicit fork/exec pair (instead of the implicit ones in the system call), so that you can perform your own signal handling in the third forked child.

I think that you are sending signal to fork process corectly. I think that the problem is with the system command. System command creates new fork and waits until it ends and I think that this waiting is blocking your quit signal. If you run your example as test.rb you'll see three processes:

  • test.rb
  • test.rb
  • sleep 10000

If you send signal "TERM" or "KILL" instead of "QUIT" the second test.rb will die but sleep 10000 will continue!