GNU/Linux >> Linux Esercitazione >  >> Linux

Il puntatore del file C cambia dopo fork e (fallito) exec

Ringraziamo Jonathan Leffler per averci indicato la giusta direzione.

Sebbene il tuo programma non produca lo stesso comportamento imprevisto per me su CentOS 7 / GCC 4.8.5 / GLIBC 2.17, è plausibile che tu osservi un comportamento diverso. Il comportamento del tuo programma è infatti indefinito secondo POSIX (su cui fai affidamento per fork ). Ecco alcuni estratti dalla sezione pertinente (corsivo aggiunto):

È possibile accedere alla descrizione di un file aperto tramite un descrittore di file, che viene creato utilizzando funzioni come open() o pipe() , o attraverso uno stream, che viene creato usando funzioni come fopen() o popen() .O un descrittore di file o uno stream è chiamato "handle" sulla descrizione di openfile a cui fa riferimento; una descrizione di un file aperto può avere diversi handle.

[...]

Il risultato delle chiamate di funzione che coinvolgono un qualsiasi handle (l'"activehandle") è definito altrove in questo volume di POSIX.1-2017, ma se vengono utilizzati due o più handle e uno di essi è uno stream, l'applicazione deve garantire che il loro le azioni sono coordinate come descritto di seguito. Se ciò non viene fatto, il risultato non è definito .

[...]

Affinché un handle diventi l'handle attivo, l'applicazione deve garantire che le azioni seguenti vengano eseguite tra l'ultimo utilizzo dell'handle (l'attuale handle attivo) e il primo utilizzo del secondo handle (il futuro handle attivo). La seconda maniglia diventa quindi la maniglia attiva. [...]

Non è necessario che gli handle si trovino nello stesso processo affinché queste regole vengano applicate.

Nota che dopo un fork() , esistono due handle dove uno esisteva prima. L'applicazione deve garantire che, se è possibile accedere a entrambi gli handle, si trovino entrambi in uno stato in cui l'altro potrebbe diventare per primo l'handle attivo. [Laddove soggetta alla precedente qualificazione, la] domanda deve preparare un fork() esattamente come se si trattasse di un cambio di maniglia attiva. (Se l'unica azione eseguita da uno dei processi è una delle funzioni exec o_exit() (non exit() ), l'handle non è mai accessibile in quel processo. )

Per il primo handle, si applica la prima condizione applicabile di seguito.[Un elenco incredibilmente lungo di alternative che non si applicano alla situazione dell'OP...]

  • Se il flusso è aperto con una modalità che consente la lettura e la descrizione sottostante del file aperto fa riferimento a un dispositivo in grado di eseguire la ricerca, l'applicazione deve eseguire un fflush() , o lo stream verrà chiuso.

Per la seconda maniglia:

  • Se un precedente handle attivo è stato utilizzato da una funzione che ha modificato esplicitamente l'offset del file, ad eccezione di quanto richiesto sopra per il primo handle, l'applicazione deve eseguire un lseek() o fseek() (a seconda del tipo di maniglia) in una posizione appropriata.

Pertanto, affinché il programma dell'OP possa accedere allo stesso flusso sia nel genitore che nel figlio, POSIX richiede che il genitore fflush() stdin prima del fork e che il figlio fseek() dopo l'avvio. Quindi, dopo aver atteso che il figlio termini, il genitore deve fseek() il flusso. Dato che sappiamo che l'esecutivo del figlio fallirà, tuttavia, il requisito per tutto il lavaggio e la ricerca può essere evitato facendo in modo che il bambino usi _exit() (che non accede allo stream) invece di exit() .

Il rispetto delle disposizioni di POSIX produce quanto segue:

Quando queste regole vengono seguite, indipendentemente dalla sequenza di handle utilizzati, le implementazioni devono garantire che un'applicazione, anche composta da più processi, fornisca risultati corretti:nessun dato deve essere perso o duplicato durante la scrittura e tutti i dati devono essere scritti in ordine, ad eccezione di richiesto da seek.

Vale la pena notare, tuttavia, che

È definito dall'implementazione se, e in quali condizioni, tutti gli input vengono visti esattamente una volta.

Mi rendo conto che potrebbe essere in qualche modo insoddisfacente sentire semplicemente che le tue aspettative per il comportamento del programma non sono giustificate dagli standard pertinenti, ma questo è davvero tutto ciò che c'è. I processi padre e figlio hanno alcuni dati condivisi rilevanti sotto forma di una descrizione di file aperto comune (a cui sono associati handle separati) e sembra probabile che sia il veicolo per il comportamento imprevisto (e non definito), ma non c'è alcuna base per prevedere il comportamento specifico che vedi, né il diverso comportamento che vedo per lo stesso programma.


Linux
  1. Permessi e salvataggio dei file?

  2. Reindirizzamento e uscita del tubo?

  3. Installazione e configurazione di vsFTPD

  4. La differenza tra fork(), vfork(), exec() e clone()

  5. Thread e descrittori di file

Impara e usa le chiamate di sistema fork(), vfork(), wait() ed exec() su sistemi Linux

vmlinuz e initrd non trovati dopo aver compilato il kernel?

Modifica degli attributi Data di creazione e Ultima modifica di un file con quelli di un altro file

Il sistema rifiuta SSH e si blocca all'avvio dopo l'installazione di systemd

File unità Systemd - WantedBy e After

Impossibile configurare la catena di certificati CA