如何在Ruby中使用信号阻塞

时间:2011-06-15 06:45:55

标签: ruby signals

如何阻止Ruby中的某些信号? (即.sigprocmask,如下所述:http://www.chemie.fu-berlin.de/chemnet/use/info/libc/libc_21.html#SEC371

这是我的示例代码

pid = fork do
  trap('INT') do
    puts "Graceful shutdown..."
    puts "goodbye"
    exit 0
  end

  loop do
    this_could_be_interrupted
    something_that_must_not_be_interrupted
    this_could_be_interrupted_too
  end
end

sleep 5
Process.kill('INT', pid)

我可以将something_that_must_not_be_interrupted包装在ruby块中,以确保在该方法运行时不会处理SIGINT吗?

我想理想的是,如果我可以做这样的事情:

Process.block_signal('INT') do
  something_that_must_not_be_interrupted
end

更新:我目前正在做类似的事情:

trap('INT') do
  @interrupted = true
end

loop do
  exit 0 if @interrupted
  something_that_must_not_be_interrupted
end

2 个答案:

答案 0 :(得分:14)

我认为你正在寻找non-block form of trap

  

Signal.trap( signal, command ) → obj
  [...]
  如果命令是字符串"IGNORE""SIG_IGN",则信号将被忽略。如果命令是"DEFAULT""SIG_DFL",则将调用Ruby的默认处理程序。

所以你应该能说出来:

trap('INT', 'IGNORE')
something_that_must_not_be_interrupted
trap('INT', 'DEFAULT')

更新:从评论中看,您似乎只想暂时忽略该信号。用你已经拥有的东西做最简单的方法就是添加一个你的信号处理程序可以看到的标志,然后记住它进来时的信号,我们当前忽略了信号,当我们不再忽略你的东西时只需将信号队列发送给自己即可清空信号队列。如果把这个逻辑包装在一个类中,你会有一些相当友好的东西:

#
# Threading and race condition issues are left as an exercise,
# this is just meant as an illustration. Handling multiple signals
# at once is also left as an exercise.
#
class SignalHandler
  def initialize(signal)
    @interuptable = true
    @enqueued     = [ ]
    trap(signal) do
      if(@interuptable)
        puts "Graceful shutdown..."
        puts "goodbye"
        exit 0
      else
        @enqueued.push(signal)
      end
    end
  end

  # If this is called with a block then the block will be run with
  # the signal temporarily ignored. Without the block, we'll just set
  # the flag and the caller can call `allow_interuptions` themselves.
  def dont_interupt
    @interuptable = false
    @enqueued     = [ ]
    if(block_given?)
      yield
      allow_interuptions
    end
  end

  def allow_interuptions
    @interuptable = true
    # Send the temporarily ignored signals to ourself,
    # see http://ruby-doc.org/core/Process.html#method-c-kill
    @enqueued.each { |signal| Process.kill(signal, 0) }
  end
end

真正的功能代码是解释技术的最简单方法(我必须编写它以确保该技术可行)所以你去。感谢Ruby中对信号处理的审查:)那么你可以这样做:

sigint = SignalHandler.new('INT')
loop do
  this_could_be_interrupted
  sigint.dont_interupt { something_that_must_not_be_interrupted }
  this_could_be_interrupted_too
end

答案 1 :(得分:5)

Ruby返回与信号关联的最后一个处理程序,因此您可以使用:

def bypass_signal(name)
  old_handler = trap("INT", 'IGNORE')
  yield
  trap("INT", old_handler)
end

bypass_signal "INT" { method_which_shouldnt_be_interrupted }