質問

gcc (GCC) 4.7.2
c89

Is it possible for check if a file has already closed?

I have opened a file using fopen() and closed using fclose(fd).

This file gets opened and closed during the running of the program. However, the user can terminate the program by doing a ctrl-c.

When I go to my cleanup routine, I don't know if the file is in a open or closed state. So if I try and do a fclose(fd) twice, it will stack dump.

Some ideas I have been throwing around:

  1. Add a state to the file to be either opened or closed, and then check that state, means more work for such a simple job.
  2. Don't do anything as the OS will cleanup automatially when the program ends.
  3. Is there is the access function, but that will just checks the mode.

Many thanks in advance,

役に立ちましたか?

解決

AFAIK, glibc doesn't provide a method to validate a FILE* variable.

Keeping some kind of state or just setting fd to NULL works, but you have to be careful not to get into your cleanup routine just after closing the file, but before resetting fd. As Ctrl+C sends a signal (SIGINT) to a program, you can just block the signal while the file is being closed and fd is being reset. So, your fclose in the main program should look like:

// 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);

And in your clean-up routine you should just check fd:

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

If your program is multithreaded, you should use pthread_sigmask instead.

In your case, simple call of fcloseall() in the cleanup routine would be much easier.

As for the second option meant in your question, about doing nothing - well, the OS will clean-up everything for your program. Then only drawback is that if there were opened write streams, some data may be not written to disk. But maybe in case of Ctrl+C it's just ok.

他のヒント

It seams to me that there is a little confusion in your mind.

File handlers, used with fopen, fclose, f... functions are process scope. I.e., they are valid within application (normally).

Two things can happen when you open a file: you succeed or not. If you succeed, you may be using file exclusively or shared with other processes. If you fail, maybe another process is already using file. Search _fsopen.

When process ends, normally or interrupted (as with Ctrl+C), OS releases resources associated with process, sooner or later. This applies to file handlers, memory, ...

Regarding your question, whether your application end normally or with Ctrl+C, unclosed file handlers will be released by OS.

Whenever your application starts, you need to open file handlers you need. OS will not keep them open for you.

I got a similar problem in a different situation (no race condition). So this solution covers both the question in the title and the one asked by ant.

When fclose(fd) is called the internal fd->_fileno low level I/O-file descriptor is set to -1 (or an other invalid unix file descriptor). So one could check using fileno(fd)>=0 to verify if fd is still a valid stream (my original problem).

When CTRL-C is pressed the process receives a signal refereed as SIGINT and executes the corresponding signal handler (e.g. a predefined function). One should never try to do anything involving the program state (except for atomic settable variables, e.g. flags) in a signal handler, because the signal might arrive in any situation, so even while fclose(fd) is executed in glibc (so fd->_fileno is undefined during the execution of the handler). Because of this the canonical way here is to set a flag triggering the shutdown and return. The main application then needs to check this flag sufficiently often, and, if it is set, cleans up and exit.

This program finishes after CTRL-C is pressed, without leading to any fd cleaned up by the system.

#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.");
}

May be it help you, just some suggestions:

You can check file open by your process using process id.

  1. pid_t getpid(void);, return the process ID of the calling process.
  2. Next pass this PID to pfiles command.

Second:

You can call int fcloseall (void); before program terminates.

This function causes all open streams of the process to be closed and the connection to corresponding files to be broken. All buffered data is written and any buffered input is discarded. The fcloseall function returns a value of 0 if all the files were closed successfully, and EOF if an error was detected.

int sig_int(int signo)
{
    if(fdOpen)
       fclose(fd);
    abort();
}

Then add this to 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;

Do this before fcolse:

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

And set fdOpen = 0 ;after you close file normally//SIGINT will not interrupt us now.

Then

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

So we do some clean jobs and then exit the program at sig_int.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top