libfuse: выход из fuse_session_loop - PullRequest
5 голосов
/ 18 января 2012

Контекст: 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, который блокирует системный вызов «read» устройства «/ 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 также делает что-то, что прерывает вызов чтения, что по какой-то причине не работает в моей системе.

Есть идеи?

Ответы [ 4 ]

2 голосов
/ 18 января 2012

Это может стоить сообщения об ошибке; он также может быть закрыт как "работающий как ожидалось".

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

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

1 голос
/ 19 января 2012

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

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

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

, то есть следующая программа ...

#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 ?Я ожидаю, что прерывание (не перезапуск) - ожидаемое поведение по умолчанию.

0 голосов
/ 08 апреля 2018

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

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

Открыть файл перед вызовом fuse_session_exit Вызвать fuse_session_exit Считать байт из файла
0 голосов
/ 18 ноября 2014

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

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

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

Ситуация в Linux выглядит следующим образом:

  • Системный вызов 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.

...