Come vengono creati i file Linux "/ dev"?


112

Ci sono file speciali in Linux che non sono realmente file.

Gli esempi più evidenti e chiari di questi sono nella devcartella, "file" come:

  • /dev/null - Ignora tutto ciò che scrivi nel file
  • /dev/random - Emette dati casuali invece del contenuto di un file
  • /dev/tcp - Invia tutti i dati che scrivi a questo file sulla rete

Prima di tutto, qual è il nome di questi tipi di "file" che sono davvero una sorta di script o binari mascherati?

Secondo, come vengono creati? Questi file sono integrati nel sistema a livello di kernel o c'è un modo per creare un "file magico" da soli (che ne dici di a /dev/rickroll)?


1
Non avevo idea di come taggare questa domanda, soprattutto perché non conosco il nome di quello che sto cercando. Sentiti libero di modificare qualsiasi tag pertinente.
IQAndreas,

15
A proposito, questa è una parte fondamentale della progettazione di sistemi operativi unix e unix-like: (quasi) tutto è un file o può essere fatto sembrare un file.
Cas

5
Vedi anche: mknod (2) man 2 mknod
RobertL

4
Questi sono "nodi dispositivo". Tuttavia quelli che hai citato - a differenza di quelli associati a dischi, tastiera, mouse, schede audio e altri dispositivi - sono i cosiddetti "pseudo-dispositivi", in quanto non sono dispositivi "reali" ed esistono solo nel kernel. È possibile crearne di nuovi, scrivendo un driver di dispositivo adatto e aggiungendolo al kernel (es. Uno pseudo-dispositivo per monitorare alcune attività sul computer). Prima che la directory / dev esistesse sul disco - oggigiorno è un file system virtuale (di tipo devfs) creato dal kernel.
Baard Kopperud,

10
Tutti i file, anche i file "reali", sono artefatti da software. Il software alla base di ogni dispositivo, di file, socket, file speciale, o qualcosa ancora da inventare fornisce una tabella di funzioni per gestire open(), read(), close(), ecc Dopo di che, è fino al software
waltinator

Risposte:


101

/dev/zeroè un esempio di "file speciale", in particolare un "nodo dispositivo". Normalmente questi vengono creati dal processo di installazione distro, ma si può assolutamente creare voi stessi, se si vuole.

Se chiedi lsdi /dev/zero:

# ls -l /dev/zero
crw-rw-rw- 1 root root 1, 5  Nov 5 09:34 /dev/zero

La "c" all'inizio indica che si tratta di un "dispositivo a caratteri"; l'altro tipo è "dispositivo a blocchi" (stampato da ls"b"). Molto approssimativamente, i dispositivi ad accesso casuale come i dischi rigidi tendono ad essere dispositivi a blocchi, mentre le cose sequenziali come le unità a nastro o la scheda audio tendono ad essere dispositivi a caratteri.

La parte "1, 5" è il "numero dispositivo principale" e il "numero dispositivo secondario".

Con queste informazioni, possiamo usare il mknodcomando per creare il nostro nodo dispositivo proprio:

# mknod foobar c 1 5

Questo crea un nuovo file chiamato foobar, nella cartella corrente, che fa esattamente la stessa cosa di /dev/zero. (Puoi ovviamente impostare diverse autorizzazioni su di esso se vuoi.) Tutto questo "file" contiene davvero i tre elementi sopra - tipo di dispositivo, numero maggiore, numero minore. È possibile utilizzare lsper cercare i codici per altri dispositivi e ricreare anche quelli. Quando ti annoi, basta usare rmper rimuovere i nodi del dispositivo che hai appena creato.

Fondamentalmente il numero maggiore indica al kernel Linux con quale driver di dispositivo parlare, e il numero minore indica al driver di dispositivo di quale dispositivo stai parlando. (Ad esempio, probabilmente hai un controller SATA, ma forse sono collegati più dischi rigidi.)

Se vuoi inventare nuovi dispositivi che fanno qualcosa di nuovo ... beh, dovrai modificare il codice sorgente per il kernel Linux e compilare il tuo kernel personalizzato. Quindi non facciamolo! :-) Ma puoi aggiungere file di dispositivo che duplicano quelli che hai già ottenuto bene. Un sistema automatizzato come udev è fondamentalmente solo alla ricerca di eventi del dispositivo e alla chiamata mknod/ rmper te automaticamente. Niente di più magico di così.

Esistono ancora altri tipi di file speciali:

  • Linux considera una directory come un tipo speciale di file. (Di solito non è possibile aprire direttamente una directory, ma se fosse possibile, si troverà che è un file normale che contiene dati in un formato speciale e dice al kernel dove trovare tutti i file in quella directory.)

  • Un link simbolico è un file speciale. (Ma un collegamento reale non lo è.) È possibile creare collegamenti simbolici utilizzando il ln -scomando. (Cerca la manpage per questo.)

  • C'è anche una cosa chiamata "pipe" o "FIFO" (coda first-in, first-out). Puoi crearne uno con mkfifo. Un FIFO è un file magico che può essere aperto da due programmi contemporaneamente: una lettura, una scrittura. Quando ciò accade, funziona come un normale tubo shell. Ma puoi avviare ogni programma separatamente ...

Un file che non è "speciale" in alcun modo è chiamato "file normale". Occasionalmente vedrai menzionarlo nella documentazione di Unix. Questo è ciò che significa; un file che non è un nodo dispositivo o un collegamento simbolico o altro. Solo un normale file di tutti i giorni senza proprietà magiche.


4
C'è anche un altro tipo di file speciale, un socket di dominio Unix associato al filesystem.
Brian Bi,

8
Se vuoi giocare mknod, corri cat /proc/devicesper vedere i numeri principali per tutti i driver. Il che ci porta a un altro tipo di file speciale del /procfile system ( questa risposta ne parla).
ugoren,

8
Altri Unices hanno inventato i loro file speciali, ad esempio Solaris aveva le porte .
Kevin,

6
Piccolo nitpick: non è necessario ricompilare il kernel per scrivere un nuovo dispositivo carattere / blocco :) crashcourse.ca/introduction-linux-kernel-programming/… Altrimenti questa è una risposta davvero buona, +1!
Comandante Coriandolo Salamander

1
@MathematicalOrchid: un passaggio mancante nella tua risposta (o almeno in modo implicito) è il fatto che quei file speciali non sono affatto script o binari mascherati della shell (come suggerito dalla domanda), ma piuttosto un'interfaccia per accedere alla funzionalità presente nel kernel del sistema operativo.
Dreamer,

34

La maggior parte delle /devvoci sono inode di dispositivi a blocchi o inode di dispositivi a caratteri. Wikipedia ha molti dettagli a riguardo, che non ho intenzione di ripetere.

Ma ciò /dev/tcpche è menzionato nella tua domanda non è spiegato da nessuna delle risposte esistenti. /dev/tcpe /dev/udpsono diversi dalla maggior parte delle altre /devvoci. I dispositivi a blocchi ea caratteri sono implementati dal kernel, ma /dev/tcpe /dev/udpsono implementati in modalità utente.

La shell bash è un programma che ha un'implementazione di /dev/tcpe /dev/udp(copiato da ksh93). Quando si tenta di aprire un percorso al di sotto di quelli con operatori di reindirizzamento bash, non eseguirà una normale openchiamata di sistema. Invece bash creerà un socket TCP e lo collegherà alla porta specificata.

Questo è implementato in modalità utente e solo in alcuni programmi come si può vedere nell'esempio seguente che dimostra la differenza tra lasciare bashe catprovare ad aprire/dev/tcp/::1/22

$ cat /dev/tcp/::1/22
cat: /dev/tcp/::1/22: No such file or directory
$ cat < /dev/tcp/::1/22
SSH-2.0-OpenSSH_6.6.1p1 Ubuntu-2ubuntu2.3

Una differenza con ksh93è che bashfarà solo quelle connessioni TCP con operatori di reindirizzamento, non negli altri punti in cui può aprire file come il sourceo .incorporato.


Inoltre, GNU ha creato gawkcasi simili in modo simile /inet{,4,6}/{tcp,udp}/$port/$remote/$rport, da qualche parte intorno al 2010 (non ricordo esattamente e non riesco a trovare le note di rilascio).
dave_thompson_085,

6
IMO, un modo migliore per affermare il punto /dev/tcpè che NON è un file. Non esiste mai un file chiamato questo. La sintassi di Bash per l'apertura di socket utilizza la stringa /dev/tcp/addresscome un nome file, ma chiamarla "file implementato nello spazio utente" suona semplicemente strana. Interessante che kshagganci quei nomi di file per tutto, non solo per i reindirizzamenti, però. Questo è più vicino a "implementare un file".
Peter Cordes,

@PeterCordes credo che UWIN li configuri come file effettivi. e penso che 3dfs faccia lo stesso. ricorda, bashcopiato solo questo comportamento, ma ha origine altrove.
Mikeserv,

19

Oltre ai nodi del dispositivo spiegati in altre risposte (creati con mknod (2) o forniti da alcuni devfs ), Linux ha altri file "magici" forniti da speciali file system virtuali , in particolare in /proc/(vedi proc (5) , leggi su procfs ) e in /sys/(leggi su sysfs ).

Questi pseudo file (che appaiono -eg a stat (2) - come file ordinari, non come dispositivi) sono una vista virtuale fornita dal kernel; in particolare, la lettura da /proc/(ad es. con cat /proc/$$/mapso con (2) -ing aperto/proc/self/status nel programma) generalmente non comporta alcun I / O fisico da disco o rete, quindi è abbastanza veloce.

Per creare qualche pseudo-file aggiuntivo in /proc/te generalmente dovresti scrivere il tuo modulo kernel e caricarlo (vedi ad esempio questo ).


3
AFAIK le informazioni sull'estensione / proc non sono aggiornate. Sebbene tecnicamente possibile, / proc (o meglio procfs) dovrebbe contenere solo informazioni sui processi in esecuzione. Tutti gli altri pseudo-file, inclusi quelli che contengono informazioni di runtime o opzioni di configurazione per il kernel, dovrebbero andare in / sys (sysfs). Ci sono ancora alcuni pseudo file non correlati al processo in / proc (es. Meminfo, cpuinfo) per motivi di compatibilità, ma i nuovi pseudo file dovrebbero andare in sysfs.
Dreamer,

13

Si chiamano nodi dispositivo e vengono creati manualmente con mknodo automaticamente da udev. Sono in genere interfacce simili a file per caratteri o blocchi di dispositivi con driver nel kernel, ad esempio i dischi sono dispositivi a blocchi, tty e porte seriali, ecc. Sono dispositivi a caratteri.

Esistono anche altri tipi di file "speciali", tra cui named pipe e fifos e socket.


9

Come altri utenti hanno già spiegato in dettaglio, i file speciali richiedono il codice per eseguirne il backup. Tuttavia, nessuno sembra aver menzionato che Linux offre diversi modi per scrivere quel codice nello spazio utente:

A. FUSE (Filesystem in USErspace) ti permette di scrivere qualcosa come /procsenza il rischio di crash del kernel e farlo in una lingua / runtime a tua scelta, come Go , Node.js , Perl , PHP , Python , Ruby , Rust , ecc .

Ha anche il vantaggio che i filesystem FUSE possono essere montati senza sudoperché funzionano come l'utente che esegue il montaggio.

Ecco alcuni esempi di cose che le persone hanno scritto usando FUSE:

  • mp3fs (Visualizza i tuoi file FLAC come file MP3 che vengono creati al volo quando li copi / fai clic e li trascini sul tuo lettore MP3)
  • PyTagsFS (Visualizza i tuoi file multimediali in un albero di cartelle virtuali create dai tag dei metadati)
  • fuse-zip (Montare i file Zip come cartelle)
  • FuseISO (montare ISO senza permessi di root)
  • iFUSE (Montare iDevices)
  • FuseDAV (montare condivisioni WebDAV)
  • fuse-exfat (Montare filesystem formattati exFAT)
  • ntfs-3g ( il driver NTFS Linux)

B. Se vuoi creare un dispositivo di input virtuale come tastiera, mouse, joystick, ecc. (Es. Per scrivere un driver libusbdi spazio utente per un dispositivo USB con cui stai parlando ), c'è uinput .

I collegamenti per questo sono più difficili da trovare, ma so che esistono per Go (solo tastiera), Python e Ruby (2) .

Esempi di utilizzo di uinput nel mondo reale includono:

  • G15Daemon (driver Linux per LCD e tasti di gioco sulle tastiere da gioco Logitech G15)
  • ds4drv (Driver per controller Sony DualShock 4)
  • xboxdrv ( driver alternativo del controller XBox 360 e Linux equivalente a x360ce giochi così mal progettati come Runner2: Future Legend of Rhythm Alien può pensare che stiano parlando con un vero controller XBox quando non lo sono)
  • I vecchi driver Wiimote come cwiid erano richiesti prima che qualcuno finalmente scrivesse un driver Wiimote del kernel in modo che il supporto fosse disponibile di default.

C. Per i dispositivi a caratteri generici, c'è CUSE (Dispositivi a caratteri in USErspace). È molto meno popolare però.

L'unico utente delle API CUSE che io sono personalmente a conoscenza è lo stesso programma che ha spinto la sua creazione: osspd , che implementa /dev/dsp, /dev/adspe /dev/mixer(l'API audio OSS) nello spazio utente in modo che possano essere instradati attraverso PulseAudio o dmix.

L'unico legame CUSE che sono riuscito a trovare è cusepy , che non è stato aggiornato dal 2010.

D. Potrebbe non essere necessario un nuovo file speciale.

Ad esempio, è possibile aprire una comunicazione non elaborata con qualsiasi dispositivo USB utilizzando libusb (Elenco di associazioni nella pagina) e quindi comunicare con altri programmi tramite altri meccanismi (socket TCP / UDP, lettura / scrittura di stdin / stdout o file regolari su disco , eccetera.).


1
cusepy potrebbe non essere stato aggiornato da un po '(in realtà, non è mai stato aggiornato; ha solo un commit!), ma avendo appena scritto un dispositivo a caratteri usando cusepy poche settimane fa, posso confermare che funziona ancora bene. Mancavano alcune funzioni relative all'implementazione poll, ma dal momento che cusepy utilizza ctypes e le associazioni sono autogenerate in base ai file di intestazione C, correggere eventuali funzioni mancanti è solo una questione di aggiungere il nome della funzione desiderata all'elenco delle funzioni esportate in setup.py.
Aleksi Torhamo,

1
Un altro esempio interessante dell'uso di FUSE è sshfs . Ti permette di sfogliare il file system remoto come se fosse locale usando la connessione SSH sottostante.
Mr. Deathless,

@ Mr.Deathless Sì. In realtà lo uso e intendevo menzionarlo ma me ne sono dimenticato.
ssokolow,

6

Il libro Linux Device Driver (altamente raccomandato) lo spiega dettagliatamente e ti ha persino creato un modulo del kernel che lo fa come esempio, ma in breve, ogni driver di dispositivo ha funzioni specifiche che vengono chiamate quando un file viene aperto, chiuso , letto, scritto, ecc. I file "speciali" fanno semplicemente qualcosa di speciale all'interno di quelle funzioni, invece di accedere all'hardware di archiviazione su un disco.

Ad esempio, la funzione di scrittura per /dev/nullproprio non fa nulla, ignorando i byte. La funzione di lettura per /dev/randomrestituisce un numero casuale.


1

mount -t devtmpfs

È anche interessante vedere che nei sistemi moderni, /devnormalmente è un tipo di filesystem che può essere montato dove vuoi. Ubuntu 16.04:

mkdir d
sudo mount -t devtmpfs none d
head -c 10 d/random
sudo umount d

Questo è abilitato da CONFIG_DEVTMPFS=ye consente al kernel stesso di creare e distruggere i file del dispositivo secondo necessità.

CONFIG_DEVTMPFS_MOUNT=y

Questa opzione consente al kernel di montare automaticamente devtmpfs /dev.

drivers/base/Kconfig documenti:

config DEVTMPFS_MOUNT
    bool "Automount devtmpfs at /dev, after the kernel mounted the rootfs"
    depends on DEVTMPFS
    help
      This will instruct the kernel to automatically mount the
      devtmpfs filesystem at /dev, directly after the kernel has
      mounted the root filesystem. The behavior can be overridden
      with the commandline parameter: devtmpfs.mount=0|1.
      This option does not affect initramfs based booting, here
      the devtmpfs filesystem always needs to be mounted manually
      after the rootfs is mounted.
      With this option enabled, it allows to bring up a system in
      rescue mode with init=/bin/sh, even when the /dev directory
      on the rootfs is completely empty.

file_operations

Infine, dovresti creare il tuo modulo del kernel del dispositivo per vedere esattamente cosa sta succedendo.

Ecco un esempio eseguibile minimo: Comprensione dei file del dispositivo di caratteri (o caratteri speciali)

Il passo più importante è impostare la file_operationsstruttura, ad esempio:

static const struct file_operations fops = {
    .owner = THIS_MODULE,
    .read = read,
    .open = open,
};

static int myinit(void)
{
    major = register_chrdev(0, NAME, &fops);
    return 0;
}

che contiene i puntatori a funzione che vengono chiamati per ogni chiamata di sistema relativa ai file.

Diventa quindi ovvio che si sovrascrivono quelle chiamate di sistema relative ai file per fare quello che vuoi, ed è così che il kernel implementa i dispositivi come /dev/zero.

Crea /devvoci automaticamente senzamknod

Il mistero finale è come il kernel crea automaticamente le /devvoci.

Il meccanismo può essere osservato creando un modulo del kernel che lo fa come mostrato a: https://stackoverflow.com/questions/5970595/how-to-create-a-device-node-from-the-init-module- code-of-a-linux-kernel-module / 45531867 # 45531867 e arriva a una device_createchiamata.


In OpenBSD c'è uno script MAKEDEV che semplifica un po 'questo, vedi man.openbsd.org/MAKEDEV.8 Non sono sicuro del perché Linux non ce l'abbia, tranne che è molto più complicato. Forse le parti potrebbero essere adattate. Puoi dire MKNOD tty per esempio e gestisce i dettagli.
Alan Corey,
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.