Dans tous les langages de programmation (que j'utilise au moins), vous devez ouvrir un fichier avant de pouvoir y lire ou y écrire.
Mais que fait réellement cette opération ouverte?
Les pages de manuel pour les fonctions typiques ne vous disent rien d'autre que d'ouvrir un fichier en lecture / écriture:
http://www.cplusplus.com/reference/cstdio/fopen/
https://docs.python.org/3/library/functions.html#open
De toute évidence, grâce à l'utilisation de la fonction, vous pouvez dire qu'elle implique la création d'une sorte d'objet qui facilite l'accès à un fichier.
Une autre façon de mettre cela serait, si je devais implémenter une open
fonction, que faudrait-il faire sous Linux?
C
et Linux; car ce que font Linux et Windows diffère. Sinon, c'est un peu trop large. De plus, tout langage de niveau supérieur finira par appeler une API C pour le système ou se compilera en C pour s'exécuter, donc laisser au niveau de "C" le place au plus petit dénominateur commun.Réponses:
Dans presque tous les langages de haut niveau, la fonction qui ouvre un fichier est un wrapper autour de l'appel système du noyau correspondant. Il peut également faire d'autres choses fantaisistes, mais dans les systèmes d'exploitation contemporains, l'ouverture d'un fichier doit toujours passer par le noyau.
C'est pourquoi les arguments de la
fopen
fonction de bibliothèque, ou de Pythonopen
ressemblent étroitement aux arguments de l'open(2)
appel système.En plus d'ouvrir le fichier, ces fonctions mettent généralement en place un tampon qui sera par conséquent utilisé avec les opérations de lecture / écriture. Le but de ce tampon est de garantir que chaque fois que vous voulez lire N octets, l'appel de bibliothèque correspondant renverra N octets, que les appels vers les appels système sous-jacents retournent moins.
Dans les systèmes d'exploitation de type Unix, un appel réussi à
open
renvoie un "descripteur de fichier" qui est simplement un entier dans le contexte du processus utilisateur. Ce descripteur est par conséquent transmis à tout appel qui interagit avec le fichier ouvert et après l'appelclose
, le descripteur devient invalide.Il est important de noter que l'appel à
open
agit comme un point de validation où sont effectués divers contrôles. Si toutes les conditions ne sont pas remplies, l'appel échoue en renvoyant à la-1
place du descripteur et le type d'erreur est indiqué danserrno
. Les contrôles essentiels sont:Dans le contexte du noyau, il doit y avoir une sorte de mappage entre les descripteurs de fichiers du processus et les fichiers physiquement ouverts. La structure de données interne qui est mappée au descripteur peut contenir encore un autre tampon qui traite des périphériques basés sur des blocs, ou un pointeur interne qui pointe vers la position actuelle de lecture / écriture.
la source
man dup2
et vérifiez la subtilité entre un descripteur de fichier ouvert (c'est-à-dire un FD qui se trouve être ouvert) et une description de fichier ouvert (un OFD).Je vous suggère de jeter un œil à ce guide à travers une version simplifiée de l'
open()
appel système . Il utilise l'extrait de code suivant, qui est représentatif de ce qui se passe en arrière-plan lorsque vous ouvrez un fichier.En bref, voici ce que fait ce code, ligne par ligne:
La
filp_open
fonction a l'implémentationce qui fait deux choses:
struct file
avec les informations essentielles sur l'inode et renvoyez-le. Cette structure devient l'entrée dans cette liste de fichiers ouverts que j'ai mentionnée plus tôt.Stockez ("installez") la structure retournée dans la liste des fichiers ouverts du processus.
read()
,write()
etclose()
. Chacun d'eux transférera le contrôle au noyau, qui peut utiliser le descripteur de fichier pour rechercher le pointeur de fichier correspondant dans la liste du processus, et utiliser les informations de ce pointeur de fichier pour effectuer la lecture, l'écriture ou la fermeture.Si vous vous sentez ambitieux, vous pouvez comparer cet exemple simplifié à l'implémentation de l'
open()
appel système dans le noyau Linux, une fonction appeléedo_sys_open()
. Vous ne devriez pas avoir de mal à trouver les 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 morceau de code de noyau le plus élevé qui est invoqué lors de l'ouverture d'un fichier. Un langage de programmation de haut niveau pourrait ajouter des couches supplémentaires en plus de cela. Il y a beaucoup de choses aux niveaux inférieurs. (Merci à Ruslan et pjc50 pour leur explication.) En gros, de haut en bas:open_namei()
etdentry_open()
invoquer le code du système de fichiers, qui fait également partie du noyau, pour accéder aux métadonnées et au contenu des fichiers et répertoires. Le système de fichiers lit les octets bruts du disque et interprète ces modèles d'octets comme une arborescence de fichiers et de répertoires./dev/sda
et similaires.)Cela peut également être quelque peu incorrect en raison de la mise en cache . :-P Sérieusement cependant, il y a beaucoup de détails que j'ai omis - une personne (pas moi) pourrait écrire plusieurs livres décrivant comment tout ce processus fonctionne. Mais cela devrait vous donner une idée.
la source
Tout système de fichiers ou système d'exploitation dont vous voulez parler me convient. Agréable!
Sur un ZX Spectrum, l'initialisation d'une
LOAD
commande mettra le système en boucle serrée, lisant la ligne Audio In.Le début des données est indiqué par une tonalité constante, et ensuite une séquence d'impulsions longues / courtes suit, où une impulsion courte est pour un binaire
0
et une plus longue pour un binaire1
( https://en.wikipedia.org/ wiki / ZX_Spectrum_software ). La boucle de charge étroite rassemble des bits jusqu'à ce qu'elle remplisse un octet (8 bits), les stocke en mémoire, augmente le pointeur de mémoire, puis revient en boucle pour rechercher plus de bits.En règle générale, la première chose qu'un chargeur lirait est un en- tête de format court et fixe , indiquant au moins le nombre d'octets à attendre, et éventuellement des informations supplémentaires telles que le nom de fichier, le type de fichier et l'adresse de chargement. Après avoir lu ce court en-tête, le programme pourrait décider de continuer à charger la majeure partie des données ou de quitter la routine de chargement et d'afficher un message approprié pour l'utilisateur.
Un état de fin de fichier peut être reconnu en recevant autant d'octets que prévu (soit un nombre fixe d'octets, câblé dans le logiciel, soit un nombre variable tel qu'indiqué dans un en-tête). Une erreur s'est produite si la boucle de chargement n'a pas reçu d'impulsion dans la plage de fréquences attendue pendant un certain temps.
Un peu d'histoire sur cette réponse
La procédure décrite charge les données d'une bande audio ordinaire - d'où la nécessité de balayer l'entrée audio (elle est connectée avec une prise standard aux magnétophones). Une
LOAD
commande est techniquement identique àopen
un fichier - mais il est lié physiquement en fait le chargement du fichier. En effet, le magnétophone n'est pas contrôlé par l'ordinateur et vous ne pouvez pas (avec succès) ouvrir un fichier sans le charger.La "boucle serrée" est mentionnée car (1) le CPU, un Z80-A (si la mémoire est bonne), était vraiment lent: 3,5 MHz, et (2) le Spectrum n'avait pas d'horloge interne! Cela signifie qu'il devait compter avec précision le nombre d' états T (temps d'instruction) pour chaque. Célibataire. instruction. à l'intérieur de cette boucle, juste pour maintenir le timing précis du bip.
Heureusement, cette faible vitesse du processeur avait l'avantage distinct que vous pouviez calculer le nombre de cycles sur une feuille de papier, et donc le temps réel qu'ils prendraient.
la source
Cela 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 sous Linux car cela 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 couvre pas les autorisations car cela rendrait cette réponse trop longue.
Sous Linux, chaque fichier est reconnu par une structure appelée inode. Chaque structure a un numéro unique et chaque fichier ne reçoit qu'un seul numéro d'inode. Cette structure stocke les métadonnées d'un fichier, par exemple la taille du fichier, les autorisations de fichier, les horodatages et le pointeur sur les blocs de disque, mais pas le nom de fichier lui-même. Chaque fichier (et répertoire) contient une entrée de nom de fichier et le numéro d'inode pour la recherche. Lorsque vous ouvrez un fichier, en supposant que vous disposez des autorisations appropriées, un descripteur de fichier est créé à l'aide du numéro d'inode unique associé au nom de fichier. Comme de nombreux processus / applications peuvent pointer vers le même fichier, inode a un champ de lien qui conserve le nombre total de liens vers le fichier. Si un fichier est présent dans un répertoire, son nombre de liens est un, s'il a un lien dur, son nombre de liens sera de deux et si un fichier est ouvert par un processus, le nombre de liens sera incrémenté de 1.
la source
Tenue de livres, surtout. Cela inclut diverses vérifications telles que "Le fichier existe-t-il?" et "Ai-je les autorisations pour ouvrir ce fichier en écriture?".
Mais c'est tout ce qui concerne le noyau - à moins que vous n'implémentiez votre propre système d'exploitation jouet, il n'y a pas grand-chose à explorer (si vous l'êtes, amusez-vous - c'est une excellente expérience d'apprentissage). Bien sûr, vous devez toujours apprendre tous les codes d'erreur possibles que vous pouvez recevoir lors de l'ouverture d'un fichier, afin de pouvoir les gérer correctement - mais ce sont généralement de jolies petites abstractions.
La partie la plus importante au niveau du code est qu'elle vous donne une poignée sur le fichier ouvert, que vous utilisez pour toutes les autres opérations que vous effectuez avec un fichier. Ne pourriez-vous pas utiliser le nom de fichier au lieu de ce descripteur arbitraire? Eh bien, bien sûr - mais l'utilisation d'une poignée vous offre certains avantages:
read
partir de la dernière position de votre fichier. En utilisant une poignée pour identifier une "ouverture" particulière d'un fichier, vous pouvez avoir plusieurs poignées simultanées vers le même fichier, chacune lisant à partir de leur propre emplacement. D'une certaine manière, le handle agit comme une fenêtre mobile dans le fichier (et un moyen d'émettre des demandes d'E / S asynchrones, ce qui est très pratique).Il y a aussi d'autres astuces que vous pouvez faire (par exemple, partager des poignées entre les processus pour avoir un canal de communication sans utiliser de 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és à l'
open
opération elle-même, donc je ne vais pas m'y plonger.la source
Au cœur de celui-ci lors de l'ouverture pour la lecture, rien de fantaisiste n'a réellement besoin se produire. Il suffit de vérifier que le fichier existe et que l'application dispose de privilèges suffisants pour le lire et créer un descripteur sur lequel vous pouvez émettre des commandes de lecture dans le fichier.
C'est sur ces commandes que la lecture réelle sera envoyée.
Le système d'exploitation aura souvent une longueur d'avance sur la lecture en lançant une opération de lecture pour remplir le tampon associé à la poignée. Ensuite, lorsque vous effectuez la lecture, il peut retourner immédiatement le contenu du tampon plutôt que d'attendre sur le disque IO.
Pour ouvrir un nouveau fichier en écriture, le système d'exploitation devra ajouter une entrée dans le répertoire du nouveau fichier (actuellement vide). Et encore une fois un handle est créé sur lequel vous pouvez émettre les commandes d'écriture.
la source
Fondamentalement, un appel à open doit rechercher le fichier, puis enregistrer tout ce dont il a besoin pour que les opérations d'E / S ultérieures puissent le retrouver. C'est assez vague, mais ce sera vrai sur tous les systèmes d'exploitation auxquels je peux immédiatement penser. Les spécificités varient d'une plateforme à l'autre. De nombreuses réponses déjà mentionnées ici parlent des systèmes d'exploitation de bureau modernes. J'ai fait un peu de programmation sur CP / M, donc j'offrirai mes connaissances sur la façon dont cela fonctionne sur CP / M (MS-DOS fonctionne probablement de la même manière, mais pour des raisons de sécurité, ce n'est normalement pas fait comme ça aujourd'hui) ).
Sur CP / M, vous avez une chose appelée FCB (comme vous l'avez mentionné C, vous pouvez l'appeler une structure; c'est vraiment une zone contiguë de 35 octets dans la RAM contenant divers champs). Le FCB a des champs pour écrire le nom de fichier et un entier (4 bits) identifiant l'unité de disque. Ensuite, lorsque vous appelez le fichier ouvert du noyau, vous passez un pointeur vers cette structure en le plaçant dans l'un des registres du processeur. Quelque temps plus tard, le système d'exploitation revient avec la structure légèrement modifiée. Quelles que soient les E / S que vous faites pour ce fichier, vous passez un pointeur vers cette structure vers l'appel système.
Que fait CP / M avec ce FCB? Il réserve certains champs pour son propre usage et les utilise pour garder une trace du fichier, il vaut donc mieux ne jamais les toucher depuis l'intérieur de votre programme. L'opération d'ouverture de fichier recherche dans la table au début du disque un fichier portant le même nom que ce qui se trouve dans le FCB (le caractère générique «?» Correspond à n'importe quel caractère). S'il trouve un fichier, il copie certaines informations dans le FCB, y compris les emplacements physiques du fichier sur le disque, afin que les appels d'E / S ultérieurs appellent finalement le BIOS qui peut transmettre ces emplacements au pilote de disque. À ce niveau, les détails varient.
la source
En termes simples, lorsque vous ouvrez un fichier, vous demandez en fait au système d'exploitation de charger le fichier souhaité (copier le contenu du fichier) depuis le stockage secondaire vers la RAM pour le traitement. Et la raison derrière cela (Chargement d'un fichier) est que vous ne pouvez pas traiter le fichier directement depuis le disque dur en raison de sa vitesse extrêmement lente par rapport à Ram.
La commande open génère un appel système qui à son tour copie le contenu du fichier du stockage secondaire (disque dur) vers le stockage principal (Ram).
Et nous «fermons» un fichier car le contenu modifié du fichier doit être reflété dans le fichier d'origine qui se trouve sur le disque dur. :)
J'espère que cela pourra aider.
la source