strace로 프로세스 상태를 체크하다가 보게 되었습니다.


FUTEX 란?

   출처 : http://www.hanbit.co.kr/preview/1492/sample_chap01.pdf

리눅스 커널 2.6에는 새로운 프로세스 간의 통신으로 FUTEX라는 것이 있습니다. 이것은 동일 머신 상의 복수 프로세스 간에 잠금(Lock) 처리를 합니다. 동일 페이지를 프로세스에 매핑하고, 그 위에 잠금 변수를 공유하게 함으로써 경합이 발생하지 않는 한 시스템 콜 개입 없이 잠금 처리를 할 수 있습니다. 많은 프로 세스나 스레드가 협조하여 동작하는 응용 프로그램에서는 매우 유용한 구조입니다.


FUTEX_WAIT ?

                           출처 : https://meenakshi02.wordpress.com/2011/02/02/strace-hanging-at-futex/

FUTEX_WAIT의 의미 

which simply means you are tracing the original parent thread, 

and it’s doing nothing but waiting for some other threads to finish.


[root@upi1 bin]# strace -p 24866
Process 24866 attached – interrupt to quit
futex(0xa280a0c, FUTEX_WAIT, 1, NULL



FUTEX_WAIT 과 FUTEX_WAIT_PRIVATE 차이 ?

출처: http://stackoverflow.com/questions/9983765/what-is-the-difference-between-futex-wait-and-futex-wait-private

This is an optimization done by linux/glibc to make futexes faster when they're not shared between processes. Glibc will use the _PRIVATE versions of each of the futex calls unless the PTHREAD_PROCESS_SHARED attribute is set on your mutex



데모 소스코드


#define _GNU_SOURCE

#include <stdio.h>
#include <errno.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 int *futex1, *futex2, *iaddr;

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

#define _GNU_SOURCE
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

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

static int *futex1, *futex2, *iaddr;

static int futex(int *uaddr, int futex_op, int val,
        const struct timespec *timeout, int *uaddr2, int val3)
{
    return syscall(SYS_futex, uaddr, futex_op, val,
            timeout, uaddr, 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(int *futexp)
{
    int s;

    /* __sync_bool_compare_and_swap(ptr, oldval, newval) is a gcc
       built-in function.  It atomically performs the equivalent of:

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

     It returns true if the test yielded true and *ptr was updated.
     The alternative here would be to employ the equivalent atomic
     machine-language instructions.  For further information, see
     the GCC Manual. */

    while (1) {

        /* Is the futex available? */

        if (__sync_bool_compare_and_swap(futexp, 1, 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 fpost(), it can proceed. */

static void fpost(int *futexp)
{
    int s;

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

    if (__sync_bool_compare_and_swap(futexp, 0, 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 j, 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(int) * 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 (j = 0; j < nloops; j++) {
            fwait(futex1);
            printf("Child  (%ld) %d\n", (long) getpid(), j);
            fpost(futex2);
        }

        exit(EXIT_SUCCESS);
    }

    /* Parent falls through to here */

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

    wait(NULL);

    exit(EXIT_SUCCESS);
}







반응형

+ Recent posts