libfuse:退出fuse_session_loop

发布于 2024-12-27 19:04:09 字数 2503 浏览 2 评论 0原文

上下文:Ubuntu 11.10 和 libfuse 2.8.4-1.4ubuntu1 Linux 3.0.0-14-generic #23-Ubuntu SMP Mon Nov 21 20:28:43 UTC 2011 x86_64 x86_64 x86_64 GNU/Linux

我正在尝试使用 libfuse。我想导致fuse_session_loop退出(从信号处理程序或不同的线程),但是当我调用fuse_session_exit时,在会话收到新请求之前不会发生任何事情。

fusion_session_exit 设置一个由 fusion_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”设备的“read”系统调用,所以即使尽管设置了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;
}

这个问题似乎会影响 libfuse 提供的 hello_ll.c 示例以及我的程序。这让我觉得也许有某种机制没有发挥作用,但它应该发挥作用。也许fuse_session_exit也应该做一些中断读取调用的事情,由于某种原因,它在我的系统上不起作用。

有什么想法吗?

Context: Ubuntu 11.10 and libfuse 2.8.4-1.4ubuntu1
Linux 3.0.0-14-generic #23-Ubuntu SMP Mon Nov 21 20:28:43 UTC 2011 x86_64 x86_64 x86_64 GNU/Linux

I'm trying to use libfuse. I want to cause fuse_session_loop to exit (from a signal handler or a different thread), but when I call fuse_session_exit nothing happens until the session receives a new request.

fuse_session_exit sets a flag that is read by fuse_session_exited. Debugging into fuse_session_loop it appears to block on fuse_chan_recv, so it doesn't check fuse_session_exited again until the top of the loop...

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 calls fuse_kern_chan_receive which blocks on the "read" syscall of the "/dev/fuse" device, so even though the fuse_session_exited flag is set nothing happens yet.

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;
}

This problem seems to effect the hello_ll.c example provided with libfuse as well as my program. It makes me think that perhaps there is some mechanism that is not working that should. Perhaps fuse_session_exit is supposed to be also doing something that interrupts the read call, that for some reason is not working on my system.

Any ideas?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

栖竹 2025-01-03 19:04:09

这可能值得提交一份错误报告;它也可能被关闭为“按预期工作”。

也就是说,如果您发送信号来中断 fuse_kern_chan_receive() 函数中执行的 read() 调用,则它似乎已准备好传播错误通过堆栈,这将触发更高级别的调用中的继续,这将注意到退出标志,并希望尽可能干净地终止循环。

尝试添加 pthread_kill(3) 来终止有问题的特定线程。 fuse_signals.c 为调用 fuse_session_exit()SIGHUPSIGINTSIGTERM 安装处理程序>。

This might be worth a bug report; it might also be closed as "working as expected".

That said, if you also send a signal to interrupt the read() call executing in the fuse_kern_chan_receive() function, it appears prepared to propagate the error up through the stack, which will trigger the continue in the higher-level call, which will notice the exited flag, and hopefully terminate the loop as cleanly as possible.

Try adding a pthread_kill(3) to kill the specific thread in question. fuse_signals.c installs handlers for SIGHUP, SIGINT and SIGTERM that call fuse_session_exit().

杯别 2025-01-03 19:04:09

通常,如果在系统调用(例如 read(2))阻塞时执行信号处理程序,系统调用会立即返回(在信号处理程序完成执行后),并带有 EINTR< /强>。这显然是 fuse_session_loopfuse_session_exit 的设计目的。

但是,如果信号处理程序安装时设置了SA_RESTART标志(请参阅sigaction(2)),则系统调用在信号处理程序已执行。系统调用将恢复阻塞。

由于某种原因,在我的系统(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_kern_chan_receive 中的阻塞读取> 正如他们的作者所期望的那样。

解决方法是使用sigaction(2)(并将SA_RESTART位保留为零)来安装处理程序(而不是signal(2))。

仍然存在的一个悬而未决的问题是,为什么对 signal(2) 的调用默认会打开 SA_RESTART 标志?我希望中断(而不是重新启动)是预期的默认行为。

Normally if a signal handler is executed while a system call (such as read(2)) is blocking, the system call returns immediately (after the signal handler is completed executing) with an EINTR. This is clearly the behaviour that fuse_session_loop and fuse_session_exit were designed for.

However if the signal handler is installed with the SA_RESTART flag set (see sigaction(2)) the system call will not return with EINTR after the signal handler has executed. The system call will resume blocking instead.

For some reason on my system (Ubuntu 11.10 x86_64) the default behaviour of signal(2) is to install the signal handler with the SA_RESTART flag.

ie the strace of the following program...

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

void f(int signum) {}

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

...is as follows...

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

For this reason the signals (in the examples provided with fuse and my own program) did not interrupt the blocking read in fuse_kern_chan_receive as their authors expected them too.

The fix was to use sigaction(2) (and leave SA_RESTART bit zeroed) to install the handler (instead of signal(2)).

An open question that still remains is why does a call to signal(2) have the SA_RESTART flag on by default? I would expect interrupting (not restarting) is the expected default behavior.

动听の歌 2025-01-03 19:04:09

我无法通过将熔丝 2.9.2 的 SA_RESTART 标志归零来解决问题。

相反,当我想退出时,我使用了假读。

  1. 调用 fuse_session_exit 之前打开文件
  2. 调用 fuse_session_exit
  3. 从文件中读取一个字节

I couldn't solve the porblem by zeroing SA_RESTART flag for fuse 2.9.2.

Instead, I used a fake read when I want to exit.

  1. Open the file before calling fuse_session_exit
  2. Call fuse_session_exit
  3. Read a byte from the file
望她远 2025-01-03 19:04:09

阅读有关信号的手册页: http://man7.org/linux/手册页/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 更加兼容。

Read the manpage on signal: http://man7.org/linux/man-pages/man2/signal.2.html

The behavior of signal() varies across UNIX versions, and has also
varied historically across different versions of Linux. Avoid its
use: use sigaction(2) instead. See Portability below.

The only portable use of signal() is to set a signal's disposition
to SIG_DFL or SIG_IGN. The semantics when using signal() to
establish a signal handler vary across systems (and POSIX.1
explicitly permits this variation); do not use it for this purpose.

The situation on Linux is as follows:

  • The kernel's signal() system call provides System V semantics.

  • By default, in glibc 2 and later, the signal() wrapper function
    does not invoke the kernel system call. Instead, it calls
    sigaction(2) using flags that supply BSD semantics. This default
    behavior is provided as long as the _BSD_SOURCE feature test macro
    is defined. By default, _BSD_SOURCE is defined; it is also
    implicitly defined if one defines _GNU_SOURCE, and can of course
    be explicitly defined.

  • On glibc 2 and later, if the _BSD_SOURCE feature test macro is not
    defined, then signal() provides System V semantics. (The default
    implicit definition of _BSD_SOURCE is not provided if one invokes
    gcc(1) in one of its standard modes (-std=xxx or -ansi) or defines
    various other feature test macros such as _POSIX_SOURCE,
    _XOPEN_SOURCE, or _SVID_SOURCE; see feature_test_macros(7).)

So GLibc used to have signal not restart afterwards but they changed
it to make it more compatible with BSD.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文