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