libfuse: выход из fuse_session_loop

Контекст: Ubuntu 11.10 и libfuse 2.8.4-1.4ubuntu1 Linux 3.0.0-14-generic #23-Ubuntu SMP Пн, 21 ноября, 20:28:43 UTC 2011 x86_64 x86_64 x86_64 GNU/Linux

Я пытаюсь использовать libfuse. Я хочу, чтобы fuse_session_loop завершился (из обработчика сигнала или другого потока), но когда я вызываю fuse_session_exit, ничего не происходит, пока сеанс не получит новый запрос.

fuse_session_exit устанавливает флаг, который считывается fuse_session_exited. Отладка в fuse_session_loop, кажется, блокирует fuse_chan_recv, поэтому он не проверяет fuse_session_exited снова до начала цикла...

int fuse_session_loop(struct fuse_session *se)
{
    int res = 0;
    struct fuse_chan *ch = fuse_session_next_chan(se, NULL);
    size_t bufsize = fuse_chan_bufsize(ch);
    char *buf = (char *) malloc(bufsize);
    if (!buf) {
        fprintf(stderr, "fuse: failed to allocate read buffer\n");
        return -1;
    }

    while (!fuse_session_exited(se)) {
        struct fuse_chan *tmpch = ch;
        res = fuse_chan_recv(&tmpch, buf, bufsize); <--- BLOCKING
        if (res == -EINTR)
            continue;
        if (res <= 0)
            break;
        fuse_session_process(se, buf, res, tmpch);
    }

    free(buf);
    fuse_session_reset(se);
    return res < 0 ? -1 : 0;
}

fuse_chan_recv вызывает fuse_kern_chan_receive, который блокирует системный вызов «чтение» устройства «/dev/fuse», поэтому, даже если установлен флаг fuse_session_exited, ничего не происходит.

static int fuse_kern_chan_receive(struct fuse_chan **chp, char *buf,
                  size_t size)
{
    struct fuse_chan *ch = *chp;
    int err;
    ssize_t res;
    struct fuse_session *se = fuse_chan_session(ch);
    assert(se != NULL);

restart:
    res = read(fuse_chan_fd(ch), buf, size); <--- BLOCKING
    err = errno;

    if (fuse_session_exited(se))
        return 0;
    if (res == -1) {
        /* ENOENT means the operation was interrupted, it's safe
           to restart */
        if (err == ENOENT)
            goto restart;

        if (err == ENODEV) {
            fuse_session_exit(se);
            return 0;
        }
        /* Errors occuring during normal operation: EINTR (read
           interrupted), EAGAIN (nonblocking I/O), ENODEV (filesystem
           umounted) */
        if (err != EINTR && err != EAGAIN)
            perror("fuse: reading device");
        return -err;
    }
    if ((size_t) res < sizeof(struct fuse_in_header)) {
        fprintf(stderr, "short read on fuse device\n");
        return -EIO;
    }
    return res;
}

Эта проблема, по-видимому, влияет на пример hello_ll.c, поставляемый с libfuse, а также на мою программу. Это заставляет меня думать, что, возможно, есть какой-то механизм, который не работает, но должен. Возможно, fuse_session_exit также должен делать что-то, что прерывает вызов чтения, что по какой-то причине не работает в моей системе.

Есть идеи?


person Andrew Tomazos    schedule 18.01.2012    source источник


Ответы (4)


Это может стоить отчета об ошибке; он также может быть закрыт как «работающий как положено».

Тем не менее, если вы также отправляете сигнал для прерывания вызова read(), выполняемого в функции fuse_kern_chan_receive(), создается впечатление, что он готов распространить ошибку вверх по стеку, что вызовет continue в вызове более высокого уровня. , который заметит флаг exited и, надеюсь, завершит цикл как можно более аккуратно.

Попробуйте добавить pthread_kill(3), чтобы убить конкретный поток, о котором идет речь. fuse_signals.c устанавливает обработчики для SIGHUP, SIGINT и SIGTERM, которые вызывают fuse_session_exit().

person sarnold    schedule 18.01.2012
comment
Кажется, я разобрался, я устанавливаю обработчик сигнала с помощью обычного вызова сигнала, который по какой-то причине передается ядру как: rt_sigaction(SIGINT, {0x40af90, [INT], SA_RESTORER|SA_RESTART, 0x7fcac93ea420}, {SIG_DFL, [], 0}, 8) = 0. По какой-то причине здесь установлен флаг SA_RESTART, поэтому при вызове обработчика сигнала системный вызов чтения не прерывается, а возобновляется. - person Andrew Tomazos; 18.01.2012

Обычно, если обработчик сигнала выполняется во время блокировки системного вызова (например, read(2)), системный вызов немедленно возвращается (после завершения выполнения обработчика сигнала) с EINTR< /сильный>. Это явно то поведение, для которого были разработаны fuse_session_loop и fuse_session_exit.

Однако, если обработчик сигнала установлен с установленным флагом SA_RESTART (см. sigaction(2)), системный вызов не вернется с EINTR после обработчик сигнала выполнился. Вместо этого системный вызов возобновит блокировку.

По какой-то причине в моей системе (Ubuntu 11.10 x86_64) по умолчанию функция signal(2) устанавливает обработчик сигнала с флагом SA_RESTART.

то есть strace следующей программы...

#include <stdlib.h>
#include <signal.h>

void f(int signum) {}

int main()
{
    signal(SIGINT,f);
    return EXIT_SUCCESS;
}

...составляет...

rt_sigaction(SIGINT, {0x400524, [INT], SA_RESTORER|SA_RESTART, 0x7f4997e1f420}, {SIG_DFL, [], 0}, 8) = 0

По этой причине сигналы (в примерах, поставляемых с fuse и моей собственной программой) не прерывали чтение блокировки в fuse_kern_chan_receive, как того ожидали их авторы.

Исправление состояло в том, чтобы использовать sigaction(2) (и оставить бит SA_RESTART равным нулю) для установки обработчика (вместо signal(2)).

Остается открытым вопрос: почему при вызове signal(2) по умолчанию установлен флаг SA_RESTART? Я ожидаю, что прерывание (не перезапуск) является ожидаемым поведением по умолчанию.

person Andrew Tomazos    schedule 18.01.2012

Я не смог решить проблему, обнулив флаг SA_RESTART для фьюза 2.9.2.

Вместо этого я использовал фальшивое чтение, когда хочу выйти.

  1. Откройте файл перед вызовом fuse_session_exit
  2. Звоните fuse_session_exit
  3. Прочитать байт из файла
person Ricardo Cristian Ramirez    schedule 08.04.2018

Прочтите справочную страницу по сигналу: http://man7.org/linux/man-pages/man2/signal.2.html

Поведение signal() различается в разных версиях UNIX, а также исторически менялось в разных версиях Linux. Избегайте его использования: вместо этого используйте sigaction(2). См. Переносимость ниже.

Единственное переносимое использование signal() — установить расположение сигнала в SIG_DFL или SIG_IGN. Семантика при использовании signal() для установки обработчика сигнала различается в разных системах (и POSIX.1 явно разрешает это изменение); не используйте его для этой цели.

В линуксе ситуация следующая:

  • Системный вызов signal() ядра обеспечивает семантику System V.

  • По умолчанию в glibc 2 и более поздних версиях функция-оболочка signal() не вызывает системный вызов ядра. Вместо этого он вызывает sigaction(2), используя флаги, обеспечивающие семантику BSD. Это поведение по умолчанию обеспечивается до тех пор, пока определен макрос тестирования функций _BSD_SOURCE. По умолчанию определен _BSD_SOURCE; он также определяется неявно, если определяется _GNU_SOURCE, и, конечно, может быть определен явно.

  • В glibc 2 и более поздних версиях, если макрос тестирования функций _BSD_SOURCE не определен, то signal() обеспечивает семантику System V. (Неявное определение _BSD_SOURCE по умолчанию не предоставляется, если кто-то вызывает gcc(1) в одном из его стандартных режимов (-std=xxx или -ansi) или определяет различные другие макросы тестирования функций, такие как _POSIX_SOURCE, _XOPEN_SOURCE или _SVID_SOURCE; см. feature_test_macros (7).)

Таким образом, GLibc раньше не перезапускал сигнал после этого, но они изменили его, чтобы сделать его более совместимым с BSD.

person Molossus Spondee    schedule 17.11.2014