实现 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
写操作,当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
的块中使用是安全的 -
对
Integer
和Float
的算术运算(‘+’,‘-’,‘%’,‘*’,‘/’)此外,信号处理程序不会在两次连续的局部变量访问之间运行,因此在信号处理程序中使用
Integer
和Float
类时,诸如“+=”和“-=”之类的快捷方式不会触发数据竞争。
在 Signal.trap
内部安全的系统调用包装方法¶ ↑
由于 Ruby 围绕许多 异步信号安全 C 函数 进行了包装,因此许多 IO
、File
、Dir
和 Socket 方法的相应包装器是安全的。
(不完整列表)
-
Dir.chdir
(不带块参数)
…