In che modo Linux distingue tra file reali e inesistenti (ad esempio: dispositivo)?


28

Questa è una domanda di livello piuttosto basso e capisco che potrebbe non essere il posto migliore da porre. Ma sembrava più appropriato di qualsiasi altro sito SE, quindi ecco qui.

So che sul filesystem Linux esistono alcuni file , per esempio: ce n'è /usr/bin/bashuno che esiste. Tuttavia, (per quanto ho capito bene), un po 'anche in realtà non esiste in quanto tale e sono più virtuali file, ad esempio: /dev/sda, /proc/cpuinfo, ecc Le mie domande sono (sono due, ma anche strettamente correlati per essere questioni separate):

  • Come funziona il kernel Linux se questi file sono reali (e quindi li leggono dal disco) oppure no quando viene emesso un comando di lettura (o simile)?
  • Se il file non è reale: ad esempio, una lettura da /dev/randomrestituirà dati casuali e una lettura da /dev/nullrestituirà EOF. Come funziona quali dati leggere da questo file virtuale (e quindi cosa fare quando / se anche i dati scritti nel file virtuale) - esiste una sorta di mappa con puntatori per separare i comandi di lettura / scrittura appropriati per ciascun file, o anche per la directory virtuale stessa? Quindi, una voce per /dev/nullpotrebbe semplicemente restituire un EOF.

1
Quando viene creato il file, il kernel registra il suo tipo. I normali file su disco vengono quindi trattati in modo diverso da collegamenti simbolici, dispositivi a blocchi, dispositivi a caratteri, directory, socket, FIFO, ecc. È compito del kernel sapere.
Jonathan Leffler,

vedere l'uomo pge per mknod
Jasen,

È un po 'come chiedere "come fa un interruttore della luce a sapere se la luce è accesa?" L'interruttore della luce ha il compito di decidere se la luce è accesa.
Lightness Races con Monica il

Risposte:


25

Quindi ci sono sostanzialmente due diversi tipi di cose qui:

  1. File system normali, che contengono file in directory con dati e metadati, in modo familiare (inclusi collegamenti software, collegamenti fisici e così via). Questi sono spesso, ma non sempre, supportati da un dispositivo a blocchi per l'archiviazione persistente (un tmpfs vive solo nella RAM, ma è altrimenti identico a un normale filesystem). La semantica di questi è familiare; leggi, scrivi, rinomina e così via, funzionano tutti come ti aspetti.
  2. File system virtuali, di vario tipo. /proce /syssono esempi qui, così come i filesystem personalizzati FUSE come sshfso ifuse. C'è molta più diversità in questi, perché in realtà si riferiscono solo a un filesystem con semantica che sono in qualche modo "personalizzati". Pertanto, quando leggi da un file in /proc, non stai effettivamente accedendo a un dato dato che è stato memorizzato da qualcos'altro che lo scrive in precedenza, come in un normale filesystem. In pratica stai facendo una chiamata al kernel, richiedendo alcune informazioni che vengono generate al volo. E questo codice può fare tutto ciò che gli piace, dato che è solo una funzione da qualche parte che implementa la readsemantica. Quindi, hai lo strano comportamento dei file sotto /proc, come per esempio fingere di essere collegamenti simbolici quando non sono "

La chiave è che in /devrealtà, di solito, è del primo tipo. È normale nelle distribuzioni moderne /devessere qualcosa di simile a un tmpfs, ma nei sistemi più vecchi, era normale avere una directory semplice su disco, senza attributi speciali. La chiave è che i file sotto /devsono nodi di dispositivo, un tipo di file speciale simile a FIFO o socket Unix; un nodo di dispositivo ha un numero maggiore e minore, e leggerli o scriverli sta facendo una chiamata a un driver del kernel, proprio come leggere o scrivere un FIFO sta chiamando il kernel per bufferizzare l'output in una pipe. Questo driver può fare quello che vuole, ma di solito tocca l'hardware in qualche modo, ad esempio per accedere a un disco rigido o riprodurre l'audio negli altoparlanti.

Per rispondere alle domande originali:

  1. Ci sono due domande rilevanti per sapere se il 'file esiste' o no; questi sono se il file del nodo del dispositivo esiste letteralmente e se il codice del kernel che lo supporta è significativo. Il primo viene risolto come qualsiasi cosa su un normale filesystem. I sistemi moderni usano udevo qualcosa del genere per controllare gli eventi hardware e creare e distruggere automaticamente i nodi del dispositivo di /devconseguenza. Ma i sistemi più vecchi, o build personalizzate leggere, possono semplicemente avere tutti i loro nodi del dispositivo letteralmente sul disco, creati in anticipo. Nel frattempo, quando leggi questi file, stai facendo una chiamata al codice del kernel che è determinato dai numeri di dispositivo maggiori e minori; se questi non sono ragionevoli (ad esempio, stai provando a leggere un dispositivo a blocchi che non esiste), otterrai solo un qualche tipo di errore I / O.

  2. Il modo in cui determina quale codice del kernel chiamare per quale file del dispositivo varia. Per file system virtuali come /proc, implementano i propri reade le writefunzioni; il kernel chiama semplicemente quel codice a seconda del punto di mount in cui si trova e l'implementazione del filesystem si occupa di tutto il resto. Per i file del dispositivo, viene inviato in base ai numeri di dispositivo principali e secondari.


Quindi, se, diciamo, un vecchio sistema fosse alimentato, i file /devsarebbero ancora lì, ma immagino che verrebbero cancellati all'avvio del sistema?
Joe,

2
Se un vecchio sistema (uno senza creazione dinamica di un dispositivo) fosse spento, normalmente o in modo anomalo, i nodi del dispositivo rimarrebbero sul disco proprio come qualsiasi altro file. Quindi, al prossimo avvio, rimarrebbero anche sul disco e potresti usarli normalmente. È solo nei sistemi moderni che succede qualcosa di speciale che crea e distrugge i nodi del dispositivo.
Tom Hunt,

Quindi un sistema più moderno che non utilizza un tmpfsli creerebbe e cancellerebbe dinamicamente secondo necessità, ad esempio: avvio e spegnimento?
Joe,

3
devtmpfs, il /devfilesystem in Linux moderno, è simile a a tmpfs, ma ha alcune differenze da supportare udev. (Il kernel esegue autonomamente la creazione di un nodo prima di passare a udev, al fine di rendere l'avvio meno complicato.) In tutti questi casi, i nodi dispositivo vivono solo nella RAM e vengono creati e distrutti dinamicamente quando l'hardware li richiede. Presumibilmente potresti anche usarlo udevsu un normale disco fisso /dev, ma non l'ho mai visto fatto e non sembrano esserci buoni motivi per farlo.
Tom Hunt,

17

Ecco un elenco di file /dev/sda1sul mio server Arch Linux quasi aggiornato:

% ls -li /dev/sda1
1294 brw-rw---- 1 root disk 8, 1 Nov  9 13:26 /dev/sda1

Quindi la voce della directory in /dev/for sdaha un numero di inode, 1294. È un vero file su disco.

Guarda dove appare di solito la dimensione del file. Viene invece visualizzato "8, 1". Questo è un numero di dispositivo maggiore e minore. Nota anche la 'b' nelle autorizzazioni del file.

Il file /usr/include/ext2fs/ext2_fs.hcontiene questa (frammento) C struct:

/*
 * Structure of an inode on the disk
 */
struct ext2_inode {
    __u16   i_mode;     /* File mode */

Quella struttura ci mostra la struttura su disco dell'inode di un file. Molte cose interessanti sono in quella struttura; date un'occhiata.

L' i_modeelemento struct ext2_inodeha 16 bit e utilizza solo 9 per le autorizzazioni utente / gruppo / altro, lettura / scrittura / esecuzione e altri 3 per setuid, setgid e sticky. Ha 4 bit per differenziare tipi come "file normale", "link", "directory", "named pipe", "Unix family socket" e "block device".

Il kernel Linux può seguire il solito algoritmo di ricerca delle directory, quindi prendere una decisione in base alle autorizzazioni e ai flag i_modenell'elemento. Per "b", blocca i file di dispositivo, può trovare i numeri di dispositivo principali e secondari e, tradizionalmente, utilizzare il numero di dispositivo principale per cercare un puntatore ad alcune funzioni del kernel (un driver di dispositivo) che si occupano dei dischi. Il numero di dispositivo secondario viene solitamente utilizzato, ad esempio, il numero di dispositivo del bus SCSI o il numero di dispositivo EIDE o qualcosa del genere.

Alcune altre decisioni su come gestire un file simile /proc/cpuinfovengono prese in base al tipo di file system. Se fai un:

% mount | grep proc 
proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)

puoi vedere che /procha il tipo di file system "proc". La lettura da un file in /procfa sì che il kernel faccia qualcosa di diverso in base al tipo di file system, così come l'apertura di un file su un file system ReiserFS o DOS indurrebbe il kernel a utilizzare diverse funzioni per individuare i file e individuare i dati del File.


Sei sicuro che solo "file reali su disco" hanno un numero di inode visualizzato? Capisco 4026531975 -r--r--r-- 1 root root 0 Nov 14 18:41 /proc/mdstatche chiaramente non è un "file reale".
Guntbert,

7

Alla fine della giornata sono tutti file per Unix, questa è la bellezza dell'astrazione.

Il modo in cui i file vengono gestiti dal kernel, ora questa è una storia diversa.

/ proc e oggigiorno / dev e / run (aka / var / run) sono filesystem virtuali nella RAM. / proc è un'interfaccia / windows per le variabili e le strutture del kernel.

Consiglio di leggere The Linux Kernel http://tldp.org/LDP/tlk/tlk.html e Linux Device Drivers, Third Edition https://lwn.net/Kernel/LDD3/ .

Mi è piaciuto anche Il design e l'implementazione del sistema operativo FreeBSD http://www.amazon.com/Design-Implementation-FreeBSD-Operating-System/dp/0321968972/ref=sr_1_1

Dai un'occhiata alla pagina pertinente relativa alla tua domanda.

http://www.tldp.org/LDP/tlk/dd/drivers.html


grazie, ho leggermente modificato la prima domanda dopo averlo commentato.
Joe,

Leggi l'ultimo commento per favore.
Rui F Ribeiro,

5

Oltre alle risposte di @ RuiFRibeiro e @ BruceEdiger, la distinzione che fai non è esattamente la distinzione che il kernel fa. In realtà, hai vari tipi di file: file regolari, directory, collegamenti simbolici, dispositivi, socket (e ne dimentico sempre alcuni, quindi non cercherò di fare un elenco completo). Puoi avere le informazioni sul tipo di un file con ls: è il primo carattere sulla riga. Per esempio:

$ls -la /dev/sda
brw-rw---- 1 root disk 8, 0 17 nov.  08:29 /dev/sda

La "b" all'inizio indica che questo file è un dispositivo a blocchi. Un trattino, indica un file normale, "l" un collegamento simbolico e così via. Questa informazione è memorizzata nei metadati del file ed è accessibile statad esempio tramite la chiamata di sistema , quindi il kernel può leggere diversamente un file e un collegamento simbolico, ad esempio.

Quindi, fai un'altra distinzione tra "file reali" come /bin/bashe "file virtuali" come /proc/cpuinfoma lssegnala entrambi come file normali, quindi la differenza è di un altro tipo:

ls -la /proc/cpuinfo /bin/bash
-rwxr-xr-x 1 root root  829792 24 août  10:58 /bin/bash
-r--r--r-- 1 root wheel      0 20 nov.  16:50 /proc/cpuinfo

Quello che succede è che appartengono a diversi filesystem. /procè il punto di montaggio di uno pseudo-filesystem procfsmentre si /bin/bashtrova su un normale filesystem su disco. Quando Linux apre un file (lo fa in modo diverso a seconda del filesystem), popola una struttura di dati fileche ha, tra gli altri attributi, una struttura di diversi puntatori di funzioni che descrivono come usare questo file. Pertanto, può implementare comportamenti distinti per diversi tipi di file.

Ad esempio, queste sono le operazioni pubblicizzate da /proc/meminfo:

static int meminfo_proc_open(struct inode *inode, struct file *file)
{
    return single_open(file, meminfo_proc_show, NULL);
}

static const struct file_operations meminfo_proc_fops = {
    .open       = meminfo_proc_open,
    .read       = seq_read,
    .llseek     = seq_lseek,
    .release    = single_release,
};

Se osservi la definizione di meminfo_proc_open, puoi vedere che questa funzione popola un buffer in memoria con le informazioni restituite dalla funzione meminfo_proc_show, il cui compito è quello di raccogliere dati sull'utilizzo della memoria. Queste informazioni possono quindi essere lette normalmente. Ogni volta che si apre il file, la funzione meminfo_proc_openviene chiamata e le informazioni sulla memoria vengono aggiornate.


3

Tutti i file in un file system sono "reali", nel senso che consentono l'I / O dei file. Quando si apre un file, il kernel crea un descrittore di file, che è un oggetto (nel senso della programmazione orientata agli oggetti) che agisce come un file. Se leggi il file, il descrittore di file esegue il suo metodo di lettura, che a sua volta richiederà al file system (sysfs, ext4, nfs, ecc.) I dati dal file. I file system presentano un'interfaccia uniforme allo spazio utente e sanno cosa fare per gestire le letture e le scritture. I file system a loro volta chiedono ad altri layer di gestire le loro richieste. Per un file normale su un file system ext4, ciò comporterà ricerche nelle strutture di dati del file system (che possono comportare letture del disco), e infine una lettura dal disco (o cache) per copiare i dati nel buffer di lettura. Per un file in dire sysfs, generalmente sprintf () s qualcosa nel buffer. Per un nodo dev blocco, chiederà al driver del disco di leggere alcuni blocchi e copiarli nel buffer (i numeri maggiore e minore indicano al file system a quale driver effettuare le richieste).

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.