一个信号是否可以有多个陷阱
Is it possible to have multiple traps for a signal
我正在尝试了解 Ruby 的 trap
标准信号。
具体来说,我正在尝试为同一个信号设置多个信号处理程序 ("traps")。这似乎是不可能的。这是演示问题的超级简化代码:
文件traps.rb
:
should_stop = false
Signal.trap 'INT' do
# won't be executed :(
puts 'int --> A'
should_stop = true
end
Signal.trap 'INT' do
# will be executed
puts 'int --> B'
should_stop = true
end
times = 0
until should_stop
puts 'waiting to stop'
sleep 1
times += 1
break if times >= 5
end
puts 'done'
运行代码:
ruby traps.rb
不按CTRL+C:
输出
waiting to stop
waiting to stop
waiting to stop
waiting to stop
waiting to stop
done
按CTRL+C2秒后输出:
waiting to stop
waiting to stop
^Cint --> B
done
似乎只有最后声明的信号陷阱才会被执行。
这是设计使然的行为吗?
如果不是,我们如何对同一个信号执行多个处理程序?
提出这个问题的主要原因是第三方库可能会将它们的陷阱添加到信号中。
如果我们有两个不同的第三方库将它们的陷阱添加到同一个信号中,那么实际上只会执行其中一个。这就是乐趣的开始:(
It seems that only the last signal trap to be declared is the one which would be executed.
Is this behavior by design?
在the documentation of Signal::trap
中不是很明确,但它是设计使然:
The command or block specifies code to be run when the signal is raised.
注意单数的使用,并且没有提及 "The command or block is added to the list of trap handlers to be run when the signal is raised."
如果您查看 Signal::trap
之后建模的 POSIX trap
shell builtin,就会变得更清楚:
The action of trap
shall override a previous action (either default action or one explicitly set).
相当于trap
的C 语言POSIX sigaction
function 或多或少说的是同一件事。但是请注意,sigaction
函数还提供了一种检索指向旧操作的函数指针的方法,因此 理论上 ,您可以将操作设置为指向旧操作的函数指针新动作使用指向旧动作的函数指针来调用旧动作作为其自身的一部分,从而以某种方式链接动作。
但是请注意,这需要新旧操作以某种方式进行合作。另请注意,此操作模式并非由 POSIX trap
.
建模
If not, how can we have multiple handlers executed to the same signal?
来自文档:
trap returns the previous handler for the given signal.
因此,Signal::trap
实现了 sigaction
的行为,使您可以访问 "old" 处理程序。您可以将旧处理程序保存在某个地方,并通过从新处理程序显式调用它来链接调用。
与 sigaction
一样,这需要处理程序之间进行某种形式的合作。
结合 from Jörg W Mittag 和文档,这里有一个简化的解决方案:
# file traps.rb
should_stop = false
Signal.trap('INT') do
puts 'int --> A'
should_stop = true
end
$prev_trap = Signal.trap('INT') do
puts 'int --> B'
should_stop = true
$prev_trap&.call
end
times = 0
until should_stop
puts 'waiting to stop'
sleep 1
times += 1
break if times >= 5
end
puts 'done'
运行ruby traps.rb
后,按CTRL+C3秒后,输出如下这个:
waiting to stop
waiting to stop
waiting to stop
^Cint --> B
int --> A
done
我正在尝试了解 Ruby 的 trap
标准信号。
具体来说,我正在尝试为同一个信号设置多个信号处理程序 ("traps")。这似乎是不可能的。这是演示问题的超级简化代码:
文件traps.rb
:
should_stop = false
Signal.trap 'INT' do
# won't be executed :(
puts 'int --> A'
should_stop = true
end
Signal.trap 'INT' do
# will be executed
puts 'int --> B'
should_stop = true
end
times = 0
until should_stop
puts 'waiting to stop'
sleep 1
times += 1
break if times >= 5
end
puts 'done'
运行代码:
ruby traps.rb
不按CTRL+C:
输出waiting to stop waiting to stop waiting to stop waiting to stop waiting to stop done
按CTRL+C2秒后输出:
waiting to stop waiting to stop ^Cint --> B done
似乎只有最后声明的信号陷阱才会被执行。
这是设计使然的行为吗?
如果不是,我们如何对同一个信号执行多个处理程序?
提出这个问题的主要原因是第三方库可能会将它们的陷阱添加到信号中。
如果我们有两个不同的第三方库将它们的陷阱添加到同一个信号中,那么实际上只会执行其中一个。这就是乐趣的开始:(
It seems that only the last signal trap to be declared is the one which would be executed.
Is this behavior by design?
在the documentation of Signal::trap
中不是很明确,但它是设计使然:
The command or block specifies code to be run when the signal is raised.
注意单数的使用,并且没有提及 "The command or block is added to the list of trap handlers to be run when the signal is raised."
如果您查看 Signal::trap
之后建模的 POSIX trap
shell builtin,就会变得更清楚:
The action of
trap
shall override a previous action (either default action or one explicitly set).
相当于trap
的C 语言POSIX sigaction
function 或多或少说的是同一件事。但是请注意,sigaction
函数还提供了一种检索指向旧操作的函数指针的方法,因此 理论上 ,您可以将操作设置为指向旧操作的函数指针新动作使用指向旧动作的函数指针来调用旧动作作为其自身的一部分,从而以某种方式链接动作。
但是请注意,这需要新旧操作以某种方式进行合作。另请注意,此操作模式并非由 POSIX trap
.
If not, how can we have multiple handlers executed to the same signal?
来自文档:
trap returns the previous handler for the given signal.
因此,Signal::trap
实现了 sigaction
的行为,使您可以访问 "old" 处理程序。您可以将旧处理程序保存在某个地方,并通过从新处理程序显式调用它来链接调用。
与 sigaction
一样,这需要处理程序之间进行某种形式的合作。
结合
# file traps.rb
should_stop = false
Signal.trap('INT') do
puts 'int --> A'
should_stop = true
end
$prev_trap = Signal.trap('INT') do
puts 'int --> B'
should_stop = true
$prev_trap&.call
end
times = 0
until should_stop
puts 'waiting to stop'
sleep 1
times += 1
break if times >= 5
end
puts 'done'
运行ruby traps.rb
后,按CTRL+C3秒后,输出如下这个:
waiting to stop waiting to stop waiting to stop ^Cint --> B int --> A done