简而言之,如何在系统调用中对EINTR等错误情况进行单元测试。
我正在研究的一个特殊例子,它本身就是一个案例,就是当它返回带有(errno == EINTR)的EOF时是否有必要再次调用fclose。行为取决于fclose的实现:
// Given an open FILE *fp
while (fclose(fp)==EOF && errno==EINTR) {
errno = 0;
}
如果在发生EINTR时释放fp,则此调用可能不安全。如何测试何时(errno == EINTR)的错误处理?
答案 0 :(得分:4)
在这种特殊情况下,再次调用fclose()是不安全的,因为C标准表示即使调用失败,流也会从文件中解除关联(并变得不确定)。
答案 1 :(得分:3)
使用函数指针结构来抽象出系统函数。将调用替换为flcose(fp),类似于sys-> fclose(fp)。在单元测试中,创建一个fclose实现,它始终返回EINTR,然后将sys-> fclose设置为该版本。
答案 2 :(得分:3)
查看linux内核源代码,我无法找到任何在文件关闭时返回EINTR的驱动程序。
如果你absosmurfly必须重现这种情况,你可以在Linux中编写自己的驱动程序,在.release方法上返回-EINTR。看看O'Reilly的Linux设备驱动程序书中的example code。 sc项目是最简单的项目之一。你会把它改成这样:
int scull_release(struct inode *inode, struct file *filp)
{
return -EINTR;
}
再说一次,通过浏览linux源代码树,我找不到任何会在关闭时返回EINTR的驱动程序。
编辑 - 好吧它看起来像保险丝 - 用户空间文件系统可能能够。这用于sshfs之类的东西。但仍然是一个优势。
答案 3 :(得分:2)
我认为没有一种简单的方法可以随意进行实际测试。
如果fclose操作被信号中断,将生成EINTR。 这意味着fclose在后台线程中,它在处理关闭请求时接收到信号。
祝你好运,重现那种情况。
答案 4 :(得分:1)
正如Kris建议的那样,暂时用您自己在单元测试代码中定义的实现来替换对“真实”fclose()
的调用。您的实现可以做一些简单的事情:
int my_fclose(FILE *fp)
{
errno = EINTR;
return EOF;
}
但是,正如fizzer指出的那样,你不应该再次调用fclose()
,因为行为是未定义的,所以你甚至不必费心去检查条件。
要问的另一个问题是你是否真的需要担心这个问题;如果您的应用程序代码在fclose()
期间阻止了所有可能的信号(SIGKILL和SIGSTOP除外),那么您将无法获得EINTR,也不需要担心。
答案 5 :(得分:1)
以下是我测试它的方法,仅仅是为了测试,因为fizzer提醒我们两次调用fclose()
是不安全的。
您可以使用自己的行为 重新定义 fclose()
(或libc的任何其他功能)。在类Unix系统上,链接器不会抱怨 - 从未尝试在Windows上使用cygwin。当然,这会阻止您的其他测试使用真正的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;
}
答案 6 :(得分:1)
fclose()无法返回EINTR。 fopen(),fwrite(),fread()或任何标准C I / O函数都不能。
只有当您涉及低级I / O调用时,例如open(2),write(2)和select(2)才需要处理EINTR。
阅读[有趣]手册页
答案 7 :(得分:1)
我认为同时处理信号和确认错误情况可能存在问题。