Cosa sono le directory, se tutto su Linux è un file?


17

Molto spesso i principianti sentono la frase "Tutto è un file su Linux / Unix". Tuttavia, quali sono le directory allora? In che cosa differiscono dai file?

Risposte:


22

Nota: originariamente questo è stato scritto per supportare la mia risposta per Perché la directory corrente nel lscomando è identificata come collegata a se stessa? ma ho sentito che questo è un argomento che merita di essere autonomo, e quindi questo D&R .

Comprensione del filesystem e dei file Unix / Linux: tutto è un inode

In sostanza, una directory è solo un file speciale, che contiene un elenco di voci e il loro ID.

Prima di iniziare la discussione, è importante fare una distinzione tra alcuni termini e capire cosa rappresentano veramente le directory e i file. Potresti aver sentito l'espressione "Tutto è un file" per Unix / Linux. Bene, ciò che gli utenti capiscono spesso come file è questo: /etc/passwd- Un oggetto con un percorso e un nome. In realtà, un nome (sia esso una directory o un file, o qualsiasi altra cosa) è solo una stringa di testo - una proprietà dell'oggetto reale. Tale oggetto è chiamato inode o I-number e memorizzato sul disco nella tabella degli inode. I programmi aperti hanno anche tabelle di inode, ma per ora non è questa la nostra preoccupazione.

La nozione di Unix di una directory è quella di Ken Thompson in un'intervista del 1989 :

... E poi alcuni di quei file erano directory che contenevano solo nome e numero I.

Un'interessante osservazione può essere fatta dal discorso di Dennis Ritchie del 1972 che

"... la directory non è in realtà altro che un file, ma i suoi contenuti sono controllati dal sistema e i contenuti sono nomi di altri file. (Una directory viene talvolta chiamata catalogo in altri sistemi.)"

... ma non si parla di inode in nessun punto del discorso. Tuttavia, il 1971 manuale su format of directoriesstati:

Il fatto che un file sia una directory è indicato da un bit nella parola flag della sua voce i-node.

Le voci della directory sono lunghe 10 byte. La prima parola è il nodo i — del file rappresentato dalla voce, se diverso da zero; se zero, la voce è vuota.

Quindi è stato lì fin dall'inizio.

L'accoppiamento di directory e inode è anche spiegato in Come vengono archiviate le strutture di directory nel filesystem UNIX? . una directory stessa è una struttura di dati, in particolare: un elenco di oggetti (file e numeri di inode) che puntano a elenchi relativi a tali oggetti (autorizzazioni, tipo, proprietario, dimensioni, ecc.). Quindi ogni directory contiene il proprio numero di inode, quindi i nomi dei file e i loro numeri di inode. Il più famoso è l' inode # 2 che è /directory . (Nota, comunque, /deve /runsono filesystem virtuali, quindi poiché sono cartelle root per il loro filesystem, hanno anche l'inode 2; cioè un inode è univoco sul proprio file system, ma con più filesystem collegati, si hanno inode non univoci). il diagramma preso in prestito dalla domanda collegata probabilmente lo spiega in modo più sintetico:

Directory-iNode-Block

Tutte le informazioni memorizzate nell'inode sono accessibili tramite stat()chiamate di sistema, come per Linux man 7 inode:

Ogni file ha un inode contenente metadati sul file. Un'applicazione può recuperare questi metadati utilizzando stat (2) (o chiamate correlate), che restituisce una struttura stat o statx (2), che restituisce una struttura statx.

È possibile accedere a un file solo conoscendo il suo numero di inode ( ref1 , ref2 )? Su alcune implementazioni Unix è possibile ma ignora i controlli di autorizzazione e accesso, quindi su Linux non è implementato e devi attraversare l'albero del filesystem (tramite find <DIR> -inum 1234ad esempio) per ottenere un nome file e il suo inode corrispondente.

A livello di codice sorgente, è definito nel sorgente del kernel Linux ed è anche adottato da molti filesystem che funzionano su sistemi operativi Unix / Linux, inclusi filesystem ext3 ed ext4 (impostazione predefinita di Ubuntu). Cosa interessante: con i dati che sono solo blocchi di informazioni, Linux ha in realtà la funzione inode_init_always che può determinare se un inode è una pipe ( inode->i_pipe). Sì, socket e pipe sono tecnicamente anche file - file anonimi, che potrebbero non avere un nome file sul disco. FIFO e socket Unix-Domain hanno nomi di file sul filesystem.

I dati stessi possono essere univoci, ma i numeri di inode non sono univoci. Se abbiamo un hard link per foo chiamato foobar, ciò indicherà anche l'inode 123. Questo inode stesso contiene informazioni su quali blocchi effettivi di spazio su disco sono occupati da quell'inode. E tecnicamente è possibile .essere collegati al nome del file della directory. Beh, quasi: non puoi creare hardlink per le directory su Linux , ma i filesystem possono consentire hard link alle directory in un modo molto disciplinato, il che rende un vincolo avere solo .e ..come hard link.

Albero delle directory

I filesystem implementano un albero di directory come una delle strutture dati dell'albero. In particolare,

  • ext3 ed ext4 usano HTree
  • xfs usa B + Tree
  • zfs usa l'hash tree

Il punto chiave qui è che le directory stesse sono nodi in un albero e le sottodirectory sono nodi figlio, con ogni figlio che ha un collegamento al nodo padre. Pertanto, per un collegamento di directory il conteggio degli inode è minimo 2 per una directory non elaborata (collegamento al nome della directory /home/example/e collegamento all'auto /home/example/.), e ogni sottodirectory aggiuntiva è un collegamento / nodo aggiuntivo:

# new directory has link count of 2
$ stat --format=%h .
2
# Adding subdirectories increases link count
$ mkdir subdir1
$ stat --format=%h .
3
$ mkdir subdir2
$ stat --format=%h .
4
# Count of links for root
$ stat --format=%h /
25
# Count of subdirectories, minus .
$ find / -maxdepth 1 -type d | wc -l
24

Il diagramma trovato sulla pagina del corso di Ian D. Allen mostra un diagramma semplificato molto chiaro:

WRONG - names on things      RIGHT - names above things
=======================      ==========================

    R O O T            --->         [etc,bin,home]   <-- ROOT directory
   /   |   \                         /    |      \
etc   bin   home       --->  [passwd]  [ls,rm]  [abcd0001]
 |   /   \    \                 |      /    \       |
 |  ls   rm  abcd0001  --->     |  <data>  <data>  [.bashrc]
 |               |              |                   |
passwd       .bashrc   --->  <data>                <data>

L'unica cosa nel diagramma DESTRO che è errata è che i file non sono tecnicamente considerati nella struttura ad albero della directory stessa: l'aggiunta di un file non ha effetti sul conteggio dei collegamenti:

$ mkdir subdir2
$ stat --format=%h .
4
# Adding files doesn't make difference
$ cp /etc/passwd passwd.copy
$ stat --format=%h .
4

Accedere alle directory come se fossero file

Per citare Linus Torvalds :

Il punto con "tutto è un file" non è che tu abbia un nome di file casuale (anzi, socket e pipe mostrano che "file" e "nome file" non hanno nulla a che fare l'uno con l'altro), ma il fatto che puoi usare il comune strumenti per operare su cose diverse.

Considerando che una directory è solo un caso speciale di un file, naturalmente ci devono essere API che ci consentono di aprirle / leggerle / scriverle / chiuderle in modo simile ai normali file.

È qui che dirent.hentra in gioco la libreria C, che definisce la direntstruttura, che puoi trovare in man 3 readdir :

   struct dirent {
       ino_t          d_ino;       /* Inode number */
       off_t          d_off;       /* Not an offset; see below */
       unsigned short d_reclen;    /* Length of this record */
       unsigned char  d_type;      /* Type of file; not supported
                                      by all filesystem types */
       char           d_name[256]; /* Null-terminated filename */
   };

Quindi, nel tuo codice C devi definire struct dirent *entry_p, e quando apriamo una directory con opendir()e iniziamo a leggerlo con readdir(), memorizzeremo ogni elemento in quella entry_pstruttura. Naturalmente, ogni elemento conterrà i campi definiti nel modello per direntmostrato sopra.

L'esempio pratico di come funziona può essere trovato nella mia risposta su Come elencare i file e i loro numeri di inode nella directory di lavoro corrente .

Si noti che il manuale POSIX su fdopen afferma che "[t] le voci della directory per punto e punto-punto sono opzionali" e che gli stati del manuale readdir struct dirent devono solo avere d_namee d_inocampi.

Nota sulla "scrittura" nelle directory: la scrittura in una directory sta modificando il suo "elenco" di voci. Quindi, la creazione o la rimozione di un file è direttamente associata alle autorizzazioni di scrittura della directory e l'aggiunta / rimozione di file è l'operazione di scrittura su detta directory.


2
Mi rifiuto di accettare i socket sono file;) "Tutto è accessibile come file" sarebbe più preciso?
Rinzwind,

@Rinzwind Bene, la frase "tutto è accessibile come file" è accurata. I file normali hanno open()e anche i read()socket hanno connect()e read(). Ciò che sarebbe più accurato è che "file" è realmente organizzato "dati" memorizzati su disco o memoria, e alcuni file sono anonimi - non hanno un nome file. Di solito gli utenti pensano ai file in termini di quell'icona sul desktop, ma non è l'unica cosa che esiste. Vedi anche unix.stackexchange.com/a/116616/85039
Sergiy Kolodyazhnyy

Bene, la domanda era più se una directory fosse un file. E questo è. I socket potrebbero quasi essere una domanda separata insieme ai tubi denominati FIFO.
WinEunuuchs2Unix

Bene, ho ricevuto una risposta sulle pipe finora: askubuntu.com/a/1074550/295286 Forse i FIFO saranno i prossimi
Sergiy Kolodyazhnyy,
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.