实现 Signal.trap 回调的注意事项
与在 C 或大多数其他语言中实现信号处理程序一样,传递给 Signal.trap 的所有代码都必须是可重入的。如果您不熟悉可重入性,请在阅读本文档的其余部分之前,自行查阅 Wikipedia 或其他资料。
最重要的是,“线程安全”并不保证可重入性;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写入操作(当IO#sync为 false 时);包括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的块内部使用是安全的 -
整数和浮点数的算术运算('+', '-', '%', '*', '/')
此外,信号处理程序不会在两次连续的局部变量访问之间运行,因此当在信号处理程序中使用整数和浮点数类时,“+=”和“-=”之类的快捷方式不会触发数据争用。
在 Signal.trap 内部安全的系统调用包装器方法
由于 Ruby 封装了许多 异步信号安全 C 函数,因此许多 IO、File、Dir 和 Socket 方法的相应包装器是安全的。
(不完整列表)
-
Dir.chdir(无块参数)
...