libfuse:退出fuse_session_loop
上下文: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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
这可能值得提交一份错误报告;它也可能被关闭为“按预期工作”。
也就是说,如果您还发送信号来中断
fuse_kern_chan_receive()
函数中执行的read()
调用,则它似乎已准备好传播错误通过堆栈,这将触发更高级别的调用中的继续,这将注意到退出标志,并希望尽可能干净地终止循环。尝试添加
pthread_kill(3)
来终止有问题的特定线程。fuse_signals.c
为调用fuse_session_exit()
的SIGHUP
、SIGINT
和SIGTERM
安装处理程序>。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 thefuse_kern_chan_receive()
function, it appears prepared to propagate the error up through the stack, which will trigger thecontinue
in the higher-level call, which will notice theexited
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 forSIGHUP
,SIGINT
andSIGTERM
that callfuse_session_exit()
.通常,如果在系统调用(例如 read(2))阻塞时执行信号处理程序,系统调用会立即返回(在信号处理程序完成执行后),并带有 EINTR< /强>。这显然是 fuse_session_loop 和 fuse_session_exit 的设计目的。
但是,如果信号处理程序安装时设置了SA_RESTART标志(请参阅sigaction(2)),则系统调用在信号处理程序已执行。系统调用将恢复阻塞。
由于某种原因,在我的系统(Ubuntu 11.10 x86_64)上,signal(2) 的默认行为是使用 SA_RESTART 标志安装信号处理程序。
即以下程序的 strace...
...如下...
因此信号(在熔丝和我自己的程序提供的示例中)没有中断 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...
...is as follows...
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.
我无法通过将熔丝 2.9.2 的 SA_RESTART 标志归零来解决问题。
相反,当我想退出时,我使用了假读。
fuse_session_exit
之前打开文件fuse_session_exit
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.
fuse_session_exit
fuse_session_exit
阅读有关信号的手册页: http://man7.org/linux/手册页/man2/signal.2.html
因此 GLibc 过去常常让信号在之后不会重新启动,但它们改变了
使其与 BSD 更加兼容。
Read the manpage on signal: http://man7.org/linux/man-pages/man2/signal.2.html
So GLibc used to have signal not restart afterwards but they changed
it to make it more compatible with BSD.