futex内核同步

futex快速用户空间互斥体,用来给上层应用构建更高级别的同步机制,是实现信号量和锁的基础。

进程间通信,管道、消息队列、信号量、共享内存、套接字、信号。

使用信号量(semget()、semop()、semctl())进程间通信,是通过系统调用在内核中完成的。

进程要陷入内核去看看有没有人和它竞争,退出的时侯还要陷入内核去看看有没有进程等待在同一同步变量上。这些不必要的系统调用造成了大量的性能开销。

所以出现了futex,futex是一种用户态和内核态混合的同步机制。通过在用户态检查,没有竞争就不陷入内核执行系统调用了。访问futex变量通知进程有竞争发生,则执行系统调用去完成相应的操作。

futex系统调用

sys_futex系统调用,对进程竞争情况下的同步处理提供支持。

futex同步

进程、线程可以利用futex来进行同步。

futex同步操作都应该从用户空间开始,创建一个futex整形同步变量。

同步的进程间通过mmap共享一段内存,futex变量就位于这段共享的内存中,执行原子操作。

进程尝试持有锁或者要进入互斥区的时候,futex同步变量减1。如果同步变量变为0,则没有竞争发生, 进程照常执行。如果为负意味有竞争发生,系统调用的futex_wait休眠当前进程。

当进程释放锁或者要离开互斥区的时候,futex同步变量加1。如果同步变量由0变成1,则没有竞争发生,进程照常执行。如果为负意味有竞争发生,系统调用的futex_wake唤醒一或多个等待进程。

futex系统调用案例

一个进程在等待某个信号的时候,最终会通过系统调用进入到futex。

系统调用有6个参数,上层应用在等待一个信号量的时候,给op这个参数传递的FUTEX_WAIT_BITSET,代码流程:

// kernel/futex.c
SYSCALL_DEFINE6(futex, u32 __user *, uaddr, int, op, u32, val,
struct __kernel_timespec __user *, utime, u32 __user *, uaddr2,
u32, val3) {
    ...
    return do_futex(... op, ...);
}

long do_futex(...int op,...) {
    int cmd = op & FUTEX_CMD_MASK;

    switch (cmd) {
        case FUTEX_WAIT_BITSET:
            return futex_wait(uaddr, flags, val, timeout, val3);
    ...
    }
    ...
}

进程系统调用futex_wait时,会将自己设置为睡眠状态,进行一次进程调度。 

// kernel/futex.c
static int futex_wait(...) {
    ...
    futex_wait_queue_me(...);
    ...
}

// kernel/futex.c
static void futex_wait_queue_me(...) {
    ...
//  调用futex的进程变为睡眠状态
    set_current_state(TASK_INTERRUPTIBLE);
    ...
    freezable_schedule();
    ...
}

// include/linux/freezer.h
static inline void freezable_schedule(void) {
    ...
    schedule();
    ...
}
Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐