Sur les systèmes Unix, pourquoi devons-nous explicitement ouvrir les fichiers «open ()» et «close ()» pour pouvoir les «lire ()» ou les «écrire ()»?

50

Pourquoi existe- t-il open()et close()existe- t- il dans la conception du système de fichiers Unix?

Le système d'exploitation ne pourrait-il pas simplement détecter la première fois read()ou a write()été appelé et fait ce qu'il open()ferait normalement?

utilisateur5977637
la source
22
Il est à noter que ce modèle ne fait pas partie du système de fichiers mais de l' API Unix . Le système de fichiers ne concerne que l’endroit où se trouvent les octets sur le disque et où placer le nom du fichier, etc. noyau pour traduire ces appels dans les mises à jour appropriées pour le système de fichiers (tout comme il est maintenant).
Marcel
18
Tel que formulé, je pense que c'est plus sur la raison pour laquelle open()existe. "Le système d'exploitation ne pourrait-il pas simplement détecter la première fois read () ou write () et faire ce que open () ferait normalement?" Y a-t-il une suggestion correspondante pour quand la fermeture aurait lieu?
Joshua Taylor
7
Comment diriez-vous read()ou à write()quel fichier accéder? Vraisemblablement en passant le chemin. Que faire si le chemin du fichier change pendant que vous y accédez (entre deux read()ou des write()appels)?
user253751
2
Aussi , vous ne faites pas habituellement le contrôle d' accès sur read()et write(), juste open().
Pavel Šimerda
6
@ Johnny: Vous oubliez peut-être à quel point le matériel était limité à cette époque. Le PDP-7 sur lequel Unix a été implémenté pour la première fois avait (par Google) un maximum de 64K de RAM et une horloge à 0.333 MHz - un peu moins qu’un simple microcontrôleur de nos jours. Faire cette collecte de place, ou utiliser le code système pour contrôler l'accès aux fichiers, aurait mis le système à genoux.
jamesqf

Réponses:

60

Dennis Ritchie mentionne dans «L'évolution du système de partage de temps Unix» que openet closeavec read, writeet creatétait présent dans le système dès le début.

Je suppose qu'un système sans openet closene serait pas inconcevable, mais je pense que cela compliquerait la conception. En règle générale, vous souhaitez effectuer plusieurs appels en lecture et en écriture, et non un seul, et c'est probablement le cas en particulier sur les anciens ordinateurs dotés d'une mémoire RAM très limitée sur laquelle UNIX est originaire. Avoir une poignée qui maintient la position actuelle de votre fichier simplifie cela. Si readouwritedevaient renvoyer le descripteur, ils devaient en renvoyer une paire - un descripteur et leur propre statut de retour. La partie poignée de la paire serait inutile pour tous les autres appels, ce qui rendrait cet arrangement peu commode. Le fait de laisser l’état du curseur au noyau lui permet d’améliorer son efficacité non seulement par la mise en mémoire tampon. Il y a aussi des coûts associés à la recherche de chemin - avoir une poignée vous permet de le payer une seule fois. En outre, certains fichiers de la vision du monde UNIX n’ont même pas de chemin de système de fichiers (ou ne l’ont pas fait - ils le font maintenant /proc/self/fd).

PSkocik
la source
7
Le coût de la recherche de chemin et de la vérification des autorisations, etc., est très important. Si vous souhaitez créer un système sans open/ close, veillez à implémenter des éléments tels que /dev/stdoutpermettre la tuyauterie.
Peter Cordes
5
Je pense qu'un autre aspect à ceci est que vous pouvez garder cette poignée sur le même fichier lorsque vous utilisez plusieurs lectures lorsque vous gardez le fichier ouvert. Sinon, vous pourriez avoir des cas où un autre processus dissocie et recrée un fichier portant le même nom, et la lecture d'un fichier en morceaux peut en réalité être complètement incohérente. (Cela peut également dépendre du système de fichiers.)
Bruno
2
J'en ai conçu un sans close (); vous passez le numéro d'inode et l'offset à read () et write (). Je ne peux pas me passer de open () très facilement car c'est là que réside la résolution de nom.
Josué
3
@Joshua: Un tel système a une sémantique fondamentalement différente, car les descripteurs de fichiers Unix ne font pas référence à des fichiers (inodes), mais à des descriptions de fichiers ouvertes , qui peuvent être nombreuses pour un fichier donné (inode).
R ..
@Joshua, vous venez de renommer open()à get_inode()et a fait tout le système plus rigide (impossible de lire / écrire le même fichier en plusieurs positions simultanément).
vonbrand
53

Ensuite, tous les appels readet writedevraient passer cette information à chaque opération:

  • le nom du fichier
  • les permissions du fichier
  • si l'appelant ajoute ou crée
  • si l'appelant a fini de travailler avec le fichier (pour supprimer les tampons de lecture inutilisés et garantir que l'écriture des tampons est vraiment terminée)

Si vous considérez les indépendants appels open , read, writeet closed'être plus simple qu'un E / S à usage unique un message est basé sur votre philosophie de conception. Les développeurs Unix ont choisi d’utiliser des opérations et des programmes simples qui peuvent être combinés de nombreuses manières, plutôt qu’une seule opération (ou un seul programme) qui fait tout.

Thomas Dickey
la source
Dans la plupart des cas, les appelants doivent également spécifier le décalage souhaité dans un fichier. Dans certaines situations (par exemple, un protocole UDP permettant l’accès aux données), il peut être utile d’avoir chaque requête identifiant indépendamment un fichier et son décalage, car cela évite à un serveur de conserver son état, mais en général il est plus pratique de disposer du serveur. garder une trace de la position du fichier. En outre, comme indiqué ailleurs, le code qui va écrire des fichiers doit souvent les verrouiller au préalable et les verrouiller ensuite; Combiner ces opérations avec ouverture / fermeture est très pratique.
Supercat
5
Le "fichier" peut ne pas avoir un nom ou des autorisations en premier lieu; readet writene sont pas limités aux fichiers stockés sur un système de fichiers, ce qui est une décision de conception fondamentale sous Unix, comme l'explique pjc50.
Reinierpost
1
De plus, dans le fichier pour le lire / l’écrire - le début, la fin ou une position arbitraire (normalement juste après la fin de la dernière lecture / écriture) - le noyau en garde une trace pour vous (avec un mode diriger toutes les écritures vers la fin du fichier, ou sinon les fichiers sont ouverts avec la position au début et avancés avec chaque lecture / écriture et peuvent être déplacés avec lseek)
Random832
51

Le concept du descripteur de fichier est important en raison du choix de conception d'UNIX selon lequel "tout est un fichier", y compris les éléments ne faisant pas partie du système de fichiers. Tels que les lecteurs de bande, le clavier et l’écran (ou le télétype!), Les lecteurs de cartes / de cartes perforées, les connexions série, les connexions réseau et (l’invention clé UNIX) des connexions directes à d’autres programmes appelés "tuyaux".

Si vous examinez de nombreux utilitaires UNIX standard simples grep, notamment dans leur version d'origine, vous remarquerez qu'ils n'incluent pas les appels à open()et close()mais juste readet write. Les descripteurs de fichier sont configurés en dehors du programme par le shell et transmis au démarrage. Ainsi, le programme n'a pas à se soucier d'écrire dans un fichier ou dans un autre programme.

En plus open, les autres façons d'obtenir des descripteurs de fichiers sont socket, listen, pipe, dupet un mécanisme très Heath Robinson pour envoyer des descripteurs de fichiers sur les tuyaux: https://stackoverflow.com/questions/28003921/sending-file-descriptor-by-linux -prise

Éditer: quelques notes de cours décrivant les couches d'indirection et expliquant comment cela permet à O_APPEND de fonctionner de manière judicieuse. Notez que garder les données inode en mémoire garantit que le système n'aura pas à aller les chercher à nouveau pour la prochaine opération d'écriture.

pjc50
la source
1
De plus creat, et listenne crée pas de fd, mais quand (et si) une requête arrive pendant l’écoute, acceptcrée et retourne un fd pour le nouveau socket (connecté).
dave_thompson_085
18
C'est la bonne réponse. Le célèbre (petit) ensemble d'opérations sur les descripteurs de fichier est une API unificatrice pour tous les types de ressources qui produisent ou consomment des données. Ce concept est extrêmement réussi. Une chaîne pourrait éventuellement avoir une syntaxe définissant le type de ressource ainsi que l'emplacement réel (URL anybody?), Mais copier des chaînes autour desquelles occupent plusieurs pour cent de la mémoire vive disponible (qu'est-ce que c'était sur le PDP 7? 16 Ko?) Semble excessif .
Peter - Réintégrez Monica le
Peut-être le serait-il si les appels de bas niveau et le shell étaient développés en même temps. Mais a pipeété introduit quelques années après le début du développement sous Unix.
Thomas Dickey
1
@Thomas Dickey: Ce qui montre simplement à quel point le design original était bon, puisqu'il permettait l'extension simple de pipes & c :-)
jamesqf
Mais suivant cette argumentation, cette réponse n’apporte rien de nouveau.
Thomas Dickey
10

La réponse est non, car open () et close () créent et détruisent un handle, respectivement. Vous voudrez peut-être (et tout le temps tout le temps, vraiment) être le seul appelant avec un niveau d'accès particulier, car un autre appelant (par exemple) qui écrit dans un fichier que vous analysez de manière inattendue peut quitter. une application dans un état inconnu ou aboutissant à une impasse, par exemple le lemme Dining Philosophers.

Même sans cette considération, il y a des implications sur les performances à prendre en compte; close () autorise le système de fichiers (si c'est approprié ou si vous l'avez appelé) à vider le tampon que vous occupiez, opération coûteuse. Plusieurs éditions consécutives d'un flux en mémoire sont beaucoup plus efficaces que plusieurs cycles essentiellement non liés en lecture-écriture-modification sur un système de fichiers qui, pour ce que vous savez, existe à l'autre bout du monde, dispersé dans un datacenter digne d'un stockage en bloc à latence élevée. Même avec le stockage local, la mémoire est généralement plus rapide de plusieurs ordres de grandeur que le stockage en masse.

msaunier
la source
7

Open () offre un moyen de verrouiller les fichiers en cours d'utilisation. Si les fichiers étaient automatiquement ouverts, lus / écrits, puis refermés par le système d'exploitation, rien ne pourrait empêcher les autres applications de modifier ces fichiers d'une opération à l'autre.

Bien que cela puisse être gérable (de nombreux systèmes prennent en charge l’accès non exclusif aux fichiers) pour plus de simplicité, la plupart des applications supposent que les fichiers qu’elles ont ouverts ne changent pas.


la source
5

Parce que le chemin du fichier peut se déplacer pendant que vous supposez qu'il restera le même.

Mehrdad
la source
4

La lecture et l'écriture sur un système de fichiers peuvent impliquer une grande variété de schémas de mise en mémoire tampon, de maintenance du système d'exploitation, de gestion de disque de bas niveau et de nombreuses autres actions potentielles. Ainsi, les actions open()et close()servent de cadre pour ces types d’activités sous le capot. Différentes implémentations d'un système de fichiers peuvent être hautement personnalisées en fonction des besoins tout en restant transparentes pour le programme appelant.

Si le système d'exploitation n'a pas ouvert / fermé, puis avec readou write, ces actions de fichier devront toujours effectuer les initialisations, le vidage / la gestion de la mémoire tampon, etc. à chaque fois. C'est beaucoup de frais généraux à imposer pour les lectures et les écritures répétitives.

PeterT
la source
Sans oublier que open () et close () conservent également la position dans le fichier (pour la prochaine lecture ou la prochaine écriture). Ainsi, à la fin, read () et write () auraient besoin d'une structure pour gérer tous les paramètres, ou d'arguments pour chaque paramètre. Créer une structure équivaut (site du programmeur) à un open. Par conséquent, si le système d'exploitation connaît également l'open, nous n'avons que plus d'avantages.
Giacomo Catenazzi
1

Le mantra Unix est "offrir une façon de faire les choses", ce qui signifie "factoriser" en pièces (réutilisables) à combiner à volonté. Par exemple, dans ce cas, séparez la création et la destruction des descripteurs de fichiers de leur utilisation. Des avantages importants sont apparus plus tard, avec les canaux et les connexions réseau (ils sont également manipulés par des descripteurs de fichiers, mais ils sont créés d'une autre manière). Pouvoir envoyer des descripteurs de fichiers (par exemple, les transmettre à des processus enfants en tant que "fichiers ouverts" qui survivent à un exec(2)processus, et même à des processus non liés via un canal), n'est possible que de cette façon. Surtout si vous souhaitez offrir un accès contrôlé à un fichier protégé. Ainsi, vous pouvez par exemple ouvrir/etc/passwd pour l’écriture et transmettez-le à un processus enfant qui n’est pas autorisé à ouvrir ce fichier en écriture (oui, je sais que c’est un exemple ridicule, n'hésitez pas à éditer avec quelque chose de plus réaliste).

vonbrand
la source