实现 Signal.trap
回调的注意事项¶ ↑
与在 C 或大多数其他语言中实现信号处理程序一样,传递给 Signal.trap
的所有代码都必须是可重入的。如果您不熟悉可重入性,您需要在 Wikipedia 或其他地方阅读有关它的内容,然后再阅读本文档的其余部分。
最重要的是,“线程安全性”不能保证可重入性;而 Mutex#lock 和 Mutex#synchronize 等通常用于线程安全性的方法甚至会阻止可重入性。
Ruby VM 的实现细节¶ ↑
Ruby VM 会延迟运行 Signal.trap
回调,直到其内部数据结构安全为止,但它不知道 YOUR 代码中的数据结构何时安全。Ruby 通过使用仅将 异步信号安全函数 注册为信号处理程序的短 C 函数来实现延迟信号处理。这些短 C 函数仅执行足够的操作,以告知 VM 稍后在 Ruby Thread
主线程中运行通过 Signal.trap
注册的回调。
在 Signal.trap
块中调用不安全的方法¶ ↑
如有疑问,请将下面未列为安全的方法视为不安全的方法。
-
Mutex#lock、Mutex#synchronize 及其使用的任何代码明确不安全。这包括标准库中的
Monitor
,它使用 Mutex 提供可重入性。 -
Dir.chdir
带有块 -
当
IO#sync
为 false 时,任何IO
写操作;包括IO#write
、IO#write_nonblock
、IO#puts
。管道和套接字默认为“IO#sync = true”,因此可以安全地写入它们,除非已禁用IO#sync
。 -
File#flock
,因为底层 flock(2) 调用未由 POSIX 指定
在 Signal.trap
块中常见的安全操作¶ ↑
-
赋值和检索局部变量、实例变量和类变量
-
不执行块的常见
Array
、Hash
、String
、Struct
操作通常是安全的;但如果在其他地方发生迭代,则要小心。 -
Thread::Queue#push
和Thread::SizedQueue#push
(自 Ruby 2.1 起) -
通过
Thread.new
/Thread.start 创建新的Thread
可用于解决在信号处理程序中无法使用 Mutex 的问题 -
在传递给
Signal.trap
的块中使用Signal.trap
是安全的 -
对
Integer
和Float
执行算术运算(“+”、“-”、“%”、“*”、“/”)此外,信号处理程序不会在两次连续的局部变量访问之间运行,因此在信号处理程序中对
Integer
和Float
类使用“+=”和“-=”等快捷方式不会触发数据竞争。
在 Signal.trap
内部的系统调用包装器方法是安全的¶ ↑
由于 Ruby 在许多 异步信号安全 C 函数 周围都有包装器,因此许多 IO
、File
、Dir
和 Socket
方法的相应包装器是安全的。
(不完整列表)
-
Dir.chdir
(无块参数)
…