264 votes

Que fait ouvrir un fichier en fait ?

Dans tous les langages de programmation (que j'utilise au moins), vous devez ouvrir un fichier avant de vous pouvez lire ou écrire.

Mais que fait cette opération d'ouverture?

Les pages de manuel pour les fonctions typiques de ne pas réellement vous dire autre chose que cela "ouvre un fichier en lecture/écriture":

http://www.cplusplus.com/reference/cstdio/fopen/

https://docs.python.org/2/library/functions.html#open

Évidemment, grâce à l'utilisation de la fonction que vous pouvez dire qu'il implique la création d'une sorte d'objet qui facilite l'accès à un fichier.

Une autre façon de mettre en place ce serait, si je devais mettre en œuvre un open fonction, quel serait-il besoin de faire sur Linux?

184voto

Blagovest Buyukliev Points 22767

Dans presque chaque langage de haut niveau, la fonction qui ouvre un fichier est un wrapper autour de la correspondante du noyau du système d'appel. Il peut faire d'autres trucs de fantaisie, mais contemporain des systèmes d'exploitation, l'ouverture d'un fichier doit toujours passer par le noyau.

C'est pourquoi les arguments de l' fopen fonction de la bibliothèque, ou Python open ressemblent les arguments de l' open(2) appel système.

En plus de l'ouverture du fichier, ces fonctions généralement mis en place une mémoire tampon qui sera donc utilisé avec les opérations de lecture/écriture. Le but de cette mémoire tampon est de s'assurer que chaque fois que vous voulez lire N octets, l'appel de la bibliothèque sera de retour N octets, indépendamment du fait que les appels au système sous-jacent appels de retour de moins en moins.

Je ne suis pas réellement intéressé à mettre en place mon propre fonction, juste à comprendre ce que l'enfer qui se passe... "au-delà de la langue" si vous le souhaitez.

Dans les systèmes de type Unix, un appel réussi à open renvoie un descripteur de fichier" qui est simplement un entier dans le contexte de l'utilisateur dans le processus. Ce descripteur est par conséquent passé à tout appel qui interagit avec le fichier ouvert, et après l'appel close , le descripteur n'est plus valable.

Il est important de noter que l'appel à open agit comme un point de validation au cours de laquelle différents contrôles sont effectués. Si toutes les conditions sont remplies, l'appel échoue en retournant -1 au lieu de le descripteur et le type d'erreur est indiqué en errno. Les contrôles essentiels sont:

  • Si le fichier existe;
  • Si le processus appelant a le privilège d'ouvrir ce fichier dans le mode spécifié. Ceci est déterminé par correspondance les autorisations de fichier, propriétaire de l'ID et l'ID de groupe à l'ID de l'appelant.

Dans le contexte du noyau, il y a une sorte de correspondance entre le processus de " descripteurs de fichier et physiquement les fichiers ouverts. La structure de données interne qui est mappé sur le descripteur de fichier peut contenir encore un autre tampon, qui traite avec bloc de périphériques, ou un pointeur interne qui pointe à l'actuelle position de lecture.

83voto

David Z Points 49476

Je vous suggère de prendre un coup d'oeil à ce guide, à travers une version simplifiée de l' open() appel système. Il utilise l'extrait de code suivant, qui est représentative de ce qui se passe en coulisses lorsque vous ouvrez un fichier.

0  int sys_open(const char *filename, int flags, int mode) {
1      char *tmp = getname(filename);
2      int fd = get_unused_fd();
3      struct file *f = filp_open(tmp, flags, mode);
4      fd_install(fd, f);
5      putname(tmp);
6      return fd;
7  }

Brièvement, voici ce que le code ne, ligne par ligne:

  1. Allouer un bloc de noyau de contrôlée de la mémoire et de copier le nom de fichier à partir d'contrôlée par l'utilisateur de la mémoire.
  2. Choisir un solde non utilisé du descripteur de fichier, que vous pouvez considérer comme un index entier dans un cultivables liste de fichiers actuellement ouverts. Chaque processus a son propre liste, mais il est maintenu par le noyau; votre code ne pouvez pas y accéder directement. Une entrée dans la liste contient toutes les informations que le système de fichiers sous-jacent va utiliser pour tirer octets sur la disquette, tels que le numéro d'inœud, les autorisations de traitement, ouvrez les drapeaux, et ainsi de suite.
  3. L' filp_open fonction a la mise en œuvre

    struct file *filp_open(const char *filename, int flags, int mode) {
            struct nameidata nd;
            open_namei(filename, flags, mode, &nd);
            return dentry_open(nd.dentry, nd.mnt, flags);
    }
    

    ce qui fait deux choses:

    1. Utiliser le système de fichiers pour rechercher l'inode (ou, plus généralement, ce genre de identifiant interne le système de fichiers utilise) correspondant au nom de fichier ou de chemin d'accès qui a été passé.
    2. Créer un struct file avec l'information essentielle à propos de l'inode et de le retourner. Cette structure devient l'entrée de la liste des fichiers ouverts que je l'ai mentionné plus tôt.
  4. Magasin ("install") le retour de l'struct dans le processus de la liste des fichiers ouverts.

  5. Gratuit le bloc alloué de noyau de contrôlée de la mémoire.
  6. Retour le descripteur de fichier, qui peut ensuite être transmis à une opération de fichier fonctions comme read(), write(), et close(). Chacun de ces aura la main hors de contrôle au noyau, qui peut utiliser le descripteur de fichier pour rechercher le fichier correspondant pointeur dans le processus de la liste, et d'utiliser les informations contenues dans ce fichier pointeur pour effectuer la lecture, l'écriture, ou à la fermeture.

Si vous vous sentez ambitieux, vous pouvez comparer cet exemple simplifié à la mise en œuvre de l' open() appel système dans le noyau Linux, une fonction appelée do_sys_open(). Vous ne devriez pas avoir de mal à trouver des similitudes.


Bien sûr, ce n'est que la "couche supérieure" de ce qui se passe lorsque vous appelez open() - ou plus précisément, c'est le niveau le plus élevé morceau de code du noyau qui est appelé dans le processus d'ouverture d'un fichier. Un haut niveau de langage de programmation peut ajouter d'autres couches sur le dessus de cela. Il y en a beaucoup qui se passe à des niveaux plus bas. (Merci à Ruslan et pjc50 pour expliquer.) En gros, du haut vers le bas:

  • open_namei() et dentry_open() invoquer code du système de fichiers, qui est aussi une partie du noyau, pour l'accès aux métadonnées et le contenu des fichiers et des répertoires. Le système de fichiers lit les raw octets à partir du disque et interprète ces modèles d'octets comme une arborescence de fichiers et de répertoires.
  • Le système de fichiers utilise le dispositif de bloc de couche, de nouveau une partie du noyau, pour obtenir ces premières octets à partir du lecteur. (Fait amusant: Linux vous permet d'accéder à des données brutes à partir du périphérique de bloc calque à l'aide d' /dev/sda et la comme.)
  • Le dispositif de bloc de couche appelle un pilote de périphérique de stockage, qui est aussi le code du noyau, de traduire à partir d'un niveau moyen d'instruction comme "lire le secteur X" à l'individu d'entrée/sortie des instructions en code machine. Il existe plusieurs types de pilotes de périphériques de stockage, y compris les IDE, le (S)ATA, SCSI, Firewire, et ainsi de suite, correspondant aux différentes normes de communication qu'un lecteur pourrait utiliser. (Notez que le nom est un gâchis.)
  • Les instructions d'e/S d'utiliser les fonctionnalités de la puce du processeur et le contrôleur de la carte mère pour envoyer et recevoir des signaux électriques sur le fil allant de l'entraînement physique. Ce type de matériel, pas les logiciels.
  • À l'autre bout du fil, le disque du firmware (embedded code de contrôle) interprète les signaux électriques pour faire tourner les plateaux et déplacer la tête (HDD), ou de lire un flash de la ROM de la cellule (SSD), ou tout ce qui est nécessaire pour accéder à des données sur ce type de périphérique de stockage.

Cela peut aussi être quelque peu incorrect en raison de la mise en cache. :-P plus Sérieusement, il ya beaucoup de détails que j'ai laissé de côté - une personne (pas moi) pourrait écrire plusieurs livres décrivant la manière dont ce processus fonctionne. Mais cela devrait vous donner une idée.

67voto

Jongware Points 6943

Tout système de fichiers ou le système d'exploitation que vous voulez parler, c'est bien pour moi. Nice!

Sur un ZX Spectrum, l'initialisation de l' LOAD commande va mettre le système dans une boucle, la lecture de l'Audio En ligne.

De début de données est indiqué par une tonalité constante, et après qu'une séquence de longues ou de courtes impulsions à suivre, où une impulsion courte est un binaire 0 et un plus long pour un binaire 1 (https://en.wikipedia.org/wiki/ZX_Spectrum_software). L'étroitesse de charge de la boucle rassemble bits jusqu'à ce qu'il répond à un octet (8 bits), le stocke dans la mémoire, augmente le pointeur de la mémoire, puis revient en boucle sur scan pour plus de bits.

Généralement, la première chose qu'un chargeur pourrait lire est un court, fixe le format de l'en-tête, indiquant au moins le nombre d'octets à attendre, et éventuellement d'autres informations telles que le nom du fichier, type de fichier et l'adresse de chargement. Après la lecture de ce court-tête, le programme pourrait décider de poursuivre le chargement de la majeure partie des données, ou de sortir de la routine de chargement et d'affichage d'un message approprié pour l'utilisateur.

Une Fin-de-fichier de l'état pouvait être reconnue par la réception que le nombre d'octets comme prévu (soit un nombre fixe d'octets, programmés dans le logiciel, ou un nombre variable comme indiqué dans l'en-tête). Une erreur est levée si le chargement de la boucle n'a pas reçu une impulsion dans la gamme de fréquence pour un certain laps de temps.


Un peu de fond sur cette réponse

La procédure décrite charge les données à partir d'une cassette audio, d'où la nécessité de numérisation Audio In (il est connecté avec une prise standard pour les magnétophones). Un LOAD commande est techniquement la même chose que open un fichier, mais il est physiquement liée à la réalité du chargement du fichier. C'est parce que le magnétophone n'est pas contrôlé par l'ordinateur, et vous ne pouvez pas (avec succès), ouvrir un fichier, mais impossible de la charger.

La "boucle" est mentionné, parce que (1) le CPU, un Z80 (si ma mémoire est bonne), était vraiment lent: 3.5 MHz, et (2) le Spectre avait pas d'horloge interne! Ce qui signifie qu'il avait précisément de tenir compte de la T-unis (instruction fois) pour chaque. unique. de l'instruction. à l'intérieur de cette boucle, juste pour maintenir l'exactitude de la bip calendrier.
Heureusement que la faible vitesse de l'UC avait l'avantage que vous pouvez calculer le nombre de cycles sur un morceau de papier, et donc le monde réel le temps qu'ils prennent.

17voto

Jaco Points 8592

Il dépend du système d'exploitation ce qui se passe exactement lorsque vous ouvrez un fichier. Ci-dessous, je décris ce qui se passe dans Linux car il vous donne une idée de ce qui se passe lorsque vous ouvrez un fichier et vous pouvez vérifier le code source si vous êtes intéressé par plus de détails. Je ne suis pas couvrir les autorisations qu'il ferait de cette réponse trop long.

Sous Linux, chaque fichier est reconnu par une structure appelée inode. Chaque structure dispose d'un numéro unique et chaque fichier obtient seulement un numéro d'inœud. Cette structure magasins de méta-données pour un fichier, par exemple de taille de fichier, le fichier des autorisations, l'heure et le pointeur de blocs de disque, cependant, pas le nom réel du fichier lui-même. Chaque fichier (répertoire) contient un nom de fichier d'entrée et le numéro d'inœud pour la recherche. Lorsque vous ouvrez un fichier, en supposant que vous disposez des autorisations requises, un descripteur de fichier est créé à l'aide de l'unique inode numéro associé à un nom de fichier. Comme de nombreux processus/applications peuvent pointer vers le même fichier, inode a un champ lien qui maintient le nombre total de liens vers le fichier. Si un fichier est présent dans un répertoire, son nombre de liaisons est l'un, si il a un lien en dur sur son lien comptage d'être deux, et si un fichier est ouvert par un processus, le nombre de liaisons sera incrémenté de 1.

11voto

Luaan Points 8934

Tenue de livres, la plupart du temps. Cela inclut des contrôles différents comme "le fichier existe?" et "puis-je avoir les autorisations pour ouvrir ce fichier pour l'écriture?".

Mais c'est tout de noyau de trucs, sauf si vous êtes à la mise en œuvre de votre propre jouet OS, il n'y a pas beaucoup à approfondir (si vous êtes, amusez - vous-c'est une excellente expérience d'apprentissage). Bien sûr, vous devriez continuer à apprendre tous les codes d'erreur que vous pouvez recevoir lors de l'ouverture d'un fichier, de sorte que vous pouvez les manipuler correctement - mais ceux-ci sont généralement peu agréable abstractions.

La partie la plus importante au niveau du code, c'est qu'il vous donne une poignée pour l'ouvrir le fichier, que vous pouvez utiliser pour toutes les autres opérations que vous faites avec un fichier. Ne pourriez-vous pas utiliser le nom de fichier à la place de ce handle arbitraire? Bien, sûr - mais à l'aide d'une poignée vous donne certains avantages:

  • Le système peut garder la trace de tous les fichiers qui sont actuellement ouverts, et les empêcher d'être supprimés (par exemple).
  • Systèmes d'exploitation modernes sont construites autour des poignées - il y a des tonnes de choses utiles que vous pouvez faire avec des poignées, et tous les différents types de poignées de se comporter de manière quasiment identique. Par exemple, lorsqu'une opération e/S asynchrone se termine sur un fichier Windows poignée, la poignée est signalé - cela vous permettra de bloquer la poignée jusqu'à ce qu'il est signalé, ou pour terminer l'opération entièrement de manière asynchrone. En attente sur un descripteur de fichier est exactement le même que l'attente sur un fil de la poignée (signalé par exemple, lorsque le thread se termine), une poignée de processus (encore une fois, signalé lorsque le processus se termine), ou une prise de courant (lorsque certaines opération asynchrone se termine). Tout aussi important, les poignées sont la propriété de leurs processus respectifs, de sorte que, lorsqu'un processus est terminé de manière inattendue (ou l'application est mal écrit), le système d'exploitation sait ce que les poignées, il peut libérer.
  • La plupart des opérations sont positionnels - vous read à partir de la dernière position dans votre fichier. En utilisant une poignée d'identifier un particulier de "l'ouverture" d'un fichier, vous pouvez avoir simultanément plusieurs poignées pour le même fichier, chaque lecture de leur propre endroits. En un sens, la poignée agit comme un mobile de la fenêtre dans le fichier (et émettre des requêtes e/S asynchrones, qui sont très pratique).
  • Les poignées sont beaucoup plus petits que les noms de fichier. Une poignée est généralement de la taille d'un pointeur, en général 4 ou 8 octets. D'autre part, les noms de fichiers peut avoir des centaines d'octets.
  • Les poignées permettent à l'OS de déplacer le fichier, même si les applications de l'avoir ouvert la poignée est toujours valide, et il pointe toujours vers le même fichier, même si le nom de fichier a changé.

Il y a aussi quelques autres trucs que vous pouvez faire (par exemple, partager des poignées entre les processus d'avoir un canal de communication sans l'aide d'un fichier physique; sur les systèmes unix, les fichiers sont également utilisés pour les appareils et divers autres canaux virtuels, donc ce n'est pas strictement nécessaire), mais ils ne sont pas vraiment liée à l' open opération elle-même, donc je ne vais pas plonger dans cela.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X