7

简而言之,您如何在系统调用中对错误条件(例如 EINTR)进行单元测试。

我正在研究的一个特定示例(可能本身就是一个案例)是,当 fclose 返回带有 (errno==EINTR) 的 EOF 时,是否有必要再次调用 fclose。该行为取决于 fclose 的实现:

// Given an open FILE *fp
while (fclose(fp)==EOF && errno==EINTR) {
    errno = 0;
}

如果在 EINTR 发生时释放 fp,则此调用可能是不安全的。如何测试 when (errno==EINTR) 的错误处理?

4

8 回答 8

4

在这种特殊情况下,再次调用 fclose() 是不安全的,因为 C 标准表示即使调用失败,流也会与文件解除关联(并且变得不确定)。

于 2008-10-07T00:27:08.257 回答
3

使用函数指针结构来抽象出系统函数。用类似 sys->fclose(fp) 的方式替换对 flcose(fp) 的调用。在您的单元测试中,创建一个始终返回 EINTR 的 fclose 实现,然后将 sys->fclose 设置为该版本。

于 2008-10-07T00:21:02.917 回答
3

查看 linux 内核源代码,我找不到任何在文件关闭时甚至返回 EINTR 的驱动程序。

如果您 absosmurfly 必须重现这种情况,您可以在 Linux 中编写自己的驱动程序以在 .release 方法上返回 -EINTR。查看 O'Reilly 的 Linux 设备驱动程序书中的示例代码。scull 项目是最简单的项目之一。你可以把它改成这样:

int scull_release(struct inode *inode, struct file *filp)
{
    return -EINTR;
}

不过,再次通过 linux 源代码树搜索,我找不到任何会在关闭时返回 EINTR 的驱动程序。

编辑- 好的,它看起来像保险丝 - 用户空间文件系统可能能够。这用于 sshfs 之类的东西。虽然仍然是一个边缘案例。

于 2008-10-07T00:38:03.973 回答
2

我认为没有一种简单的方法可以随意进行实际测试。

如果 fclose 操作被信号中断,则将生成 EINTR。这意味着 fclose 位于后台线程中,该线程在处理关闭请求时接收到信号。

祝你好运,试图重现这种情况。

于 2008-10-07T00:06:53.320 回答
1

fclose()正如 Kris 建议的那样,用您在单元测试代码中定义的自己的实现暂时替换对“真实”的调用。您的实现可以做一些简单的事情:

int my_fclose(FILE *fp)
{
  errno = EINTR;
  return EOF;
}

但是,正如 fizzer 指出的那样,fclose()由于行为未定义,您不应该再次调用,因此您甚至不必费心检查条件。

另一个要问的问题是你是否真的需要担心这个;如果您的应用程序代码在您的期间阻止所有可能的信号(SIGKILL 和 SIGSTOP 除外),fclose()那么您将不会得到 EINTR,并且根本不需要担心。

于 2008-10-07T02:20:38.900 回答
1

这是我将如何测试它,只是为了测试,因为 fizzer 提醒我们调用fclose()两次是不安全的。

可以用自己的行为在程序中重新定义 fclose()(或 libc 的任何其他函数)。在类 Unix 系统上,链接器不会抱怨 - 从未在 Windows 上尝试过,但在 cygwin 上尝试过。当然,这会阻止您的其他测试使用 real ,因此必须将此类测试放入单独的测试可执行文件中。fclose()

这是minunit的多合一示例。

#include <errno.h>
#include <stdio.h>
#include <stdbool.h>

/* from minunit.h : http://www.jera.com/techinfo/jtns/jtn002.html */
 #define mu_assert(message, test) do { if (!(test)) return message; } while (0)
 #define mu_run_test(test) do { char *message = test(); tests_run++; \
                                if (message) return message; } while (0)

int tests_run = 0;
bool fclose_shall_fail_on_EINTR = false;

//--- our implemention of fclose()
int fclose(FILE *fp) {
    if (fclose_shall_fail_on_EINTR) {
        errno = EINTR;
        fclose_shall_fail_on_EINTR = false;   //--- reset for next call 
        return EOF;
    } else { return 0; }
}

//--- this is the "production" function to be tested 
void uninterruptible_close(FILE *fp) {
    // Given an open FILE *fp
     while (fclose(fp)==EOF && errno==EINTR) {
        errno = 0;
     }
}

char *test_non_interrupted_fclose(void) {
    FILE *theHandle = NULL;   //--- don't care here
    uninterruptible_close(theHandle);
    mu_assert("test fclose wo/ interruption", 0 == errno);
    return 0;
}

char *test_interrupted_fclose(void) {
    FILE *theHandle = NULL;   //--- don't care here
    fclose_shall_fail_on_EINTR = true;

    uninterruptible_close(theHandle);
    mu_assert("test fclose wo/ interruption", 0 == errno);
    return 0;
}

char *test_suite(void)
{
    mu_run_test(test_non_interrupted_fclose);
    mu_run_test(test_interrupted_fclose);
    return 0;
}

int main(int ac, char **av)
{
  char *result = test_suite();

  printf("number of tests run: %d\n", tests_run);
  if (result) { printf("FAIL: %s\n", result); } 

  return 0 != result;
}
于 2008-11-10T13:47:01.677 回答
1

我认为同时处理信号和确认错误条件可能存在问题。

于 2011-02-26T00:47:38.320 回答
1

嗯,我认为你们都忽略了一些非常重要的事情。

fclose() 不能返回 EINTR。fopen()、fwrite()、fread() 或任何标准 CI/O 函数也不能。

只有当您涉足诸如 open(2)、write(2) 和 select(2) 之类的低级 I/O 调用时,您才需要处理 EINTR。

阅读 [有趣的] 手册页

于 2009-07-02T01:02:36.557 回答