작업 중.

NAME

futex - 빠른 사용자 공간 락킹

SYNOPSIS

#include <linux/futex.h>
#include <stdint.h>
#include <sys/time.h>

long futex(uint32_t *uaddr, int futex_op, uint32_t val,
          const struct timespec *timeout,   /* 또는: uint32_t val2 */
          uint32_t *uaddr2, uint32_t val3);

주의: 이 시스템 호출에 대한 glibc 래퍼가 없다. NOTES 참고.

DESCRIPTION

futex() 시스템 호출은 어떤 조건이 참이 될 때까지 기다리는 방법을 제공한다. 보통 공유 메모리 동기화 쪽에서 블로킹 요소로 쓰인다. 퓨텍스 사용 시 동기화 동작은 대부분 사용자 공간에서 이뤄진다. ...

....

....

....

....

....

인자

....

....

....

....

....

퓨텍스 동작

....

FUTEX_PRIVATE_FLAG (리눅스 2.6.22부터)

....

....

FUTEX_CLOCK_REALTIME (리눅스 2.6.28부터)

....

....

....

....

FUTEX_WAIT (리눅스 2.6.0부터)

....

....

....

....

uaddr2, val3 인자는 무시한다.

FUTEX_WAKE (리눅스 부터 2.6.0)

....

timeout, uaddr2, val3 인자는 무시한다.

FUTEX_FD (리눅스 2.6.0부터 리눅스 2.6.25까지)

....

....

timeout, uaddr2, val3 인자는 무시한다.

근본적으로 경쟁이 있으므로 리눅스 2.6.26부터 FUTEX_FD가 제거되었다.

FUTEX_REQUEUE (리눅스 2.6.0부터)
....
FUTEX_CMP_REQUEUE (리눅스 2.6.7부터)

....

....

....

....

....

lock(A);
while (!check_value(V)) {
    unlock(A);
    block_on(B);
    lock(A);
}
unlock(A);

....

FUTEX_WAKE_OP (리눅스 2.6.14부터)

....

....

uint32_t oldval = *(uint32_t *) uaddr2;
*(uint32_t *) uaddr2 = oldval op oparg;
futex(uaddr, FUTEX_WAKE, val, 0, 0, 0);
if (oldval cmp cmparg)
    futex(uaddr2, FUTEX_WAKE, val2, 0, 0, 0);

....

  • ....

  • ....

  • ....

....

+---+---+-----------+-----------+
|op |cmp|   oparg   |  cmparg   |
+---+---+-----------+-----------+
  4   4       12          12    <== 비트 수

....

#define FUTEX_OP(op, oparg, cmp, cmparg) \
                (((op & 0xf) << 28) | \
                ((cmp & 0xf) << 24) | \
                ((oparg & 0xfff) << 12) | \
                (cmparg & 0xfff))

....

....

FUTEX_OP_SET        0  /* uaddr2 = oparg; */
FUTEX_OP_ADD        1  /* uaddr2 += oparg; */
FUTEX_OP_OR         2  /* uaddr2 |= oparg; */
FUTEX_OP_ANDN       3  /* uaddr2 &= ~oparg; */
FUTEX_OP_XOR        4  /* uaddr2 ^= oparg; */

....

FUTEX_OP_ARG_SHIFT  8  /* Use (1 << oparg) as operand */

....

FUTEX_OP_CMP_EQ     0  /* if (oldval == cmparg) wake */
FUTEX_OP_CMP_NE     1  /* if (oldval != cmparg) wake */
FUTEX_OP_CMP_LT     2  /* if (oldval < cmparg) wake */
FUTEX_OP_CMP_LE     3  /* if (oldval <= cmparg) wake */
FUTEX_OP_CMP_GT     4  /* if (oldval > cmparg) wake */
FUTEX_OP_CMP_GE     5  /* if (oldval >= cmparg) wake */

....

FUTEX_WAIT_BITSET (리눅스 2.6.25부터)

....

....

uaddr2 인자는 무시한다.

FUTEX_WAKE_BITSET (리눅스 2.6.25부터)

....

....

....

uaddr2, timeout 인자는 무시한다.

우선순위 상속 퓨텍스

....

....

....

....

....

....

....

....

....

....

....

FUTEX_LOCK_PI (리눅스 2.6.18부터)

....

....

  1. ....

  2. ....

  3. ....

....

....

uaddr2, val, val3 인자는 무시한다.

FUTEX_TRYLOCK_PI (리눅스 2.6.18부터)

....

....

uaddr2, val, timeout, val3 인자는 무시한다.

FUTEX_UNLOCK_PI (리눅스 2.6.18부터)

....

....

uaddr2, val, timeout, val3 인자는 무시한다.

FUTEX_CMP_REQUEUE_PI (리눅스 2.6.31부터)

....

....

....

FUTEX_WAIT_REQUEUE_PI (리눅스 2.6.31부터)

....

....

....

val3 인자는 무시한다.

....

RETURN VALUE

....

....

FUTEX_WAIT
....
FUTEX_WAKE
....
FUTEX_FD
....
FUTEX_REQUEUE
....
FUTEX_CMP_REQUEUE
....
FUTEX_WAKE_OP
....
FUTEX_WAIT_BITSET
....
FUTEX_WAKE_BITSET
....
FUTEX_LOCK_PI
....
FUTEX_TRYLOCK_PI
....
FUTEX_UNLOCK_PI
....
FUTEX_CMP_REQUEUE_PI
....
FUTEX_WAIT_REQUEUE_PI
....

ERRORS

EACCES
....
EAGAIN

....

주의: ....

EAGAIN
....
EAGAIN
....
EDEADLK
....
EDEADLK
....
EFAULT
....
EINTR
....
EINVAL
....
EINVAL
....
EINVAL
....
EINVAL
....
EINVAL
....
EINVAL
....
EINVAL
....
EINVAL
....
EINVAL
....
EINVAL
....
EINVAL
....
EINVAL
....
EINVAL
....
ENFILE
....
ENOMEM
....
ENOSYS
....
ENOSYS
....
ENOSYS
....
EPERM
....
EPERM
....
ESRCH
....
ESRCH
....
ETIMEDOUT
....

VERSIONS

....

....

CONFORMING TO

이 시스템 호출은 리눅스 전용이다.

NOTES

glibc에서 이 시스템 호출에 대한 래퍼를 제공하지 않는다. syscall(2)을 이용해 호출해야 한다.

....

EXAMPLES

....

$ ./futex_demo
Parent (18534) 0
Child  (18535) 0
Parent (18534) 1
Child  (18535) 1
Parent (18534) 2
Child  (18535) 2
Parent (18534) 3
Child  (18535) 3
Parent (18534) 4
Child  (18535) 4

프로그램 소스

/* futex_demo.c

   Usage: futex_demo [nloops]
                    (Default: 5)

   Demonstrate the use of futexes in a program where parent and child
   use a pair of futexes located inside a shared anonymous mapping to
   synchronize access to a shared resource: the terminal. The two
   processes each write 'num-loops' messages to the terminal and employ
   a synchronization protocol that ensures that they alternate in
   writing messages.
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <errno.h>
#include <stdatomic.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <linux/futex.h>
#include <sys/time.h>

#define errExit(msg)    do { perror(msg); exit(EXIT_FAILURE); \
                        } while (0)

static uint32_t *futex1, *futex2, *iaddr;

static int
futex(uint32_t *uaddr, int futex_op, uint32_t val,
      const struct timespec *timeout, uint32_t *uaddr2, uint32_t val3)
{
    return syscall(SYS_futex, uaddr, futex_op, val,
                   timeout, uaddr2, val3);
}

/* Acquire the futex pointed to by 'futexp': wait for its value to
   become 1, and then set the value to 0. */

static void
fwait(uint32_t *futexp)
{
    long s;

    /* atomic_compare_exchange_strong(ptr, oldval, newval)
       atomically performs the equivalent of:

           if (*ptr == *oldval)
               *ptr = newval;

       It returns true if the test yielded true and *ptr was updated. */

    while (1) {

        /* Is the futex available? */

        const uint32_t one = 1;
        if (atomic_compare_exchange_strong(futexp, &one, 0))
            break;      /* Yes */

        /* Futex is not available; wait. */

        s = futex(futexp, FUTEX_WAIT, 0, NULL, NULL, 0);
        if (s == -1 && errno != EAGAIN)
            errExit("futex-FUTEX_WAIT");
    }
}

/* Release the futex pointed to by 'futexp': if the futex currently
   has the value 0, set its value to 1 and the wake any futex waiters,
   so that if the peer is blocked in fwait(), it can proceed. */

static void
fpost(uint32_t *futexp)
{
    long s;

    /* atomic_compare_exchange_strong() was described
       in comments above */

    const uint32_t zero = 0;
    if (atomic_compare_exchange_strong(futexp, &zero, 1)) {
        s = futex(futexp, FUTEX_WAKE, 1, NULL, NULL, 0);
        if (s  == -1)
            errExit("futex-FUTEX_WAKE");
    }
}

int
main(int argc, char *argv[])
{
    pid_t childPid;
    int nloops;

    setbuf(stdout, NULL);

    nloops = (argc > 1) ? atoi(argv[1]) : 5;

    /* Create a shared anonymous mapping that will hold the futexes.
       Since the futexes are being shared between processes, we
       subsequently use the "shared" futex operations (i.e., not the
       ones suffixed "_PRIVATE"). */

    iaddr = mmap(NULL, sizeof(*iaddr) * 2, PROT_READ | PROT_WRITE,
                MAP_ANONYMOUS | MAP_SHARED, -1, 0);
    if (iaddr == MAP_FAILED)
        errExit("mmap");

    futex1 = &iaddr[0];
    futex2 = &iaddr[1];

    *futex1 = 0;        /* State: unavailable */
    *futex2 = 1;        /* State: available */

    /* Create a child process that inherits the shared anonymous
       mapping. */

    childPid = fork();
    if (childPid == -1)
        errExit("fork");

    if (childPid == 0) {        /* Child */
        for (int j = 0; j < nloops; j++) {
            fwait(futex1);
            printf("Child  (%jd) %d\n", (intmax_t) getpid(), j);
            fpost(futex2);
        }

        exit(EXIT_SUCCESS);
    }

    /* Parent falls through to here. */

    for (int j = 0; j < nloops; j++) {
        fwait(futex2);
        printf("Parent (%jd) %d\n", (intmax_t) getpid(), j);
        fpost(futex1);
    }

    wait(NULL);

    exit(EXIT_SUCCESS);
}

SEE ALSO

get_robust_list(2), restart_syscall(2), pthread_mutexattr_getprotocol(3), futex(7), sched(7)

다음 커널 소스 파일:

  • Documentation/pi-futex.txt

  • Documentation/futex-requeue-pi.txt

  • Documentation/locking/rt-mutex.txt

  • Documentation/locking/rt-mutex-design.txt

  • Documentation/robust-futex-ABI.txt

Franke, H., Russell. R., and Kirwood, M., 2002. Fuss, Futexes and Furwocks: Fast Userlevel Locking in Linux (from proceedings of the Ottawa Linux Symposium 2002), http://kernel.org/doc/ols/2002/ols2002-pages-479-495.pdf

Hart, D., 2009. A futex overview and update, http://lwn.net/Articles/360699/

Hart, D. and Guniguntala, D., 2009, Requeue-PI: Making Glibc Condvars PI-Aware (from proceedings of the 2009 Real-Time Linux Workshop), http://lwn.net/images/conf/rtlws11/papers/proc/p10.pdf

Drepper, U., 2011, Futexes Are Tricky, http://www.akkadia.org/drepper/futex.pdf

Futex 예시 라이브러리, futex-*.tar.bz2 at ftp://ftp.kernel.org/pub/linux/kernel/people/rusty/


2021-03-22