file - Qu'arrive-t-il à un descripteur de fichier ouvert sur Linux si le fichier pointé est déplacé, supprimer




file-io linux-kernel systems-programming (7)

Les informations en mémoire d'un fichier supprimé (tous les exemples que vous donnez sont des instances d'un fichier supprimé) ainsi que les inodes sur disque restent en existence jusqu'à la fermeture du fichier.

Le matériel en cours de branchement à chaud est un problème complètement différent, et vous ne devriez pas attendre que votre programme reste actif longtemps si les inodes ou les métadonnées sur disque ont changé du tout .

Qu'advient-il d'un descripteur de fichier ouvert sur Linux si le fichier pointé entre-temps obtient:

  • Moved away -> Le gestionnaire de fichier reste-t-il valide?
  • Deleted -> Est-ce que cela conduit à un EBADF, indiquant un handle de fichier invalide?
  • Remplacé par un nouveau fichier -> Le fichier gère-t-il ce nouveau fichier?
  • Remplacé par un lien vers un nouveau fichier -> Est-ce que mon fichier gère "suivre" ce lien?
  • Remplacé par un lien logiciel vers un nouveau fichier -> Est-ce que mon identificateur de fichier rencontre maintenant ce fichier de lien logiciel?

Pourquoi je pose ces questions: j'utilise du matériel hot-plug (comme des périphériques USB, etc.). Il peut arriver que l'appareil (et aussi son / dev / file) soit rattaché par l'utilisateur ou un autre Gremlin.

Quelle est la meilleure pratique face à cela?


Si le fichier est déplacé (dans le même système de fichiers) ou renommé, le handle de fichier reste ouvert et peut toujours être utilisé pour lire et écrire le fichier.

Si le fichier est supprimé, le handle de fichier reste ouvert et peut toujours être utilisé (ce n'est pas ce que certaines personnes attendent). Le fichier ne sera pas vraiment supprimé tant que le dernier handle n'est pas fermé.

Si le fichier est remplacé par un nouveau fichier, cela dépend exactement comment. Si le contenu du fichier est remplacé, le handle de fichier sera toujours valide et accédera au nouveau contenu. Si le fichier existant est dissocié et qu'un nouveau fichier est créé avec le même nom ou, si un nouveau fichier est déplacé sur le fichier existant en rename() , c'est la même chose que la suppression (voir ci-dessus). pour se référer à la version originale du fichier.

En général, une fois le fichier ouvert, le fichier est ouvert, et personne ne modifiant la structure du répertoire peut changer cela - ils peuvent se déplacer, renommer le fichier ou mettre quelque chose d'autre à sa place, il reste simplement ouvert.

Dans Unix, il n'y a pas de suppression, seulement unlink() , ce qui est logique car il ne supprime pas nécessairement le fichier - supprime simplement le lien du répertoire.

Si par contre le périphérique sous-jacent disparaît (par exemple USB débrancher) alors le handle de fichier ne sera plus valide et risque de provoquer des E / S sur n'importe quelle opération. Vous devez toujours le fermer cependant. Cela va être vrai même si l'appareil est branché, car il n'est pas judicieux de garder un fichier ouvert dans ce cas.


L'expérience suivante montre que la réponse de MarkR est correcte.

code.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;
}

Les données:

1234
1234
1234
1234
1234

Utilisez gcc code.c pour produire a.out . Exécutez ./a.out . Lorsque vous voyez la sortie suivante:

line: 1234

Utilisez les rm data pour supprimer des data . Mais ./a.out continuera à fonctionner sans erreurs et produira la sortie entière suivante:

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

J'ai fait l'expérience sur Ubuntu 16.04.3.


Je ne suis pas sûr des autres opérations, mais de la suppression: La suppression n'a tout simplement pas lieu (physiquement, c'est-à-dire dans le système de fichiers) jusqu'à ce que le dernier descripteur ouvert du fichier soit fermé. Ainsi, il ne devrait pas être possible de supprimer un fichier sous votre application.

Quelques applications (qui ne me viennent pas à l'esprit) s'appuient sur ce comportement, en créant, ouvrant et supprimant immédiatement des fichiers, qui vivent ensuite aussi longtemps que l'application - permettant aux autres applications de connaître le cycle de vie de la première application sans avoir à Regardez les cartes de processus et autres.

Il est possible que des considérations similaires s'appliquent aux autres choses.


Les handles de fichiers pointent sur un inode et non sur un chemin, donc la plupart de vos scénarios fonctionnent toujours comme vous le supposez, puisque le handle pointe toujours vers le fichier.

Plus précisément, avec le scénario de suppression - la fonction est appelée "unlink" pour une raison, il détruit un "lien" entre un nom de fichier (un dentry) et un fichier. Lorsque vous ouvrez un fichier, puis le dissocier, le fichier existe toujours jusqu'à ce que son nombre de références passe à zéro, ce qui est lorsque vous fermez le handle.

Edit: Dans le cas du matériel, vous avez ouvert un handle vers un nœud de périphérique spécifique, si vous débranchez le périphérique, le noyau échouera tous les accès, même si le périphérique revient. Vous devrez fermer l'appareil et le rouvrir.


Sous le répertoire / proc / vous trouverez une liste de tous les processus actuellement actifs, il suffit de trouver votre PID et toutes les données qui s'y trouvent. Une information interressante est le dossier fd /, vous trouverez tous les gestionnaires de fichiers actuellement ouverts par le processus.

Finalement, vous trouverez un lien symbolique vers votre périphérique (sous / dev / ou même / proc / bus / usb /), si le périphérique se bloque, le lien sera mort et il sera impossible d'actualiser ce handle, le processus doit se fermer et l'ouvrir à nouveau (même avec la reconnexion)

Ce code peut lire l'état actuel de votre lien 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;
}

Ce code final est simple, vous pouvez jouer avec la fonction 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;
}

Essentiellement, cela signifie que vous avez un problème de mémoire. Plus que probable c'est une fuite.

Redémarrez d'abord votre téléphone et vérifiez que l'erreur persiste. Si c'est le cas, vous devrez commencer à creuser dans votre code et découvrir où vous fuyez!

Besoin d'aide? Publiez du code!







linux file file-io linux-kernel systems-programming