- 万物皆连。尽管应用程序通常是可以拆分成多个独立子系统,但往往这些子系统的进程通常必须相互通信并在彼此之间共享状态。如数据库系统,它通常在用户空间中维护共享的I/O缓冲区。缓冲区由各种数据库引擎和预取进程并发访问。
- 在传统的UNIX系统中,System-V IPC(进程间通信)如信号量、消息队列、套接字和文件锁(flock ()) 是两个进程同步的基本机制。这些机制在内核中会记录共享状态和原子操作的内核对象,然后暴露给用户一个句柄。这就意味着服务加解锁必须通过系统调用(例如,Semop ()). 这种方法的缺点是每次锁访问都需要系统调用,当锁具有低争用率时,系统调用可能会构成显著的开销。
- 经研究发现,很多同步是无竞争的,即某个进程进入互斥区,到再从某个互斥区出来这段时间,常常是没有进程也要进这个互斥区或者请求同一同步变量的。但是在这种情况下,这个进程也要陷入内核去看看有没有人和它竞争,退出的时还要陷入内核去看看有没有进程等待在同一同步变量上。这些不必要的系统调用(或者说内核陷入)造成了大量的性能开销。为了解决这个问题,Futex就应运而生。futex是一种快速的用户级别的锁,他其实是由用户态和内核态协助完成。
- 在无竞争的情况下,应用程序会自动更改锁状态字,而无需进入内核。因此,原子操作,如atomic_inc (), atomic_dec、cmpxchg (), 和test_and_set () 在用户空间中是必不可少的。
- 在有竞争环境下,应用程序需要等待锁的释放,或者在解锁操作的情况下需要唤醒等待任务。此时需要一个内核对象以及等待队列。
- 尽可能避免系统调用,因为系统调用通常会消耗几百个指令。
- 避免不必要的上下文切换: 上下文切换会使TLB无效等相关的开销,保存内核和用户态的栈。
考虑到更加方便的管理锁对象,所以futex直接通过虚拟地址来处理锁,而不是像以前一样创建一个需要初始化和以及跨进程全局句柄的内核句柄。
- 我们为每个futex使用一个唯一的标识符(它可以在不同的地址空间中共享,所以每个空间中可能有不同的虚拟地址):这个标识符是“strcut page”指针和该page中的偏移量
- 该结构体可以表示进程放在哈希表中的futex对象
futex在用户空间只是一个地址对齐的整形--->uaddr。