【问题标题】:Can statvfs block on certain network devices? How to handle that case?statvfs 可以阻止某些网络设备吗?这种情况怎么处理?
【发布时间】:2018-07-21 11:04:27
【问题描述】:

我正在使用 keybase(您的 SSH 和其他密钥的云基础数据存储),但今天我启动 X-Windows 时它没有重新启动。

因此,命令df(以及我的代码中的statvfs())在告诉我传输已关闭后会阻塞。

$ df
df: '/home/alexis/"/home/alexis/.local/share/keybase/fs"': Transport endpoint is not connected
df: /run/user/1000/gvfs: Transport endpoint is not connected
_

提示会坐在那里,永远不会回来。

我不太在意df 目前会卡住,但我想知道我应该如何更新我的 C++ 代码来处理statvfs() 在我的应用程序中阻塞的情况,因为那是不可接受的。我只是看不到在不使用信号的情况下中断该呼叫的方法(想到 SIGALRM)。

有没有更好的方法来处理这种情况?

(注意:我的代码是用 C++ 编写的,尽管 C 解决方案应该可以正常工作并且可能是所需的,因此使用两种语言进行标记。)

【问题讨论】:

  • 您的应用程序是单线程的吗?您必须安装您的 SIGALARM 处理程序而不使用默认的 SA_RESTART,如果您在整个应用程序中这样做,可能会有很多其他代码会中断。而且,如果您是多线程的,则无论如何您都可能会破坏其他线程。
  • @AndrewHenle 它是单线程的。确保它不会到处损坏的最简单方法......这也是一个短暂的 fork()-ed 过程。我想SA_RESTART 会重新启动statvfs()... 从而继续阻塞。嗯...
  • 如果你的进程是单线程的,只需使用sigaction()为SIGALARM安装一个信号处理程序没有SA_RESTART标志,所以警报会导致@ 987654333@ 失败,errno 设置为 EINTR。

标签: c++ c networking disk


【解决方案1】:

此代码会将statvfs() 包装在一个设置警报以中断呼叫的函数中。如果警报触发并中断对statvfs() 的调用,它将返回-1,并将errno 设置为EINTR(我没有尝试过,所以它可能并不完美......):

#include <sigaction.h>
#include <sys/statvfs.h>
#include <unistd.h>
#include <string.h>

// alarm handler doesn't need to do anything
// other than simply exist
static void alarm_handler( int sig )
{
    return;
}

 .
 .
 .

// statvfs() with a timeout measured in seconds
// will return -1 with errno set to EINTR should
// it time out
int statvfs_try( const char *path, struct statvfs *s, unsigned int seconds )
{
    struct sigaction newact;
    struct sigaction oldact;

    // make sure they're entirely clear (yes I'm paranoid...)
    memset( &newact, 0, sizeof( newact ) );
    memset( &oldact, 0, sizeof( oldact) );

    sigemptyset( &newact.sa_mask );

    // note that does not have SA_RESTART set, so
    // statvfs should be interrupted on a signal
    // (hopefully your libc doesn't restart it...)
    newact.sa_flags = 0;
    newact.sa_handler = alarm_handler;
    sigaction( SIGALRM, &newact, &oldact );

    alarm( seconds );

    // clear errno
    errno = 0;
    int rc = statvfs( path, s );

    // save the errno value as alarm() and sigaction() might change it
    int save_errno = errno;

    // clear any alarm and reset the signal handler
    alarm( 0 );
    sigaction( SIGALRM, &oldact, NULL );

    errno = saved_errno;
    return( rc );
}

这也可以使用一些错误检查,尤其是在 sigaction() 调用中,但它已经足够长以生成滚动条,所以我省略了。

如果您发现您的进程仍然停留在statvfs() 调用中,并且如果您在Linux 上运行,请在strace 下运行您的进程并跟踪实际的系统调用。您应该看到对statvfs() 的调用,然后是一个中断statvfs() 调用的警报信号。如果您看到另一个调用statvfs(),这意味着您的libc 已重新启动系统调用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-08-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-16
    • 1970-01-01
    相关资源
    最近更新 更多