姓名
futex - 用于创建用户空间同步工具的基元。
SYNOPSIS
futex 是一种快速用户空间 muTEX。这是一个低级别
同步基元,是更高层级的构建块
pthread_mutex_t
和 pthread_cond_t
等 API。
Futexe 不会进入内核或分配内核 在无争议的情况下使用资源。
说明
Zircon Futex 实现目前支持三种分布式操作 超过 6 个系统调用:
zx_status_t zx_futex_wait(const zx_futex_t* value_ptr,
zx_futex_t current_value,
zx_handle_t new_futex_owner,
zx_time_t deadline);
zx_status_t zx_futex_wake(const zx_futex_t* value_ptr, uint32_t wake_count);
zx_status_t zx_futex_wake_single_owner(const zx_futex_t* value_ptr);
zx_status_t zx_futex_requeue(const zx_futex_t* value_ptr,
uint32_t wake_count,
zx_futex_t current_value,
const zx_futex_t* requeue_ptr,
uint32_t requeue_count,
zx_handle_t new_requeue_owner);
zx_status_t zx_futex_requeue_single_owner(const zx_futex_t* value_ptr,
zx_futex_t current_value,
const zx_futex_t* requeue_ptr,
uint32_t requeue_count,
zx_handle_t new_requeue_owner);
zx_status_t zx_futex_get_owner(const zx_futex_t* value_ptr, uint64_t* koid);
它们共用一个 value_ptr
参数,也就是
已对齐的用户空间整数的地址。此虚拟地址是
内核中用于跟踪哪些 futex 给定线程的信息
等待。内核目前不会修改
*value_ptr
(但请参阅下文,了解未来可能执行的
因此)。由用户空间代码正确以原子方式修改此
值以构建互斥量等等。
请注意,使用地址标记时,用户空间指针
内核中的 futex 实例并非始终是一对一的映射关系。地址
使用已删除架构专用标记信息
。例如,在 ARM 上,Top-Byte-Ignore (TBI) 为
值为 0x0A000000FF123450
的 Futex 指针具有相同的
将 futex ID 用作值为 0x0B000000FF123450
的 futex 指针,因为
它们的标记(0x0A
和 0x0B
)不同,它们的地址位相同。
请参阅 zx_futex_wait()
、zx_futex_wake()
、zx_futex_requeue()
和
zx_futex_get_owner()
手册页面。
权利
Futex 对象没有任何与之相关联的权利。
用户空间代码只能对 futex:等待和唤醒(重新排队是这两者的组合)。因为 严格来说,futex 是进程的局部概念,撤消了对其中任何一项的访问 都会导致此 futex 在功能上毫无价值。
此外,从内核的角度来看,futex 是临时对象, 仅当 futex 有 Waiter 时才存在状态。如果没有更持久的状态 因此在内核中存在长效的概念时,或多或少不可能
与 Linux Futex 的区别
请注意,所有的 zircon futex 操作都离虚拟模式 用户空间指针的地址这不同于 此机制有助于区分私有 futex 操作( 对应于我们只处理进程的命令) 地址空间。
如上文所述,我们所有的 futex 操作都会保留
futex 不再经过内核修改。其他可能的操作,例如
Linux 的 FUTEX_WAKE_OP
,需要对值进行原子操作
从内核开始,而我们当前的实现不需要这样做。
所有权和优先级继承
概览
某些运行时可能需要实现基于 futex 的同步基元 具有优先级继承行为。为了给这些用户提供支持 锆石有一种“所有权”的概念它可用于实现 基元。您可以选择是否使用此功能。
在任何时间,futex 可能不再拥有,也可能归某个人所有 线程。当某个线程拥有一个或多个 futex 时,其有效优先级将变为 其基本优先级中的最大值,以及所有当前 当前拥有的所有 futex 的服务员。当某个会话串 拥有 futex,futex 服务员的优先事务就不再存在 来自上述关系。一旦线程不再拥有任何 futex, 优先级会放宽回其基准优先级。
用户空间代码负责向 futex 的所有者发出信号, 就像在构建特定类型的模型时正确应用所有权概念一样 需要优先级继承行为的同步对象。
锆石最常见的一个所有者。拥有多份 futex 不支持优先级继承的目的。Futex 的所有者 绝不能同时担任同一 futex 的服务员。
分配所有权
每次等待时都会分配 futex 的所有权或“重新排队”操作。在 如果是重新排队操作,则目标 futex 是重新排队的 futex,而不是 wake_futex。用户将句柄传递到线程,以指明当前所有者是谁 futex 应为;如果没有所有者,则为 ZX_HANDLE_INVALID。
- 向线程传递一个有效句柄,以表明 futex 所有者是 用户空间代码的责任。传递无效句柄或句柄 非线程对象会导致等待/重新排队操作失败。
- 尚未启动的线程可能没有 futex。任何试图 将 futex 的所有权分配给尚未启动的线程 会导致等待/重新排队操作失败。
- 已退出的线程可能不是 futex 的所有者。如果线程退出 当它拥有 futex 时,此 futex 将重置为不归任何人所有。如果 用户尝试将 futex 的所有权分配给已经退出的线程, 等待/重新排队操作将表现为,当 ZX_HANDLE_INVALID 被传递为 成为了新的 Ftex 所有者
- 如果等待/重新排队操作成功,目标 futex 的所有者将 always 设置为指定的线程,如果不是,则不设置 已传递 ZX_HANDLE_INVALID。
- 特别是,如果等待/重新排队操作因数据不一致而失败, 预期 futex 值和实际 futex 值之间 futex 将保持不变,操作的状态代码将 ZX_ERR_BAD_STATE.无论值是什么,系统都会返回此错误代码 为表示所有权的句柄传递了传递信息,即使传递的值本应 导致返回的状态是 ZX_ERR_BAD_HANDLE。
转让所有权
内核可以代表用户转移 futex 的所有权
在唤醒操作或重新排队操作期间发生。重新排队时
操作,传输的目标是 wake_futex,而不是 requeue_futex。
只有在使用
zx_futex_wake_single_owner()
或 zx_futex_requeue_single_owner()
不同的唤醒/重新排队操作。single_owner
变体的
这些操作只会释放一个 Waiter,
将 futex 的所有权分配给已释放的线程。
- 如果在唤醒操作期间没有等候程序,则已经没有 所有者。这一点不会改变。
- 如果重新排队操作因预期 futex 不匹配而失败 值和实际的 futex 值,则 futex 的所有者将仍然 未更改。
- 成功调用 唤醒/重新排队操作会导致目标 futex 的所有者被设置为 什么都不用做。
关于五人足球的文章
Fuss、Futex 和 Furwocks:Linux 中的快速用户级锁定,Hubertus Franke 和 Rusty Russell
本文是介绍 Linux Futex 的原始白皮书。它 记录了原始实现的历史和设计, 之前(失败的)尝试创建快速用户空间 同步原语和性能测量。
Futexes Are Tricky,Ulrich Drepper
此白皮书介绍了 Linux 中的 futex。还介绍了内核实现, 更详细地介绍正确、高效的用户空间 互斥量、条件变量等的实现。
-
对“Futexes are string”有进一步评论,概述一个简单的 无需
FUTEX_CMP_REQUEUE
在 WebKit 中锁定 (Filip Pizlo)
深入探索 WebKit 中的锁定基元,包括 基准和分析包含对“停车”操作的详细说明 很多"此概念可以非常紧凑地表示用户空间。 互斥量。