Что произойдет с дескриптором открытого файла в Linux, если указанный файл будет перемещен, удалите

StackOverflow https://stackoverflow.com/questions/2028874

Вопрос

Что происходит с дескриптором открытого файла в Linux, если указанный файл тем временем получает:

  • Удалено -> Остается ли дескриптор файла действительным?
  • Удалено -> Приводит ли это к EBADF, указывающему на недопустимый дескриптор файла?
  • Заменен новым файлом -> Обрабатывает ли файл, указывающий на этот новый файл?
  • Заменено жесткой ссылкой на новый файл -> Обрабатывает ли мой файл "переход" по этой ссылке?
  • Заменено программной ссылкой на новый файл -> Попадает ли мой дескриптор файла в этот файл программной ссылки сейчас?

Почему я задаю такие вопросы:Я использую оборудование с горячим подключением (например, USB-устройства и т.д.).Может случиться так, что устройство (а также его /dev/файл) будет повторно подключено пользователем или другим Gremlin.

Какова наилучшая практика решения этой проблемы?

Это было полезно?

Решение

Если файл перемещен (в той же файловой системе) или переименован, то дескриптор файла остается открытым и его все еще можно использовать для чтения и записи файла.

Если файл удален, дескриптор файла остается открытым и его все еще можно использовать (это не то, чего ожидают некоторые люди).Файл на самом деле не будет удален до тех пор, пока не будет закрыт последний дескриптор.

Если файл заменяется новым файлом, это зависит от того, как именно.Если содержимое файла будет перезаписано, дескриптор файла останется действительным и позволит получить доступ к новому содержимому.Если существующий файл разорван и создан новый с тем же именем или, если новый файл перемещен в существующий файл с помощью rename(), это то же самое, что удаление (см. выше) - то есть дескриптор файла будет продолжать ссылаться на оригинал версия файла.

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

В Unix нет удаления, только unlink(), что имеет смысл, поскольку это не обязательно удаляет файл - просто удаляет ссылку из каталога.


Если, с другой стороны, базовое устройство исчезает (напримерUSB отключите), то дескриптор файла больше не будет действительным и, скорее всего, выдаст IO / error при любой операции.Тем не менее, вам все равно придется закрыть его.Это будет верно, даже если устройство снова подключено, поскольку в этом случае нецелесообразно держать файл открытым.

Другие советы

Дескрипторы файлов указывают на индекс, а не на путь, поэтому большинство ваших сценариев по-прежнему работают так, как вы предполагаете, поскольку дескриптор по-прежнему указывает на файл.

В частности, в сценарии удаления - функция называется "unlink" не просто так, она уничтожает "ссылку" между именем файла (dentry) и файлом.Когда вы открываете файл, а затем разблокируете его, файл фактически все еще существует до тех пор, пока количество его ссылок не станет равным нулю, то есть когда вы закроете дескриптор.

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

Я не уверен насчет других операций, но что касается удаления:Удаление просто не происходит (физически, т.е.в файловой системе) до тех пор, пока не будет закрыт последний открытый дескриптор файла.Таким образом, не должно быть возможности удалить файл из вашего приложения.

Несколько приложений (которые не приходят на ум) полагаются на это поведение, создавая, открывая и немедленно удаляя файлы, которые затем живут ровно столько, сколько приложение, что позволяет другим приложениям быть в курсе жизненного цикла первого приложения без необходимости просматривать карты процессов и тому подобное.

Возможно, аналогичные соображения применимы и к другим материалам.

если вы хотите проверить, все ли в порядке с обработчиком файла (файловым дескриптором), вы можете вызвать эту функцию.

/**
 * version : 1.1
 *    date : 2015-02-05
 *    func : check if the fileDescriptor is fine.
 */

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

/**
 * On success, zero is returned.  On error, -1  is  returned,  and  errno  is  set
 *      appropriately.
 */
int check_fd_fine(int fd) {
    struct stat _stat;
    int ret = -1;
    if(!fcntl(fd, F_GETFL)) {
        if(!fstat(fd, &_stat)) {
            if(_stat.st_nlink >= 1)
                ret = 0;
            else
                printf("File was deleted!\n");
        }
    }
    if(errno != 0)
        perror("check_fd_fine");
    return ret;
}

int main() {
    int fd = -1;
    fd = open("/dev/ttyUSB1", O_RDONLY);
    if(fd < 0) {
        perror("open file fail");
        return -1;
    }
    // close or remove file(remove usb device)
//  close(fd);
    sleep(5);
    if(!check_fd_fine(fd)) {
        printf("fd okay!\n");
    } else {
        printf("fd bad!\n");
    }
    close(fd);
    return 0;
}

Информация об удаленном файле в памяти (все приведенные вами примеры являются экземплярами удаленного файла), а также индексы на диске сохраняются до тех пор, пока файл не будет закрыт.

Проблема с горячим подключением оборудования - это совершенно другая проблема, и вам не следует ожидать, что ваша программа будет работать долго, если на диске изменились индексные индексы или метаданные вообще.

Следующий эксперимент показывает, что Ответ Маркра является правильным.

код.c:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <strings.h>
#include <stdio.h>

void perror_and_exit() {
  perror(NULL);
  exit(1);
}

int main(int argc, char *argv[]) {
  int fd;
  if ((fd = open("data", O_RDONLY)) == -1) {
    perror_and_exit();
  }
  char buf[5];
  for (int i = 0; i < 5; i++) {
    bzero(buf, 5);
    if (read(fd, buf, 5) != 5) {
      perror_and_exit();
    }
    printf("line: %s", buf);
    sleep(20);
  }
  if (close(fd) != 0) {
    perror_and_exit();
  }
  return 0;
}

данные:

1234
1234
1234
1234
1234

Использование gcc code.c производить a.out.Беги ./a.out.Когда вы увидите следующий вывод:

line: 1234

Использование rm data чтобы удалить data.Но ./a.out продолжит работать без ошибок и выдаст следующий полный результат:

line: 1234
line: 1234
line: 1234
line: 1234
line: 1234

Я провел эксперимент на Ubuntu 16.04.3.

В каталоге /proc / вы найдете список всех процессов, активных в данный момент, просто найдите свой PID и все относящиеся к нему данные.Интересной информацией является папка fd /, в которой вы найдете все обработчики файлов, открытые в данный момент процессом.

В конце концов вы найдете символическую ссылку на ваше устройство (в / dev / или даже /proc/bus / usb /), если устройство зависнет, ссылка будет отключена, и обновить этот дескриптор будет невозможно, процесс должен закрыть и открыть его снова (даже при повторном подключении)

Этот код может считывать текущее состояние ссылки вашего PID

#include <unistd.h>
#include <stdio.h>
#include <dirent.h>

int main() {
    // the directory we are going to open
    DIR           *d;

    // max length of strings
    int maxpathlength=256;

    // the buffer for the full path
    char path[maxpathlength];

    // /proc/PID/fs contains the list of the open file descriptors among the respective filenames
    sprintf(path,"/proc/%i/fd/",getpid() );

    printf("List of %s:\n",path);

    struct dirent *dir;
    d = opendir(path);
    if (d) {
        //loop for each file inside d
        while ((dir = readdir(d)) != NULL) {

            //let's check if it is a symbolic link
            if (dir->d_type == DT_LNK) {

                const int maxlength = 256;

                //string returned by readlink()
                char hardfile[maxlength];

                //string length returned by readlink()
                int len;

                //tempath will contain the current filename among the fullpath
                char tempath[maxlength];

                sprintf(tempath,"%s%s",path,dir->d_name);
                if ((len=readlink(tempath,hardfile,maxlength-1))!=-1) {
                    hardfile[len]='\0';
                        printf("%s -> %s\n", dir->d_name,hardfile);

                } else
                    printf("error when executing readlink() on %s\n",tempath);

            }
        }

        closedir(d);
    }
    return 0;
}

Этот окончательный код прост, вы можете поиграть с функцией linkat.

int
open_dir(char * path)
{
  int fd;

  path = strdup(path);
  *strrchr(path, '/') = '\0';
  fd = open(path, O_RDONLY | O_DIRECTORY);
  free(path);

  return fd;
}

int
main(int argc, char * argv[])
{
  int odir, ndir;
  char * ofile, * nfile;
  int status;

  if (argc != 3)
    return 1;

  odir = open_dir(argv[1]);
  ofile = strrchr(argv[1], '/') + 1;

  ndir = open_dir(argv[2]);
  nfile = strrchr(argv[2], '/') + 1;

  status = linkat(odir, ofile, ndir, nfile, AT_SYMLINK_FOLLOW);
if (status) {
  perror("linkat failed");
}


  return 0;
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top