Linux Futex Syscall ложных пробуждений с возвращаемым значением 0? - PullRequest
6 голосов
/ 11 сентября 2011

У меня возникла проблема с системным вызовом Linux futex (операция FUTEX_WAIT), который иногда возвращался рано без видимой причины. В документации указаны некоторые условия, которые могут привести к его раннему возврату (без FUTEX_WAKE), но все они включают ненулевые возвращаемые значения: EAGAIN, если значение по адресу futex не совпадает, ETIMEDOUT для синхронизированных ожиданий, которые тайм-аут, EINTR при прерывании (без перезапуска) сигнала и т. д. Но я вижу возвращаемое значение 0. Что, кроме FUTEX_WAKE или завершение потока, указатель set_tid_address которого указывает на futex, может привести к возвращению FUTEX_WAIT со значением возврата 0?

В случае, если это полезно, конкретный futex, на котором я ожидал, это адрес tid потока (установленный с помощью системного вызова clone с помощью CLONE_CHILD_CLEARTID), и у потока not завершено. Мое (очевидно неверное) предположение, что операция FUTEX_WAIT, возвращающая 0, могла произойти только тогда, когда поток завершился, что привело к серьезным ошибкам в логике программы, которые я с тех пор исправил, повторяя цикл и повторяя попытку, даже если он возвращает 0, но теперь я Любопытно, почему это произошло.

Вот минимальный тестовый пример:

#define _GNU_SOURCE
#include <sched.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <linux/futex.h>
#include <signal.h>

static char stack[32768];
static int tid;

static int foo(void *p)
{
        syscall(SYS_getpid);
        syscall(SYS_getpid);
        syscall(SYS_exit, 0);
}

int main()
{
        int pid = getpid();
        for (;;) {
                int x = clone(foo, stack+sizeof stack,
                        CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND
                        |CLONE_THREAD|CLONE_SYSVSEM //|CLONE_SETTLS
                        |CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID
                        |CLONE_DETACHED,
                        0, &tid, 0, &tid);
                syscall(SYS_futex, &tid, FUTEX_WAIT, x, 0);
                /* Should fail... */
                syscall(SYS_tgkill, pid, tid, SIGKILL);
        }
}

Дайте ему поработать некоторое время, в конце концов он должен завершиться с Killed (SIGKILL), что возможно только в том случае, если поток все еще существует, когда возвращается FUTEX_WAIT.

Прежде чем кто-либо предположит, что это всего лишь ядро, разбудившее futex, прежде чем оно завершит уничтожение потока (что на самом деле может происходить в моем минимальном тестовом примере здесь), пожалуйста, обратите внимание, что в моем исходном коде я действительно наблюдал, как выполняется код пользовательского пространства. в теме хорошо после FUTEX_WAIT вернулся.

1 Ответ

0 голосов
/ 15 сентября 2011

Не могли бы вы иметь дело с состоянием гонки между тем, выполняются ли вначале родительские или дочерние операции? Вероятно, вы можете исследовать эту теорию, поместив маленькие спящие в начале функции foo () или сразу после clone (), чтобы определить, маскирует ли проблему принудительная последовательность событий. Я не рекомендую что-то исправлять, но это может быть полезно для расследования. Может быть, фьютекс не готов к тому, чтобы его можно было ждать, пока ребенок не продолжит инициализацию, но клона родителя достаточно, чтобы вернуться к вызывающей стороне?

В частности, наличие опции CLONE_VFORK, похоже, подразумевает, что это опасный сценарий. Вам может понадобиться механизм двунаправленной сигнализации, чтобы ребенок сигнализировал родителю, что он достаточно далеко продвинулся, чтобы его можно было безопасно ждать.

...