实现 Signal.trap
回调的注意事项¶ ↑
与在 C 或大多数其他语言中实现信号处理程序一样,传递给 Signal.trap
的所有代码都必须是可重入的。如果您不熟悉可重入性,则在阅读本文档的其余部分之前,需要在 维基百科 或其他地方了解它。
最重要的是,“线程安全”并不能保证可重入性;而像 Mutex#lock 和 Mutex#synchronize 这样通常用于线程安全的方法甚至会阻止可重入性。
Ruby VM 的一个实现细节¶ ↑
Ruby VM 会推迟 Signal.trap
回调的运行,直到其内部数据结构安全为止,但它不知道何时您的代码中的数据结构是安全的。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
,因为 POSIX 没有指定底层的 flock(2) 调用
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
(没有代码块参数)
...