【问题标题】:How to properly open and close an already fopened gzip file with zlib?如何使用 zlib 正确打开和关闭已打开的 gzip 文件?
【发布时间】:2021-01-13 15:06:11
【问题描述】:

我正在编写一个小型 C 库,用于读取作为 FILE * 传递的 gzip 文件。我正在使用zlib的gzdopen()打开文件文件描述符:

int zOpenCloseTest(FILE *const plainFile) {
    gzFile file = gzdopen(fileno(plainFile), "rb");
    if(file == NULL) {
        goto error;
    }
    if(gzclose_r(file) != Z_OK) {
        goto error;
    }
    return 0;
    error:
    // gzdopen does not close fd if it fails
    fclose(plainFile);
    return -1;
}

int main() {
    FILE *const file = fopen("test.xp", "rb");
    if(file == NULL) {
        return -1;
    }
    if(zOpenCloseTest(file) < 0) {
        return -2;
    }
    return 0;
}

zlib manual 声明:

文件描述符是从opendupcreatpipefileno 等调用中获得的(在该文件之前已使用fopen 打开)。 [...] 对返回的 gzFile 的下一次调用 gzclose 也将关闭文件描述符 fd,就像 fclose(fdopen(fd), mode) 关闭文件描述符 fd 一样。如果您想保持fd 处于打开状态,请使用fd = dup(fd_keep); gz = gzdopen(fd, mode);。 [...] 如果您使用fileno()FILE * 获取文件描述符,那么您将不得不使用dup() 以避免双重close()ing 文件描述符。 gzclose()fclose() 都会关闭关联的文件描述符,所以它们需要有不同的文件描述符。

我不想让文件保持打开状态,所以我不创建重复项。

当使用 Valgrind memcheck (--leak-check=full --show-leak-kinds=all --track-origins=yes --vgdb=no --track-fds=yes) 检查时,上述代码会在第 21 行 (fopen) 上创建 Leak_StillReachable 错误。
在 Windows 上,它会因“调试断言失败!”而崩溃。 (_osfile(fh) &amp; FOPEN) close.cpp line 49 错误框。

两者都是在“调试模式”下构建的。

该库是更大的 CMake 项目的一部分,用户可以选择将其构建为静态库还是共享库。无论哪种情况,我都希望 zlib 静态链接到库。 我最初认为崩溃是由于库和 zlib 之间的运行时库链接模式不匹配,但最小的示例表明情况并非如此? 如果您检查 CMake 输出,您可以看到 zlib 正在使用 /MDd 标志构建,应该是这样。 (现代)CMake 通常默认为 CMAKE_MSVC_RUNTIME_LIBRARY 的“MultiThreaded$DLL”。

我相信我忘记了关于文件描述符流关系如何工作的重要内容。 code is available on GitHub。该项目使用 Hunter 以 CMake 友好的方式自动设置 zlib。 来源是跨平台的,mre 提供了安静 msvc 警告的定义。

【问题讨论】:

  • 我猜潜在的问题是gzclose 关闭了文件描述符,但对它上面的 stdio 流一无所知。这类似于在 stdio 流上执行 close(fileno(fp));。你也许可以这样做:fd = dup(fileno(plainFile));fclose(plainFile);file = gzdopen(fd);if (gzclose_r(file) != Z_OK) {close(fd);return -1;}。 MS CRT 可能希望您改用 _dup_close
  • 问题是 zlib man 明确声明“在返回的gzFile 上的下一次调用gzclose关闭文件描述符fd,就像@ 987654365@。” fclose 接受 FILE* 并关闭整个事物,而不仅仅是描述符。我还强调了看起来它建议不要在 gzclose 之后调用 fclose 的部分。关于 mscrt 警告,我将添加一条注释。
  • 谢谢!你和 Mark Adler 让我意识到我真的需要修改我对 io 的了解。在结束这个问题之前,我借此机会问一下,你为什么在gzclose_r 的错误处理中加入了close(fd)gzclose 失败可以让描述符保持打开状态吗?这样您就可以一步处理这两个错误,因为如果gzdopen 失败,那么保证gzclose 也会失败?
  • 看起来gzclose 总是在文件描述符上调用close,除非它返回Z_STREAM_ERROR。如果返回值为Z_ERRNOclose返回错误,

标签: c valgrind zlib c99 msvcrt


【解决方案1】:

您没有按照 zlib 文档所说的去做,您自己在问题中强调了这一点。使用dup()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-02-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-28
    • 2012-02-03
    • 1970-01-01
    相关资源
    最近更新 更多