작업 중.
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_WAITERS | TID
....
....
....
....
....
....
....
-
....
-
....
....
FUTEX_LOCK_PI
(리눅스 2.6.18부터)-
....
....
-
....
-
....
-
....
....
....
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