Futex is designed to be a user space fast lock operation, implemented by user space FastPath, and the kernel provides lock competition queue arbitration Service, which is implemented Slowpath by user space using Futex system call. The Futex system call provides three mating call interfaces for different use cases, Noraml Futex,pi-futex, and Requeue-pi, respectively.
The Futex synchronization (lock) state definition is performed by the user space, and the Futex system call does not need to understand how user space defines and uses the 4-byte long integer futex of this address alignment, except Pi-futex, where the user space must use the lock rule defined by the Futex system call. User space through the bus lock atom access to this integer futex, the state of the modification, lock, unlock and lock competition. When user space discovery Futex enters a state where a definition needs to be queued, the user space needs to be queued using Futex system calls, waiting for the queue to wake up and then back to the user space again for lock-up operations. When the lock competes, each lock and unlock, must carry on the user space lock operation and the Futex system call successively, and two steps is not atomically executes, the lock and the unlock execution process may occur the disorderly order.
This is what we want.
Task A |
Futex in User |
Futex Queue in Kerenl |
Task B |
|
Owned |
Empty |
1. Own Futex |
1.try Lock (try to modify Futex) |
|
Empty |
|
2.mark Waiter (Find lock competition, modify Futex status) |
Owned-waiters |
Empty |
|
3.futex_wait |
0 |
Empty |
2. Unlock (modify Futex, get old state as waiters) |
4. Enqueue |
0 |
Has waiter |
3. Futex_wake (found with lock competition) |
5. Sleep and schedule |
0 |
Has waiter |
4. Dequeue |
|
0 |
Empty |
5. Wakeup |
6. Wokenup |
0 |
Empty |
|
7.try Lock again (after being awakened, I don't know if there are any other tasks waiting, So lock the race state to lock to make sure that you slowpath when you unlock, Perform kernel checks for any other waiting tasks) |
0-Waiters |
Empty |
|
8. Own Futex |
Waiters |
Empty |
|
9. Unlock (approach to Slowpath) |
Waiters |
|
|
However, we do not want the situation, although the bus lock atomic operation makes the lock and unlock user space phase operation to lock first, let Futex to lock the competition state, so that lock and unlock are slowpath. However, when they invoke the Futex system call separately, the CPU that executes futex_wait is interrupted and Futex_wake is executed before futex_wait. Futex_wake found no task to wake up and left. Then the late futex_wait was unaware, and resolutely queued for a lock that had been released. Thus, if the lock does not compete in the future, task A will not be awakened and forgotten.
Task A |
Futex in User |
Futex Queue in Kerenl |
Task B |
|
Owned |
Empty |
1. Own Futex |
1.try Lock (try to modify Futex) |
|
Empty |
|
2.mark Waiter (Find lock competition, modify Futex status) |
Owned | Waiters |
Empty |
|
3.futex_wait |
0 |
Empty |
2. Unlock (modify Futex, get old status to owned | waiters) |
interupted |
0 |
Empty |
3. Futex_wake (found with lock competition) |
interupted |
0 |
Empty |
4. Quit |
4. Enqueue |
0 |
Has waiter |
|
5. Sleep and schedule |
0 |
Has waiter |
|
|
|
|
|
|
|
|
|
|
|
|
|
So the Futex system calls that need to be queued are required to pass the current copy of Futex as a parameter, and the Futex system call is compared with the latest futex of the replica and user space before executing the queue, deciding whether to return the user space and let the user space be re-judged. For Pi-futex's FUTEX_LOCK_PI system invoke operation portal, user space is not required to pass in the current Futex copy because the user space must use the lock rules that are called by the Futex system to Pi-futex, FUTEX_LOCK_PI The function then determines whether the Pi-futex is released with the Pi-futex lock rule. This futex_lock_pi is a futex_unlock_pi when a user-space Futex follows the futex.h-Pi-futex lock-state rule and uses Futex and Pi-futex operations called by the Futex system.
Futex system calls the paired operation entry:
1. Normal Futex:
static int futex_wait(u32 __user *uaddr, unsigned int flags, U32 val, ktime_t *abs_ti Me, u32 Bitset)
static int futex_wake(u32 __user *uaddr, unsigned int flags, int nr_wake, u32 bitset)
2. Pi-futex:
static int futex_lock_pi(u32 __user *uaddr, unsigned int flags, int detect, ktime_t *time, int trylock)
static int futex_unlock_pi(u32 __user *uaddr, unsigned int flags)
3. Requeue-pi:
static int futex_wait_requeue_pi(u32 __user *uaddr, unsigned int flags, u32 val , ktime_t *abs_time, U32 bitset, u32 __user *uaddr2)
static int futex_requeue(u32 __user *uaddr1, unsigned int flags, u32 __user *uaddr2, int nr_wake, int nr_requeue, u32 *cmpval, int requeue_pi)
futex_wait The rules applied to NON-PI Futex,futex are defined by user space, requiring user space to pass non-pi Futex copy values into the filter work by Futex_wait_ Setup sub-function is completed, and then by Futex_wait_queue_me Sub-function Non-pi Futex queue and sleep wait.
futex_wait_requeue_pi consolidates requeue from Non-pi to Pi Futex non-pi to Non-pi. However, it is the first to futex_wait the Futex of Non-pi, so it is like futex_wait , which requires user space to pass non-pi Futex copy values. So futex_wait_requeue_pi , like its name, splits into two stages, or combines two actions, futex_wait and requeue_pi . futex_wait First, futex_requeue wake-Up execution requeue_pi . You can see the futex_wait_requeue_pi The first half of the code and   futex_wait The code flow is similar.
FUTEX_LOCK_PI rules applied to Pi-futex,futex are defined by the Futex system call (header file), and user space must be used in accordance with the rules. Because the rules are defined by the kernel and do not require user space to pass in a Futex current copy, and also in the kernel, before using the Rt_mutex proxy queue wait, the Futex_lock_pi_atomic lock attempt, failed to enter Rt_ The mutex agent is queued for waiting. After the queue wakes up, the Pi-futex of the user space is locked by Fixup_owner and Fixup_pi_state_owner. Here are two points to note that the Rt_mutex lock rule is marked with a task_struct pointer, while the Pi-futex lock rule is marked with a PID (TID) number. In addition Pi-futex queue also note, Pi-futex although in Rt_mutex agent on the queue, but also like Non-pi Futex into the list of Futex_hash_bucket, for is not queued, but let the back in the queue, You can find Futex_queue in Futex_hash_bucket to get Futex_pi_state (Rt_mutex proxy).
Futex system invocation of the Linux kernel (ii)