简体   繁体   中英

How to check if a file is already opened in C

I am working on a multithreaded system where a file can be shared among different threads based on the file access permissions.

How can I check if file is already opened by another thread?

To find out if a named file is already opened on , you can scan the /proc/self/fd directory to see if the file is associated with a file descriptor. The program below sketches out a solution:

DIR *d = opendir("/proc/self/fd");
if (d) {
    struct dirent *entry;
    struct dirent *result;

    entry = malloc(sizeof(struct dirent) + NAME_MAX + 1);
    result = 0;
    while (readdir_r(d, entry, &result) == 0) {
        if (result == 0) break;
        if (isdigit(result->d_name[0])) {
            char path[NAME_MAX+1];
            char buf[NAME_MAX+1];
            snprintf(path, sizeof(path), "/proc/self/fd/%s",
                     result->d_name);
            ssize_t bytes = readlink(path, buf, sizeof(buf));
            buf[bytes] = '\0';
            if (strcmp(file_of_interest, buf) == 0) break;
        }
    }
    free(entry);
    closedir(d);
    if (result) return FILE_IS_FOUND;
}
return FILE_IS_NOT_FOUND;

From your comment, it seems what you want to do is to retrieve an existing FILE * if one has already been created by a previous call to fopen() on the file. There is no mechanism provided by the standard C library to iterate through all currently opened FILE * . If there was such a mechanism, you could derive its file descriptor with fileno() , and then query /proc/self/fd/# with readlink() as shown above.

This means you will need to use a data structure to manage your open FILE * s. Probably a hash table using the file name as the key would be the most useful for you.

You can use int flock(int fd, int operation); to mark a file as locked and also to check if it is locked.

   Apply or remove an advisory lock on the open file specified by fd.
   The argument operation is one of the following:

       LOCK_SH  Place a shared lock.  More than one process may hold a
                shared lock for a given file at a given time.

       LOCK_EX  Place an exclusive lock.  Only one process may hold an
                exclusive lock for a given file at a given time.

       LOCK_UN  Remove an existing lock held by this process.

flock should work in a threaded app if you open the file separately in each thread: multiple threads able to get flock at the same time

There's more information about flock and it's potential weaknesses here .

如果您倾向于在 shell 中执行此操作,则可以简单地使用lsof $filename

I don't know much in the way of multithreading on Windows, but you have a lot of options if you're on Linux. Here is a FANTASTIC resource. You might also take advantage of any file-locking features offered inherently or explicitly by the OS (ex: fcntl ). More on Linux locks here . Creating and manually managing your own mutexes offers you more flexibility than you would otherwise have. user814064 's comment about flock() looks like the perfect solution, but it never hurts to have options!

Added a code example:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

FILE *fp;
int counter;
pthread_mutex_t fmutex = PTHREAD_MUTEX_INITIALIZER;

void *foo() {
        // pthread_mutex_trylock() checks if the mutex is
        // locked without blocking
        //int busy = pthread_mutex_trylock(&fmutex);

        // this blocks until the lock is released
        pthread_mutex_lock(&fmutex);
        fprintf(fp, "counter = %d\n", counter);
        printf("counter = %d\n", counter);
        counter++;
        pthread_mutex_unlock(&fmutex);
}

int main() {

        counter = 0;
        fp = fopen("threads.txt", "w");

        pthread_t thread1, thread2;

        if (pthread_create(&thread1, NULL, &foo, NULL))
                printf("Error creating thread 1");
        if (pthread_create(&thread2, NULL, &foo, NULL))
                printf("Error creating thread 2");

        pthread_join(thread1, NULL);
        pthread_join(thread2, NULL);

        fclose(fp);
        return 0;
}

If you need to determine whether another thread opened a file instead of knowing that a file was already opened, you're probably doing it the wrong way.

In a multithreaded application, you want to manage resources used in common in a list accessible by all the threads. That list needs to be managed in a multithread safe manner. This just means you need to lock a mutex , do things with the list, then unlock the mutex . Further, reading/writing to the files by more than one thread can be very complicated. Again, you need locking to do that safely. In most cases, it's much easier to mark the file as "busy" (aka a thread is using that file) and wait for the file to be "ready" (aka no thread is using it).

So assuming you have a form of linked list implementation, you can have a search of the list in a way similar to:

my_file *my_file_find(const char *filename)
{
    my_file *l, *result = NULL;

    pthread_mutex_lock(&fmutex);

    l = my_list_of_files;

    while(l != NULL)
    {
         if(strcmp(l->filename, filename) == 0)
         {
             result = l;
             break;
         }
         l = l->next;
    }

    pthread_mutex_unlock(&fmutex);

    return result;
}

If the function returns NULL , then no other threads had the file open while searching (since the mutex was unlocked, another thread could have opened the file before the function executed the return ). If you need to open the file in a safe manner (ie only one thread can open file filename ) then you need to have a my_file_open() function which locks, searches, adds a new my_file if not found, then return that new added my_file pointer. If the file already exists, then the my_file_open() probably returns NULL meaning that it could not open a file which that one thread can use (ie another thread is already using it).

Just remember that you can't unlock the mutex between the search and the add. So you can't use the my_file_find() function above without first getting a lock on your mutex (in which case you probably want to have recursive mutexes).

In other words, you can search the exiting list, grow the existing list, and shrink (aka close a file) only if you first lock the mutex, do ALL THE WORK, then unlock the mutex.

This is valid for any kind of resources, not just files. It could be memory buffers, a graphical interface widget, a USB port, etc.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM