HOWTO · C
La funzione dup2 in C
In questo articolo viene illustrato come utilizzare la funzione dup2 in C
In questa pagina
In C POSIX, dup2(oldfd, newfd) fa sì che newfd si riferisca alla stessa descrizione di file aperta di oldfd. Si usa spesso per reindirizzare l’input o l’output standard prima di leggere da o scrivere in un file. Controlla il valore restituito: in caso di errore la chiamata restituisce -1 e imposta errno.
Cosa fa dup2 e quando usarla
La dichiarazione è int dup2(int oldfd, int newfd); ed è disponibile tramite <unistd.h>. In caso di successo, restituisce newfd. Se newfd è già aperto, dup2 lo chiude e riutilizza quel descrittore. La sostituzione è atomica rispetto all’operazione di chiusura e duplicazione, quindi è utile per il reindirizzamento anche in codice eseguito insieme ad altri thread.
I due descrittori si riferiscono alla stessa descrizione di file aperta. Condividono quindi l’offset del file e i flag di stato del file: una lettura o scrittura tramite uno dei due fa avanzare lo stesso offset. I flag del descrittore, invece, appartengono al singolo descrittore; quello nuovo creato da dup2 non eredita FD_CLOEXEC da oldfd.
Se oldfd e newfd sono uguali, dup2 restituisce quel descrittore senza chiuderlo. Il descrittore deve essere già valido. I numeri dei descrittori sono specifici del processo e dipendono dai descrittori aperti: non dare per scontato che l’apertura di un file restituisca sempre il descrittore 3 o che ogni processo ne abbia esattamente tre aperti.
Reindirizzare l’output standard con dup2
Apri una destinazione in scrittura, quindi duplicane il descrittore su STDOUT_FILENO. Questo programma compatto reindirizza l’output verso un file temporaneo, ripristina l’output standard originale e stampa la riga acquisita, così da rendere visibile il risultato. Usa funzioni POSIX per i file temporanei e i descrittori.
Esegui questo esempio:
Il codice e le etichette o gli output visibili in questa sezione restano in inglese.
#define _POSIX_C_SOURCE 200809L
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void) {
char path[] = "/tmp/dup2-demo-XXXXXX";
int file = mkstemp(path);
if (file == -1) return 1;
int saved_stdout = dup(STDOUT_FILENO);
if (saved_stdout == -1) {
close(file);
unlink(path);
return 1;
}
if (dup2(file, STDOUT_FILENO) == -1) {
close(saved_stdout);
close(file);
unlink(path);
return 1;
}
int write_ok = puts("Hello through dup2") != EOF && fflush(stdout) == 0;
int restore_ok = dup2(saved_stdout, STDOUT_FILENO) != -1;
close(saved_stdout);
if (!write_ok || !restore_ok) {
close(file);
unlink(path);
return 1;
}
if (lseek(file, 0, SEEK_SET) == (off_t)-1) {
close(file);
unlink(path);
return 1;
}
char buffer[64];
ssize_t length = read(file, buffer, sizeof(buffer) - 1);
close(file);
unlink(path);
if (length < 0) return 1;
buffer[length] = '\0';
if (printf("Captured: %s", buffer) < 0) return 1;
return 0;
}
Captured: Hello through dup2
L’esecuzione fornita ha acquisito Captured: Hello through dup2. Il programma scrive la prima riga nel file temporaneo, ripristina l’output standard, legge i byte salvati e stampa la riga acquisita. La riga mostrata dall’esempio resta in inglese. In un programma di produzione, scegli un percorso di destinazione e permessi adatti ai dati; l’esempio con mkstemp crea un file temporaneo privato.
Reindirizzare l’input standard con dup2
Per fare in modo che un programma legga un file tramite l’input standard, apri il file in sola lettura e duplica il suo descrittore su STDIN_FILENO. Dopo una chiamata riuscita, funzioni come fgets, che leggono da stdin, leggeranno da quel file. Puoi chiudere il descrittore restituito da open dopo la duplicazione, perché l’input standard ora si riferisce alla stessa descrizione di file aperta.
Questo esempio completo crea un file temporaneo di input, reindirizza l’input standard verso di esso e legge una riga con fgets. Poi stampa la riga per mostrare cosa è stato letto. Il file temporaneo è l’input dell’esempio; quando adatti questo schema a un file esistente, sostituiscine il contenuto o modifica la logica di fgets.
Il codice e le etichette o gli output visibili in questa sezione restano in inglese.
#define _POSIX_C_SOURCE 200809L
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void) {
char path[] = "/tmp/dup2-input-XXXXXX";
int input = mkstemp(path);
if (input == -1) return 1;
const char line[] = "Read through redirected stdin\n";
ssize_t written = write(input, line, sizeof line - 1);
if (written != (ssize_t)(sizeof line - 1) || lseek(input, 0, SEEK_SET) == (off_t)-1) {
close(input);
unlink(path);
return 1;
}
if (dup2(input, STDIN_FILENO) == -1) {
close(input);
unlink(path);
return 1;
}
close(input);
unlink(path);
char buffer[80];
if (fgets(buffer, sizeof buffer, stdin) == NULL) return 1;
if (printf("Read: %s", buffer) < 0) return 1;
return 0;
}
La forma basata su open per un percorso di input reale è int input = open("input.txt", O_RDONLY);; controlla che il risultato non sia -1, chiama dup2(input, STDIN_FILENO) e, in caso di successo, chiudi input. Le letture successive da stdin useranno quel file. Nell’esempio completo, la stessa sequenza legge la riga scritta nel file temporaneo. Se l’apertura, la duplicazione o la lettura falliscono, gestisci l’errore e chiudi tutti i descrittori ancora di tua proprietà.
Differenze tra dup e dup2
Entrambe le funzioni creano un altro descrittore per la stessa descrizione di file aperta. dup(oldfd) sceglie e restituisce il descrittore disponibile con il numero più basso. dup2(oldfd, newfd) usa il numero specificato, chiudendo prima l’eventuale descrittore di destinazione già aperto. Usa dup quando va bene qualsiasi numero disponibile; usa dup2 quando un descrittore specifico, spesso quello dell’input o dell’output standard, deve riferirsi alla sorgente.
Per esempio, int copy = dup(file); restituisce un nuovo descrittore oppure -1 in caso di errore. Controlla il risultato prima di usare copy e chiudi entrambi i descrittori quando non servono più. Non è garantito che il numero restituito sia 3: dipende dai descrittori aperti nel processo.
Gestire gli errori e la durata dei descrittori
Controlla il risultato di ogni chiamata a open, dup e dup2 prima di usare il descrittore restituito. In caso di errore, la chiamata restituisce -1 e imposta errno; perror("dup2") può segnalare l’errore corrente. Non usare un risultato di errore come descrittore. Se una chiamata in una sequenza di reindirizzamento composta da più passaggi fallisce, chiudi i descrittori aperti o salvati dal tuo codice e, se l’input o l’output standard è già stato modificato e il programma deve continuare con lo stream originale, ripristinalo.
Dopo un reindirizzamento riuscito, chiudere il descrittore sorgente originale non annulla il reindirizzamento: il descrittore standard resta aperto e si riferisce alla descrizione di file aperta condivisa. Chiudi i descrittori salvati e le altre copie quando non servono più. Svuota i buffer dell’output prima di modificare o ripristinare STDOUT_FILENO: i buffer di stdio in C sono distinti dalle operazioni sui descrittori del kernel. Questi controlli rendono prevedibili la sostituzione e la chiusura dei descrittori ed evitano supposizioni sui loro numeri.