Sui sistemi Unix, perché dobbiamo esplicitamente file `open ()` e `close ()` per poterli leggere () `o` scrivere () `?


50

Perché esiste open()ed close()esiste nella progettazione del filesystem Unix?

Il sistema operativo non è in grado di rilevare la prima volta read()o è write()stato chiamato e fare qualsiasi cosa open()normalmente farebbe?


22
Vale la pena notare che questo modello non fa parte del filesystem ma piuttosto dell'API Unix . Il filesystem si preoccupa semplicemente di dove vanno i byte sul disco e dove mettere il nome del file, ecc. Sarebbe perfettamente possibile avere il modello alternativo che descrivi su un filesystem Unix come UFS o ext4, sarebbe fino al kernel per tradurre quelle chiamate negli aggiornamenti corretti per il filesystem (così come è ora).
marzo

18
Come detto, penso che questo sia più sul perché open()esiste. "Il sistema operativo non potrebbe rilevare la prima volta read () o write () e fare qualunque cosa open () normalmente farebbe?" C'è un suggerimento corrispondente per quando avverrebbe la chiusura ?
Joshua Taylor,

7
Come diresti read()o a write()quale file accedere? Presumibilmente passando il sentiero. Cosa succede se il percorso del file cambia durante l'accesso (tra due read()o write()chiamate)?
user253751

2
Inoltre di solito non si esegue il controllo degli accessi read()e write(), solo su open().
Pavel Šimerda,

6
@Johnny: forse stai dimenticando quanto fosse limitato l'hardware in quei giorni. Il PDP-7 su cui Unix fu implementato per la prima volta aveva (per Google) un massimo di 64 KB di RAM e un clock a 0,333 MHz - piuttosto meno di un semplice microcontrollore in questi giorni. Fare una tale spazzatura, o usare il codice di sistema per monitorare l'accesso ai file, avrebbe messo in ginocchio il sistema.
jamesqf,

Risposte:


60

Dennis Ritchie cita nel «L'evoluzione del Unix time-sharing di sistema» che opene closeinsieme read, writee createrano presenti nel sistema giusto fin dall'inizio.

Immagino che un sistema senza opene closenon sarebbe inconcepibile, tuttavia credo che complicherebbe il design. In genere si desidera effettuare più chiamate di lettura e scrittura, non solo una, e questo era probabilmente vero soprattutto su quei vecchi computer con RAM molto limitata su cui aveva origine UNIX. Avere un handle che mantiene la posizione del file corrente semplifica questo. Se readowritedovevano restituire la maniglia, avrebbero dovuto restituire una coppia - una maniglia e il loro stato di restituzione. La parte handle della coppia sarebbe inutile per tutte le altre chiamate, il che renderebbe scomodo tale accordo. Lasciare lo stato del cursore sul kernel consente di migliorare l'efficienza non solo mediante buffering. C'è anche qualche costo associato alla ricerca del percorso: avere un handle ti consente di pagarlo solo una volta. Inoltre, alcuni file nella visione del mondo UNIX non hanno nemmeno un percorso di filesystem (o no - ora lo fanno con cose del genere /proc/self/fd).


7
Il costo della ricerca del percorso e del controllo delle autorizzazioni, ecc. È molto significativo. Se si desidera creare un sistema senza open/ close, si sarebbe sicuri di implementare cose come /dev/stdoutconsentire il piping.
Peter Cordes,

5
Penso che un altro aspetto di questo sia che puoi mantenere quell'handle nello stesso file quando usi più letture quando mantieni il file aperto. Altrimenti, potresti avere casi in cui un altro processo scollega e ricrea un file con lo stesso nome e la lettura di un file in blocchi potrebbe effettivamente essere completamente incoerente. (Alcuni di questi potrebbero dipendere anche dal filesystem.)
Bruno,

2
Ne ho disegnato uno senza close (); si passa il numero di inode e si offset a read () e write (). Non posso fare a meno di open () molto facilmente perché è lì che vive la risoluzione dei nomi.
Joshua,

3
@Joshua: un sistema di questo tipo ha una semantica sostanzialmente diversa perché i descrittori di file unix non si riferiscono a file (inode) ma ad aprire descrizioni di file , di cui potrebbero essercene molte per un determinato file (inode).
R ..

@Joshua, basta rinominato open()per get_inode()e ha reso l'intero sistema più rigido (impossibile leggere / scrivere lo stesso file in diversi punti contemporaneamente).
vonbrand,

53

Quindi tutte le chiamate reade writedovrebbero passare queste informazioni su ogni operazione:

  • il nome del file
  • le autorizzazioni del file
  • se il chiamante sta aggiungendo o creando
  • se il chiamante ha finito di lavorare con il file (per scartare i buffer di lettura inutilizzati e assicurarsi che i buffer di scrittura abbiano veramente finito di scrivere)

Se si considerano le indipendenti chiamate open , read, writee closeper essere più semplice di un singolo-purpose di I / O messaggio è basato sulla vostra filosofia progettuale. Gli sviluppatori di Unix hanno scelto di utilizzare semplici operazioni e programmi che possono essere combinati in molti modi, piuttosto che un'unica operazione (o programma) che fa tutto.


Nella maggior parte dei casi i chiamanti dovrebbero anche specificare l'offset desiderato all'interno di un file. Esistono alcune situazioni (ad esempio un protocollo UDP che consente l'accesso ai dati) in cui ogni richiesta identifica in modo indipendente un file e l'offset può essere utile poiché elimina la necessità di un server per mantenere lo stato, ma in generale è più conveniente avere il server tenere traccia della posizione del file. Inoltre, come notato altrove, il codice che scriverà spesso i file deve prima bloccarli e successivamente bloccarli; pettinare quelle operazioni con apertura / chiusura è molto conveniente.
supercat,

5
Il "file" potrebbe non avere un nome o autorizzazioni in primo luogo; reade writenon sono limitati ai file che vivono su un file system, e questa è una decisione di progettazione fondamentale in Unix, come spiega pjc50.
reinierpost,

1
Anche dove nel file leggerlo / scriverlo - l'inizio, la fine o una posizione arbitraria (in genere essere immediatamente dopo la fine dell'ultima lettura / scrittura) - il kernel tiene traccia di questo per te (con una modalità per dirige tutte le scritture alla fine del file, altrimenti i file vengono aperti con la posizione all'inizio e avanzati con ogni lettura / scrittura e possono essere spostati con lseek)
Casuale 832

51

Il concetto di handle di file è importante a causa della scelta progettuale di UNIX secondo cui "tutto è un file", comprese le cose che non fanno parte del filesystem. Come unità a nastro, tastiera e schermo (o teletipo!), Lettori di schede / nastri perforati, connessioni seriali, connessioni di rete e (l'invenzione chiave UNIX) connessioni dirette ad altri programmi chiamati "pipe".

Se guardi molte delle semplici utility UNIX standard come grep, specialmente nelle loro versioni originali, noterai che non includono chiamate a open()e close()ma solo reade write. Gli handle di file vengono impostati all'esterno del programma dalla shell e passati all'avvio. Quindi il programma non deve preoccuparsi se sta scrivendo su un file o su un altro programma.

Così come open, gli altri modi di ottenere descrittori di file sono socket, listen, pipe, dup, e un meccanismo di Heath Robinson per l'invio di descrittori di file su tubi: https://stackoverflow.com/questions/28003921/sending-file-descriptor-by-linux -socket

Modifica: alcune note di lezione che descrivono i livelli di riferimento indiretto e come questo fa funzionare O_APPEND in modo sensato. Tenere presente che la conservazione dei dati degli inode garantisce che il sistema non debba andare e recuperarli nuovamente per la successiva operazione di scrittura.


1
Inoltre creat, e listennon crea un fd, ma quando (e se) arriva una richiesta durante l'ascolto acceptcrea e restituisce un fd per il nuovo socket (connesso).
dave_thompson_085,

18
Questa è la risposta corretta. Il famoso (piccolo) insieme di operazioni sui descrittori di file è un'API unificante per tutti i tipi di risorse che producono o consumano dati. Questo concetto ha un enorme successo. Una stringa potrebbe presumibilmente avere una sintassi che definisce il tipo di risorsa insieme alla posizione effettiva (URL qualcuno?), Ma per copiare le stringhe attorno alle quali occupano diverse percentuali della RAM disponibile (che cosa era sul PDP 7? 16 kB?) Sembra eccessivo .
Peter - Ripristina Monica il

Forse lo sarebbe se le chiamate di basso livello e la shell fossero sviluppate contemporaneamente. Ma è pipestato introdotto pochi anni dopo l'inizio dello sviluppo su Unix.
Thomas Dickey,

1
@Thomas Dickey: che mostra semplicemente quanto fosse buono il design originale, dal momento che ha permesso la semplice estensione di pipe & c :-)
jamesqf

Ma seguendo tale argomento, questa risposta non fornisce nulla di nuovo.
Thomas Dickey,

10

La risposta è no, perché open () e close () creano e distruggono un handle, rispettivamente. Ci sono momenti (beh, sempre, davvero) in cui potresti voler garantire di essere l'unico chiamante con un determinato livello di accesso, poiché un altro chiamante (ad esempio) che scrive su un file che stai analizzando inaspettatamente potrebbe lasciare un'applicazione in uno stato sconosciuto o portare a un livelock o deadlock, ad esempio il lemma dei filosofi da pranzo.

Anche senza quella considerazione, ci sono implicazioni sulle prestazioni da considerare; close () consente al filesystem di (se è appropriato o se lo hai richiesto) svuotare il buffer che stavi occupando, un'operazione costosa. Diverse modifiche consecutive a un flusso in memoria sono molto più efficienti di diversi cicli di modifica lettura / scrittura essenzialmente non correlati a un filesystem che, per quanto ne sai, esiste a metà mondo sparpagliato su un data center di archiviazione di massa ad alta latenza. Anche con l'archiviazione locale, la memoria è in genere molti ordini di grandezza più veloce dell'archiviazione di massa.


7

Open () offre un modo per bloccare i file mentre sono in uso. Se i file venissero aperti, letti / scritti automaticamente e quindi richiusi dal sistema operativo, non vi sarebbe nulla che impedisse ad altre applicazioni di modificare tali file tra le operazioni.

Sebbene ciò possa essere gestibile (molti sistemi supportano l'accesso ai file non esclusivo) per semplicità la maggior parte delle applicazioni presume che i file aperti non cambino.


5

Perché il percorso del file potrebbe spostarsi mentre stai assumendo che rimarrà lo stesso.


4

La lettura e la scrittura su un filesystem può comportare una grande varietà di schemi di buffering, pulizia del sistema operativo, gestione del disco di basso livello e una serie di altre potenziali azioni. Quindi le azioni open()e close()fungono da set-up per questi tipi di attività sottocoperta. Diverse implementazioni di un filesystem potrebbero essere altamente personalizzate secondo necessità e rimanere comunque trasparenti al programma chiamante.

Se il sistema operativo non avesse apertura / chiusura, quindi con reado write, quelle azioni sui file dovrebbero comunque eseguire qualsiasi inizializzazione, svuotamento / gestione del buffer, ecc. Ogni volta. È un sacco di spese generali da imporre per letture e scritture ripetitive.


Da non dimenticare che open () e close () mantiene anche la posizione nel file (per la lettura successiva o successiva). Quindi alla fine o read () e write () avrebbero bisogno di una struttura per gestire tutti i parametri, o di argomenti per ogni parametro. La creazione di una struttura equivale (sito del programmatore) a un open, quindi se anche il SO conosce open, abbiamo solo più vantaggi.
Giacomo Catenazzi,

1

Il mantra Unix è "offrire un modo di fare le cose", che significa "factoring" in pezzi (riutilizzabili) da combinare a piacimento. Vale a dire, in questo caso, separare la creazione e la distruzione degli handle di file dal loro uso. Importanti vantaggi sono arrivati ​​in seguito, con pipe e connessioni di rete (sono anche manipolati tramite handle di file, ma sono creati in altri modi). Essere in grado di spedire handle di file in giro (ad es. Passandoli a processi figlio come "file aperti" che sopravvivono exec(2)e persino a processi non correlati attraverso una pipe) sono possibili solo in questo modo. Soprattutto se si desidera offrire un accesso controllato a un file protetto. Quindi puoi ad es. Aprire/etc/passwd per la scrittura e passalo a un processo figlio a cui non è consentito aprire quel file per la scrittura (sì, lo so che questo è un esempio ridicolo, sentiti libero di modificarlo con qualcosa di più realistico).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.