【问题标题】:Check if a file that has been opened with fopen has been closed检查已经用 fopen 打开的文件是否已经关闭
【发布时间】:2013-07-30 07:51:35
【问题描述】:
gcc (GCC) 4.7.2
c89

是否可以检查文件是否已经关闭?

我已使用fopen() 打开一个文件并使用fclose(fd) 关闭。

此文件在程序运行期间被打开和关闭。但是,用户可以通过ctrl-c 来终止程序。

当我执行清理例程时,我不知道文件是处于打开状态还是关闭状态。因此,如果我尝试执行 fclose(fd) 两次,它将堆栈转储。

我一直在抛出的一些想法:

  1. 为文件添加一个状态以打开或关闭,然后检查该状态,这意味着对于这样一个简单的工作需要更多的工作。
  2. 不要做任何事情,因为操作系统会在程序结束时自动清理。
  3. 是否有访问功能,但这只会检查模式。

非常感谢,

【问题讨论】:

  • 添加状态(第一个选项)会添加竞争条件,除非您在状态与文件同步时屏蔽 Ctrl+C 信号。
  • 关闭后为什么不能将fd (strange name, btw) 设置为NULL
  • 它与 libc(通常是 Glibc,但也可能是 musl-libc 等)的关系比与编译器的关系更大。
  • 有一个状态变量#defined 为OPENCLOSE。每当您在代码中调用 fopen 时,将状态设置为 Current_State = OPEN;,当您在代码中调用 fclose 时,将状态设置为 Current_State = CLOSE; 并在需要时使用该 current_state 变量。

标签: c fopen fclose


【解决方案1】:

AFAIK,glibc 不提供验证FILE* 变量的方法。

保持某种状态或只是将fd 设置为NULL 是可行的,但您必须注意不要在关闭文件后、重置fd 之前进入清理例程。当 Ctrl+C 向程序发送信号 (SIGINT) 时,您可以在关闭文件和重置 fd 时阻止信号。因此,您在主程序中的fclose 应如下所示:

// 1. Block SIGINT
sigset_t old_mask, to_block;
sigemptyset(&to_block);
sigaddset(&to_block, SIGINT);
sigprocmask(SIG_BLOCK, &to_block, &old_mask);

// 2. Close the file and reset fd
fclose(fd);
fd = NULL;

// 3. Restore signal handling
sigprocmask(SIG_SETMASK, &old_mask, NULL);

在你的清理程序中,你应该检查fd

if (fd != NULL) fclose(fd);

如果你的程序是多线程的,你应该改用pthread_sigmask

在您的情况下,在清理例程中简单调用 fcloseall() 会容易得多。

至于您问题中的第二个选项,即什么都不做 - 好吧,操作系统会为您的程序清理所有内容。那么唯一的缺点是,如果有打开的写入流,一些数据可能不会写入磁盘。但也许在 Ctrl+C 的情况下就可以了。

【讨论】:

  • 什么是 AFAIK 顺便说一句?
  • 据我所知,AFAIK 是“据我所知”的缩写 :)
【解决方案2】:

我觉得你脑子里有点混乱。

fopenfclosef... 函数一起使用的文件处理程序是进程范围。即,它们在应用程序中有效(通常)。

打开文件时可能会发生两件事:成功与否。如果成功,您可能正在独占使用文件或与其他进程共享。如果你失败了,可能另一个进程已经在使用 file.搜索_fsopen

当进程结束时,正常或被中断(如Ctrl+C),操作系统迟早会释放与进程相关的资源。这适用于文件处理程序、内存、...

关于您的问题,无论您的应用程序正常结束还是以Ctrl+C 结束,操作系统都会释放未关闭的文件处理程序。

每当您的应用程序启动时,您都需要打开所需的文件处理程序。操作系统不会让它们为您打开。

【讨论】:

    【解决方案3】:

    我在不同的情况下遇到了类似的问题(没有竞争条件)。所以这个解决方案既涵盖了标题中的问题,也涵盖了 ant 提出的问题。

    当调用fclose(fd) 时,内部fd->_fileno 低级I/O 文件描述符设置为-1(或其他无效的unix 文件描述符)。所以可以使用fileno(fd)>=0 来检查fd 是否仍然是一个有效的流(我原来的问题)。

    当按下CTRL-C 时,进程接收到一个引用为SIGINT 的信号并执行相应的信号处理程序(例如预定义函数)。永远不要尝试在信号处理程序中做任何涉及程序状态的事情(除了原子可设置变量,例如标志),因为信号可能在任何情况下到达,所以即使fclose(fd) 在 glibc 中执行(所以fd->_fileno在处理程序执行期间未定义)。因此,这里的规范方法是设置一个触发关闭和返回的标志。然后,主应用程序需要经常检查此标志,如果设置了,则清理并退出。

    该程序在按下CTRL-C 后结束,不会导致系统清理任何 fd。

    #include <stdio.h>
    #include <signal.h>
    #include <stdlib.h>
    
    char interrupted=0;
    
    void exit_handler(int sig){
        interrupted=1;
    }
    
    int main(char** argc, int argv)
    {
        FILE *fd;
    
        signal(SIGINT, exit_handler);
        signal(SIGTERM, exit_handler);
    
        while(1){
            fd=fopen("filepath.file","w");
            char *tst_str="Test String";
            if(fwrite(tst_str,1,11,fd)!=11)
                printf("Writing Error occured\n");
    
            if (fileno(fd)>=0){
                printf("FD Closed. (1)\n");
                fclose(fd);
            }
            if (fileno(fd)>=0){
                printf("FD Closed. (2)\n");
                fclose(fd);
            }
            if(interrupted)
                exit(0);
        }
        printf("Done.");
    }
    

    【讨论】:

      【解决方案4】:

      也许对你有帮助,只是一些建议:

      您可以使用进程 ID 检查您的进程打开的文件。

      1. pid_t getpid(void);,返回调用进程的进程ID。
      2. 接下来将此 PID 传递给 pfiles 命令。

      第二个:

      您可以在程序终止前调用int fcloseall (void);

      这个函数会导致进程的所有打开的流被关闭并且 与要断开的相应文件的连接。所有缓冲数据 被写入并且任何缓冲的输入都被丢弃。 fcloseall 函数 如果所有文件都成功关闭,则返回值 0,并且 如果检测到错误,则为 EOF。

      【讨论】:

      • 在 Linux 上,您可以从进程内部使用(并以编程方式检查)/proc/self/fd//proc/self/fdinfo/ 目录,或者从外部使用 /proc/$PID/fd/ 等。 /proc/pfiles 访问
      • @BasileStarynkevitch 我是否在手册中阅读过pfiles command read proc-filesystem
      【解决方案5】:
      int sig_int(int signo)
      {
          if(fdOpen)
             fclose(fd);
          abort();
      }
      

      然后将此添加到main:

      static volatile fdOpen = 0;
      static FILE *fd;//I guess
      if(signal(SIGINT,sig_int) == SIG_ERR)
          printf("signal(SIGINT) error\n");
      
      if((fd =fopen("your_file_name","flag_you_need")) != NULL)
          fdOpen = 1;
      

      在 fcolse 之前执行此操作:

      sigset_t newmask,oldmask;
      sigemptyset(&newmask);
      sigaddset(&newmask, SIGINT);
      sigprocmask(SIG_BLOCK, &newmask, &oldmask);//BLOCK SIGINT
      

      并在正常关闭文件后设置fdOpen = 0 ;//SIGINT will not interrupt us now.

      然后

      sigprocmask(SIG_SETMASK,&oldmask,NULL);//RESET signal mask
      

      所以我们做了一些干净的工作,然后在sig_int 退出程序。

      【讨论】:

      • 这里唯一的问题是当 sig_int() 在正常的 fclose 之后、fdOpen = 0; 之前被调用。
      猜你喜欢
      • 1970-01-01
      • 2019-04-29
      • 2012-08-30
      • 1970-01-01
      • 1970-01-01
      • 2011-07-28
      • 1970-01-01
      • 2017-12-01
      相关资源
      最近更新 更多