Как не открыть файл дважды в linux?



У меня есть связанный список с fd и строкой, которую я использовал, чтобы открыть этот файл в каждой записи. Я хочу открывать и добавлять файлы в этот список, только если этот файл еще не открыт, потому что я открываю и анализирую эти файлы и не хочу делать это дважды. Моя идея состояла в том, чтобы сравнить имя файла с каждым именем в этом списке, но моя программа делает это несколько раз, и один файл в Linux может иметь несколько имен (мягкие/жесткие ссылки). Я думаю, что это не должно быть так сложно, потому что его легко для операционной системы, чтобы проверить, использовал ли я уже индекс или нет, r?
Я уже пробовал open один и тот же файл с flock и без flock, но я всегда получаю новый fd.

629   3  

3 ответов:

При успешном открытии файла используйте fstat по делу. Проверьте, чтобы увидеть, если st_ino и st_dev из struct stat поданные в fstat уже были записаны в вашем связанном списке. Если да, то закройте файловый дескриптор и перейдите к следующему файлу. В противном случае добавьте в список дескриптор файла, имя файла и значения st_ino и st_dev.

Вы можете вместо этого использовать stat проверить перед открытием файла, но используя fstat после будет немного быстрее, если обычно дело в том, что файл еще не был открыт.

В подобных ситуациях часто полезно рассмотреть свои структуры данных. Измените структуру данных, которая не допускает дублирования, например хэш-таблицу.

Сохраняйте набор данных, которые вы видели раньше. Я использовалхэш-таблицу для этого набора. В соответствии с ответом @RossRidge , Используйте индекс и устройство в качестве ключа. Это позволяет обнаруживать дубликаты за O (1) Время.

Вот пример реализации.

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <glib.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>

static int get_fd(GHashTable *fds, const char *filename, int mode) {
    int fd;
    struct stat stat;
    int keysize = 33;
    char key[keysize];  /* Two 64 bit numbers as hex and a separator */

    /* Resolve any symlinks */
    char *real_filename = realpath(filename, NULL);
    if( real_filename == NULL ) {
        printf("%s could not be resolved.\n", filename);
        return -1;
    }

    /* Open and stat */
    fd = open( real_filename, mode );
    if( fd < 0 ) {
        printf("Could not open %s: %s.\n", real_filename, strerror(errno));
        return -1;
    }
    if( fstat(fd, &stat) != 0 ) {
        printf("Could not stat %s: %s.\n", real_filename, strerror(errno));
        return -1;
    }

    /* Make a key for tracking which data we've processed.
       This uses both the inode and the device it's on.
       It could be done more efficiently as a bit field.
     */
    snprintf(key, keysize, "%lx|%lx", (long int)stat.st_ino, (long int)stat.st_dev);

    /*  See if we've already processed that */
    if( g_hash_table_contains(fds, key) ) {
        return 0;
    }
    else {
        /* Note that we've processed it */
        g_hash_table_add(fds, key);
        return fd;
    }
}


int main(int argc, char** argv) {
    int mode = O_RDONLY;
    int fd;
    GHashTable *fds = g_hash_table_new(&g_str_hash, &g_str_equal);

    for(int i = 1; i < argc; i++) {
        char *filename = argv[i];

        fd = get_fd(fds, filename, mode);
        if( fd == 0 ) {
            printf("%s has already been processed.\n", filename);
        }
        else if( fd < 0 ) {
            printf("%s could not be processed.\n", filename);
        }
        else {
            printf("%s: %d\n", filename, fd);
        }
    }
}

А вот пример результат.

$ touch one two three
$ ln one one_link
$ ln -s two two_sym
$ ./test one* two* three*
one: 3
one_link has already been processed.
two: 5
two_sym has already been processed.
three: 7

До тех пор, пока вы не закроете успешно и намеренно открытые файлы, вы можете использовать nonblocking flock для предотвращения другой блокировки на том же файле:

#include <unistd.h>
#include <sys/file.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <assert.h>

int openAndLock(const char* fn){
  int fd = -1;
  if(((fd = open(fn, O_RDONLY)) >= 0) && (flock(fd, LOCK_EX|LOCK_NB) == 0)){
      fprintf(stderr, "Successfully opened and locked %s\n", fn);
      return fd;
  }else{
    fprintf(stderr, "Failed to open or lock %s\n", fn);
    close(fd);
    return -1;
  }
}

int main(int argc, char** argv){
  for(int i=1; i<argc; i++){
    openAndLock(argv[i]);
  }
  return 0;
}

Пример:

$ touch foo
$ ln foo bar
$ ./a.out foo foo
Successfully opened and locked foo
Failed to open or lock foo
$ ./a.out foo bar
Successfully opened and locked foo
Failed to open or lock bar

Comments

    Ничего не найдено.