futex 函数详解
- 函数介绍
futex(Fast Userspace muTEX)是 Linux 内核提供的一种高效的同步原语。可以把 futex 想象成一个”智能的红绿灯系统”——在大多数情况下,它在用户空间快速运行(就像绿灯畅通无阻),只有在发生竞争时才需要内核介入(就像遇到红灯需要交通警察协调)。
传统的互斥锁在用户空间和内核空间之间频繁切换,开销很大。而 futex 的巧妙之处在于:如果没有竞争,线程可以在用户空间直接完成同步操作;只有当出现等待情况时,才需要进入内核进行睡眠和唤醒操作。这就像排队买票,如果没人排队,你可以直接买票离开;如果人很多,就需要排队等待,这时工作人员会来维持秩序。
- 函数原型
1 2 3 4 5 6 7 8
| #include <linux/futex.h> #include <sys/syscall.h> #include <unistd.h>
int futex(int *uaddr, int futex_op, int val, const struct timespec *timeout, int *uaddr2, int val3);
|
注意:futex 是系统调用,通常通过 syscall() 函数调用。
- 功能
futex 提供了一种高效的用户空间同步机制,主要用于实现各种同步原语(如互斥锁、条件变量、信号量等)。它通过在用户空间和内核空间之间智能切换,实现了高性能的线程同步。
- 参数
uaddr: 指向用户空间的一个整型变量(futex 字),用于存储同步状态
futex_op: 操作类型(见下表)
val: 操作参数,根据不同的操作类型有不同的含义
timeout: 超时时间(可选,某些操作使用)
uaddr2: 第二个 futex 地址(某些操作使用)
val3: 第三个参数(某些操作使用)
- 常用操作类型
操作说明功能FUTEX_WAIT等待操作如果 *uaddr == val,则睡眠等待FUTEX_WAKE唤醒操作唤醒在 uaddr 上等待的 val 个线程FUTEX_WAIT_BITSET带位集的等待可以指定唤醒条件FUTEX_WAKE_BITSET带位集的唤醒按位集唤醒等待线程FUTEX_LOCK_PI优先级继承锁用于实现互斥锁FUTEX_UNLOCK_PI解锁优先级继承锁解除互斥锁
- 返回值
成功: 返回值根据操作类型而定
失败: 返回 -1,并设置相应的 errno 错误码
常见错误码:
- 相似函数或关联函数
pthread_mutex_t: POSIX 互斥锁(基于 futex 实现)
pthread_cond_t: POSIX 条件变量(基于 futex 实现)
sem_t: POSIX 信号量(基于 futex 实现)
atomic operations: 原子操作(与 futex 配合使用)
- 示例代码
示例1:基础用法 - 实现简单的计数器同步
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
| #define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> #include <sys/syscall.h> #include <linux/futex.h> #include <sys/time.h> #include <errno.h> #include <stdatomic.h>
int futex_wait(int *addr, int val, const struct timespec *timeout) { return syscall(SYS_futex, addr, FUTEX_WAIT, val, timeout, NULL, 0); }
int futex_wake(int *addr, int num) { return syscall(SYS_futex, addr, FUTEX_WAKE, num, NULL, NULL, 0); }
atomic_int counter = 0; int futex_var = 0;
void* worker_thread(void* arg) { int thread_id = *(int*)arg; for (int i = 0; i < 5; i++) { int expected = 0; while (!atomic_compare_exchange_weak(&futex_var, &expected, 1)) { futex_wait(&futex_var, 1, NULL); expected = 0; } int old_value = atomic_fetch_add(&counter, 1); printf("线程 %d: 计数器从 %d 增加到 %d\n", thread_id, old_value, old_value + 1); usleep(100000); atomic_store(&futex_var, 0); futex_wake(&futex_var, 1); usleep(50000); } return NULL; }
int main() { pthread_t threads[3]; int thread_ids[3] = {1, 2, 3}; printf("=== FUTEX 基础同步示例 ===\n"); printf("启动 3 个线程,每个线程对计数器执行 5 次操作\n\n"); for (int i = 0; i < 3; i++) { if (pthread_create(&threads[i], NULL, worker_thread, &thread_ids[i]) != 0) { perror("pthread_create"); return 1; } } for (int i = 0; i < 3; i++) { pthread_join(threads[i], NULL); } printf("\n最终计数器值: %d\n", atomic_load(&counter)); return 0; }
|
示例2:带超时的 futex 操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
| #define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> #include <sys/syscall.h> #include <linux/futex.h> #include <sys/time.h> #include <errno.h> #include <stdatomic.h>
int futex_wait_timeout(int *addr, int val, int timeout_ms) { struct timespec timeout; timeout.tv_sec = timeout_ms / 1000; timeout.tv_nsec = (timeout_ms % 1000) * 1000000; return syscall(SYS_futex, addr, FUTEX_WAIT, val, &timeout, NULL, 0); }
int futex_wake(int *addr, int num) { return syscall(SYS_futex, addr, FUTEX_WAKE, num, NULL, NULL, 0); }
int buffer = -1; int data_ready = 0;
void* producer_thread(void* arg) { for (int i = 1; i <= 5; i++) { printf("生产者: 准备生产数据 %d\n", i); while (__atomic_load_n(&data_ready, __ATOMIC_ACQUIRE) == 1) { printf("生产者: 缓冲区满,等待消费者...\n"); int ret = futex_wait_timeout(&data_ready, 1, 2000); if (ret == -1 && errno == ETIMEDOUT) { printf("生产者: 等待超时,继续检查...\n"); } } buffer = i; __atomic_store_n(&data_ready, 1, __ATOMIC_RELEASE); printf("生产者: 生产了数据 %d\n", i); futex_wake(&data_ready, 1); sleep(1); } return NULL; }
void* consumer_thread(void* arg) { for (int i = 0; i < 5; i++) { printf("消费者: 准备消费数据\n"); while (__atomic_load_n(&data_ready, __ATOMIC_ACQUIRE) == 0) { printf("消费者: 无数据,等待生产者...\n"); int ret = futex_wait_timeout(&data_ready, 0, 3000); if (ret == -1 && errno == ETIMEDOUT) { printf("消费者: 等待超时,继续检查...\n"); } } printf("消费者: 消费了数据 %d\n", buffer); buffer = -1; __atomic_store_n(&data_ready, 0, __ATOMIC_RELEASE); futex_wake(&data_ready, 1); sleep(2); } return NULL; }
int main() { pthread_t producer, consumer; printf("=== 带超时的 FUTEX 操作示例 ===\n"); printf("生产者生产 5 个数据,消费者消费它们\n"); printf("等待操作设置 2-3 秒超时\n\n"); if (pthread_create(&producer, NULL, producer_thread, NULL) != 0 || pthread_create(&consumer, NULL, consumer_thread, NULL) != 0) { perror("pthread_create"); return 1; } pthread_join(producer, NULL); pthread_join(consumer, NULL); printf("\n生产者-消费者操作完成\n"); return 0; }
|
示例3:实现简单的互斥锁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
| #define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> #include <sys/syscall.h> #include <linux/futex.h> #include <errno.h> #include <stdatomic.h>
typedef struct { atomic_int state; } futex_mutex_t;
void futex_mutex_init(futex_mutex_t *mutex) { atomic_store(&mutex->state, 0); }
void futex_mutex_lock(futex_mutex_t *mutex) { int expected = 0; while (!atomic_compare_exchange_weak(&mutex->state, &expected, 1)) { syscall(SYS_futex, &mutex->state, FUTEX_WAIT, 1, NULL, NULL, 0); expected = 0; } }
void futex_mutex_unlock(futex_mutex_t *mutex) { atomic_store(&mutex->state, 0); syscall(SYS_futex, &mutex->state, FUTEX_WAKE, 1, NULL, NULL, 0); }
int shared_counter = 0; futex_mutex_t my_mutex;
void* increment_thread(void* arg) { int thread_id = *(int*)arg; int operations = 100000; printf("线程 %d: 开始执行 %d 次操作\n", thread_id, operations); for (int i = 0; i < operations; i++) { futex_mutex_lock(&my_mutex); shared_counter++; futex_mutex_unlock(&my_mutex); } printf("线程 %d: 完成\n", thread_id); return NULL; }
int main() { pthread_t threads[4]; int thread_ids[4] = {1, 2, 3, 4}; int expected_result = 400000; printf("=== FUTEX 互斥锁实现示例 ===\n"); printf("4 个线程,每个执行 100,000 次递增操作\n"); printf("期望结果: %d\n\n", expected_result); futex_mutex_init(&my_mutex); for (int i = 0; i < 4; i++) { if (pthread_create(&threads[i], NULL, increment_thread, &thread_ids[i]) != 0) { perror("pthread_create"); return 1; } } for (int i = 0; i < 4; i++) { pthread_join(threads[i], NULL); } printf("\n实际结果: %d\n", shared_counter); printf("结果%s正确\n", (shared_counter == expected_result) ? "" : "不"); if (shared_counter != expected_result) { printf("可能存在竞态条件或同步问题\n"); } return 0; }
|
编译和运行说明
1 2 3 4 5 6 7 8 9 10
| gcc -o futex_example1 example1.c -lpthread gcc -o futex_example2 example2.c -lpthread gcc -o futex_example3 example3.c -lpthread
./futex_example1 ./futex_example2 ./futex_example3
|
重要注意事项
原子操作配合: futex 通常与原子操作(atomic operations)配合使用,确保检查和等待操作的原子性
虚假唤醒: 与条件变量一样,futex 也可能出现虚假唤醒,需要在循环中检查条件
内存序: 使用适当的内存序(memory ordering)确保同步正确性
错误处理: 始终检查 futex 调用的返回值和 errno
移植性: futex 是 Linux 特有的特性,在其他系统上不可用
调试困难: futex 相关的 bug 很难调试,建议使用成熟的同步库
futex 的优势
高性能: 无竞争时完全在用户空间操作
低延迟: 避免不必要的内核切换
可扩展性: 支持大量并发线程
灵活性: 可以实现各种同步原语
这些示例展示了 futex 的基本使用方法,从简单的同步操作到带超时机制,再到完整的互斥锁实现,帮助你理解这个强大而高效的同步机制。
futex系统调用, futex函数详解, Linux futex示例, Fast Userspace muTEX, futex编程指南, 如何使用futex, futex性能优化, 基于futex的同步机制, futex与线程同步, 高级futex技术应用
https://www.calcguide.tech/2025/09/08/futex系统调用及示例/
https://blog.csdn.net/zidier215/article/details/151332503?sharetype=blogdetail&sharerId=151332503&sharerefer=PC&sharesource=zidier215&spm=1011.2480.3001.8118
相关阅读