Come sono implementati i canali I / O nel kernel Linux?


8

stdin, stdout, stderr sono alcuni numeri interi che si indicizzano in una struttura di dati che "conosce" quali canali I / O devono essere utilizzati per il processo. Comprendo che questa struttura di dati è unica per ogni processo. I canali I / O non sono altro che alcune strutture di array di dati con allocazione dinamica della memoria?


per canali I / O intendi stream o pipe? inoltre, questo probabilmente varia a seconda del kernel. probabilmente dovrai chiedere informazioni su un kernel specifico.
strugee,

1
@strugee Sto parlando del kernel Linux. Intendevo stream per canali I / O. Come vengono implementati questi flussi in Linux? qualche array o qualcosa del genere?
KawaiKx,

Risposte:


14

In Unix-like sistemi operativi, i flussi di ingresso, di uscita e di errore standard sono identificati dai descrittori di file 0, 1, 2. Su Linux, questi sono visibili nel procfilesystem in /proc/[pid]/fs/{0,1,2}. Questi file sono in realtà collegamenti simbolici a un dispositivo pseudoterminale nella /dev/ptsdirectory.

Uno pseudoterminal (PTY) è una coppia di dispositivi virtuali, uno pseudoterminal master (PTM) e uno pseudoterminal slave (PTS) (collettivamente definiti come una coppia pseudoterminal ), che forniscono un canale IPC, un po 'come un tubo bidirezionale tra un programma che prevede essere collegato a un dispositivo terminale e un programma driver che utilizza lo pseudoterminale per inviare input e ricevere input dal precedente programma.

Un punto chiave è che lo schiavo pseudoterminale appare proprio come un terminale normale, ad esempio può essere commutato tra modalità canonica e non canonica (impostazione predefinita), in cui interpreta determinati caratteri di input, come generare un SIGINTsegnale quando un carattere di interruzione (normalmente generato premendo Ctrl+ Csulla tastiera) viene scritto sul master pseudoterminale o fa sì che il successivo read()ritorni 0quando viene rilevato un carattere di fine file (normalmente generato da Ctrl+ D). Altre operazioni supportate dai terminali sono l'attivazione o la disattivazione dell'eco, l'impostazione del gruppo di processi in primo piano, ecc.

Gli pseudoterminali hanno una serie di usi:

  • Consentono a programmi come sshfar funzionare programmi orientati al terminale su un altro host collegato tramite una rete. Un programma orientato al terminale può essere qualsiasi programma, che normalmente verrebbe eseguito in una sessione terminale interattiva. L'input, l'output e l'errore standard di tale programma non possono essere collegati direttamente alla presa, poiché le prese non supportano la funzionalità relativa al terminale sopra menzionata.

  • Consentono a programmi come expectguidare un programma interattivo orientato al terminale da uno script.

  • Sono utilizzati da emulatori di terminali tali da xtermfornire funzionalità relative ai terminali.

  • Sono utilizzati da programmi come screenmultiplexare un singolo terminale fisico tra più processi.

  • Sono utilizzati da programmi come scriptper registrare tutti gli input e output che si verificano durante una sessione di shell.

I PTY in stile Unix98 , utilizzati in Linux, sono configurati come segue:

  • Il programma del driver apre il multiplexer master pseudo-terminale su dev/ptmx, sul quale riceve un descrittore di file per un PTM e un dispositivo PTS viene creato nella /dev/ptsdirectory. Ogni descrittore di file ottenuto aprendo /dev/ptmxè un PTM indipendente con il proprio PTS associato.

  • Il programma del driver chiama fork()per creare un processo figlio, che a sua volta esegue i seguenti passaggi:

    • Il bambino chiama setsid()per iniziare una nuova sessione, di cui il bambino è il leader della sessione. Questo fa sì che il bambino perda il suo terminale di controllo .

    • Il bambino procede all'apertura del dispositivo PTS corrispondente al PTM creato dal programma del driver. Poiché il bambino è un leader della sessione, ma non ha un terminale di controllo, il PTS diventa il terminale di controllo del bambino.

    • Il bambino usa dup()per duplicare il descrittore di file per il dispositivo slave su di esso input, output ed errore standard.

    • Infine, il bambino chiama exec()per avviare il programma orientato al terminale che deve essere collegato al dispositivo pseudoterminale.

A questo punto, tutto ciò che il programma del driver scrive sul PTM, appare come input per il programma orientato al terminale sul PTS e viceversa.

Quando si opera in modalità canonica, l'ingresso nel PTS viene bufferizzato riga per riga. In altre parole, proprio come con i terminali regolari, la lettura del programma da un PTS riceve una riga di input solo quando un carattere di nuova riga viene scritto nel PTM. Quando la capacità di buffering è esaurita, ulteriori write()chiamate si bloccano fino a quando parte dell'input è stata consumata.

Nel kernel di Linux, le chiamate relativi file system open(), read(), write() stat()ecc sono implementati nello strato virtuale Filesystem (VFS), che fornisce una divisa interfaccia del file system per i programmi userspace. VFS consente a diverse implementazioni di file system di coesistere all'interno del kernel. Quando i programmi di userspace chiamano le sopracitate chiamate di sistema, VFS reindirizza la chiamata all'implementazione del filesystem appropriata.

I dispositivi PTS in uso /dev/ptssono gestiti dall'implementazione del devptsfile system definita in /fs/devpts/inode.c, mentre il driver TTY che fornisce il ptmxdispositivo in stile Unix98 è definito in drivers/tty/pty.c.

Il buffering tra i dispositivi TTY e le discipline di linea TTY , come gli pseudoterminali, viene fornita una struttura di buffer mantenuta per ciascun dispositivo tty, definito ininclude/linux/tty.h

Prima della versione 3.7 del kernel, il buffer era un flip buffer :

#define TTY_FLIPBUF_SIZE 512

struct tty_flip_buffer {
        struct tq_struct tqueue;
        struct semaphore pty_sem;
        char             *char_buf_ptr;
        unsigned char    *flag_buf_ptr;
        int              count;
        int              buf_num;
        unsigned char    char_buf[2*TTY_FLIPBUF_SIZE];
        char             flag_buf[2*TTY_FLIPBUF_SIZE];
        unsigned char    slop[4];
};

La struttura conteneva spazio di archiviazione diviso in due buffer di uguale dimensione. I buffer sono stati numerati 0(prima metà di char_buf/flag_buf) e 1(seconda metà). Il driver ha archiviato i dati nel buffer identificato da buf_num. L'altro buffer potrebbe essere scaricato nella disciplina di linea.

Il buffer è stato "capovolto" alternando buf_numtra 0e 1. Quando buf_numcambiato, char_buf_ptre flag_buf_ptr era impostato all'inizio del buffer identificato da buf_num, ed countera impostato 0.

Dalla versione 3.7 del kernel i buffer di vibrazione TTY sono stati sostituiti con oggetti allocati tramite kmalloc()organizzati in anelli . In una situazione normale per una porta seriale guidata da IRQ a velocità tipiche, il loro comportamento è più o meno lo stesso del vecchio flip buffer; due buffer finiscono per essere allocati e il kernel scorre ciclicamente tra loro come prima. Tuttavia, quando si verificano ritardi o aumenta la velocità, la nuova implementazione del buffer funziona meglio poiché il pool di buffer può aumentare leggermente.


Risposte tanto informate quanto estremamente difficili da trovare.
étale-cohomology

-1

Dalle pagine man di uno dei tre spiega la risposta:

   Under  normal circumstances every UNIX program has three streams opened
   for it when it starts up, one for input, one for output,  and  one  for
   printing diagnostic or error messages.  These are typically attached to
   the user's terminal but might instead  refer  to  files  or
   other  devices,  depending  on what the parent process chose to set up.

   The input stream is referred to as "standard input"; the output  stream
   is  referred  to as "standard output"; and the error stream is referred
   to as "standard error".  These terms are abbreviated to form  the  sym-
   bols used to refer to these files, namely stdin, stdout, and stderr.

   Each  of these symbols is a stdio(3) macro of type pointer to FILE, and
   can be used with functions like fprintf(3) or fread(3).

   Since FILEs are a buffering wrapper around UNIX file  descriptors,  the
   same  underlying  files  may  also  be accessed using the raw UNIX file
   interface, that is, the functions like read(2) and lseek(2).

   On program startup, the integer file descriptors  associated  with  the
   streams  stdin,  stdout, and stderr are 0, 1, and 2, respectively.  The
   preprocessor symbols STDIN_FILENO, STDOUT_FILENO, and STDERR_FILENO are
   defined  with  these values in <unistd.h>.

Questa risposta descrive l'attuazione di stdin, stdoute stderrdal punto di vista della libreria C, ma la domanda è esplicitamente circa l'attuazione del kernel. Ho cercato di spiegare il punto di vista del kernel nella mia risposta .
Thomas Nyman,
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.