futex2

作者:

André Almeida <andrealmeid@collabora.com>

futex(快速用户态互斥锁,fast user mutex)是一组系统调用,允许用户空间创建高性能的同步机制,例如用户空间中的互斥锁、信号量和条件变量。像 glibc 这样的 C 标准库使用它来实现诸如 pthreads 等更高级别的接口。

futex2 是最初的 futex 系统调用的后续版本,旨在克服原有接口的局限性。

用户 API

futex_waitv()

等待一个 futex 数组,唤醒其中任意一个

futex_waitv(struct futex_waitv *waiters, unsigned int nr_futexes,
            unsigned int flags, struct timespec *timeout, clockid_t clockid)

struct futex_waitv {
      __u64 val;
      __u64 uaddr;
      __u32 flags;
      __u32 __reserved;
};

用户空间设置一个 struct futex_waitv 数组(最多 128 个条目),使用 uaddr 作为要等待的地址,val 作为期望值,flags 用于指定 futex 的类型(例如私有)和大小。__reserved 必须为 0,但它可用于未来的扩展。指向数组第一项的指针作为 waiters 传递。waiters 或任何 uaddr 的地址无效将返回 -EFAULT

如果用户空间使用的是 32 位指针,则应进行显式类型转换以确保高位清零。uintptr_t 巧妙地解决了这个问题,并且它对 32 位和 64 位指针都适用。

nr_futexes 指定数组的大小。超出 [1, 128] 区间的数值将导致系统调用返回 -EINVAL

系统调用的 flags 参数必须为 0,但它可用于未来的扩展。

对于 waiters 数组中的每个条目,将 uaddr 处的当前值与 val 进行比较。如果不同,系统调用将撤销迄今为止所做的所有工作并返回 -EAGAIN。如果所有测试和验证均成功,系统调用将一直等待,直到发生以下情况之一:

  • 超时到期,返回 -ETIMEOUT

  • 向休眠任务发送了信号,返回 -ERESTARTSYS

  • 列表中的某个 futex 被唤醒,返回被唤醒的 futex 的索引。

如何使用该接口的示例可以在 tools/testing/selftests/futex/functional/futex_waitv.c 中找到。

超时

struct timespec *timeout 参数是一个可选参数,指向一个绝对超时时间。你需要在 clockid 参数中指定所使用的时钟类型。支持 CLOCK_MONOTONICCLOCK_REALTIME。此系统调用仅接受 64 位 timespec 结构体。

Futex 的类型

futex 可以是私有的(private)或共享的(shared)。私有用于共享同一内存空间的进程,且所有进程中该 futex 的虚拟地址是相同的。这允许内核进行优化。要使用私有 futex,必须在 futex 标志中指定 FUTEX_PRIVATE_FLAG。对于不共享同一内存空间、因此对于同一个 futex 可能具有不同虚拟地址的进程(例如,使用文件映射的共享内存),需要不同的内部机制来正确将其加入队列。这是默认行为,它同时适用于私有和共享 futex。

Futex 可以有不同的位宽:8、16、32 或 64 位。目前,唯一支持的是 32 位大小的 futex,并且需要使用 FUTEX_32 标志来指定。