Zircon Handles
基础
句柄是允许用户程序引用内核对象引用的一种内核结构,它可以被认为是与特定内核对象的会话或连接。
通常情况,多个进程可通过不同的句柄同时访问同一个对象。 但是,单个句柄只能绑定到单个进程或绑定到内核中。
当它被绑定到内核时,我们称其为“传输(in-transit)”状态。
在用户空间中,句柄简单地以某些系统调用返回的特定数字值表示。 并且只有非“传输”状态的句柄对用户空间是可见的。
表示句柄的整型数值仅在该进程中是有意义的。 另一个进程中的相同数字可能不表示任何句柄,也可能表示为指向完全不同的内核对象的句柄。
表示句柄的整型值是除了ZX_HANDLE_INVALID
对应的值之外的任何32位数。
在内核模式下,句柄是一个包含三个逻辑字段的C++对象: <!— + A reference to a kernel object
- The rights to the kernel object
- The process it is bound to (or if it’s bound to kernel) —>
- 内核对象的引用
- 内核对象的权限
- 它绑定的进程(或表示它绑定到内核)
“权限”指定了可以对内核对象执行哪些操作的特权。 同一个进程可以为同一内核对象提供两个具有不同权限的句柄。
使用句柄
有多个系统调用的功能是创建一个新的内核对象,并返回一个句柄。 以下面几个例子为例:
这些调用同时创建了内核对象和指向它的第一个句柄。 句柄绑定到发出系统调用的进程中,权限是该类型内核对象的默认权限。
只有下列一个系统调用可以创建句柄的副本,新句柄指向同一个内核对象并绑定到发出系统调用的进程中:
另有一个系统调用可用于创建一个等效的句柄(可能具有较少的权限),并同时使原始句柄无效:
以下系统调用仅用于销毁一个句柄:
以下两个系统调用将一个句柄绑定到调用进程并将其绑定到内核(将句柄置于“传输”状态):
以下三个系统调用适用“传输”状态的句柄并将其绑定到调用进程:
上述的channel和socket系统调用用于将句柄从一个进程传输到另一个进程。
例如,如果使用channel连接两个进程,源进程调用zx_channel_write
或zx_channel_call
发送句柄,然后目标进程在同一channel上调用zx_channel_read
接收句柄。
最后,以下系统调用为新创建的进程提供引导句柄,即它是可以用来请求获取其他句柄的句柄:
引导句柄可以是任何可传输的内核对象,但最合理的使用方式是它指向channel的一端,使得通过该初始的channel可以将更多句柄发送到新进程中。
垃圾回收
如果句柄有效,则可它指向的内核对象一定是有效的。 这是可以保证的,因为内核对象采用引用计数(ref-count)的方式被追踪,而每个句柄都包含对其内核对象的引用。
但相反的情况却并不成立。 当句柄被销毁时,并不意味着它引用的对象一定会被销毁。 可能有其他句柄指向该对象,或者内核本身可能持有对内核对象的引用。 这其中一个例子是指向线程的句柄,线程的最后一个句柄被关闭并不意味着该线程将被终止。
当释放对内核对象的最后一个引用时,内核对象被销毁或者内核将该对象标记为可垃圾回收,当对象的当前排队中的操作全部完成以后将销毁该对象。
特别情况
- 当句柄处于“传输”状态且它被写入的channel或socket被销毁时,该句柄将被关闭。
- 调试会话(和调试器)可能具有特殊的系统调用来访问句柄。
无效句柄和句柄重用
传递以下句柄值给除zx_object_get_info
以外的任何系统调用,都将产生错误:
<!—
- A handle value that corresponds to a closed handle
- The ZX_HANDLE_INVALID value, except for
zx_handle_close
syscall —> - 与已关闭句柄对应的句柄值
- 对于除了
zx_handle_close
之外的其他系统调用而言,ZX_HANDLE_INVALID
内核可以自由地为新创建的对象重用已关闭句柄的整型值。 因此,请务必确保遵守正确的句柄使用规则:
- 请勿在一个线程关闭给定的句柄时,另一个线程以竞争的方式使用相同的句柄,即使第二个线程也在关闭它。
- 请勿忽略ZX_ERR_BAD_HANDLE的返回码,它们通常意味着代码存在逻辑错误。
通过使用带有ZX_POL_ACTION_EXCEPTION标志位的ZX_POL_BAD_HANDLE作业策略,可以自动检测无效句柄的使用情况,以便使此类作业对象下的进程在尝试任何上述无效情况时产生异常。