È sicuro analizzare un file / proc /?


152

Voglio analizzare /proc/net/tcp/, ma è sicuro?

Come dovrei aprire e leggere i file /proc/e non aver paura che qualche altro processo (o il sistema operativo stesso) lo cambierà nello stesso tempo?


29
+1. Questa è una dannata buona domanda. Vorrei solo avere la risposta, ma non vedo l'ora di scoprirlo da quando ho fatto questo genere di cose un po 'prima.
paxdiablo,

1
Sono abbastanza sicuro che leggerlo ti darà un elenco di connessioni, oltre all'UID che possiede ciascuna di esse, come lo erano quando l'hai aperto . Tuttavia, non lo trovo documentato, quindi per ora è un commento.
Tim Post

3
La risposta semplice è ovviamente sì, in quanto non è un file: leggerlo dovrebbe essere sempre sicuro. Le risposte potrebbero non essere coerenti dopo la lettura, ma saranno sicure.
Rory Alsop,

Questo è il motivo per cui dovresti usare sysctl invece. (è anche meno syscalls)
Good Person

@ GoodPerson: come può sysctlaiutarmi ad analizzare un /proc/net/tcp/file, ad esempio?
Kiril Kirov,

Risposte:


111

In generale, no. (Quindi la maggior parte delle risposte qui sono sbagliate.) Potrebbe essere sicuro, a seconda della proprietà desiderata. Ma è facile finire con i bug nel tuo codice se dai troppa importanza alla coerenza di un file /proc. Ad esempio, vedi questo bug derivante dall'ipotesi che /proc/mountsfosse un'istantanea coerente .

Per esempio:

  • /proc/uptimeè totalmente atomico , come qualcuno ha menzionato in un'altra risposta, ma solo da Linux 2.6.30 , che ha meno di due anni. Quindi anche questo file piccolo e banale era soggetto a una condizione di razza fino ad allora, ed è ancora nella maggior parte dei kernel aziendali. Controlla fs/proc/uptime.cla fonte corrente o il commit che l'ha resa atomica . Su un kernel precedente alla 2.6.30, puoi un po ' openil file, readquindi se torni più readvolte, il pezzo che otterrai sarà incoerente con il primo pezzo. (Ho appena dimostrato questo: provalo tu stesso per divertimento.)

  • /proc/mountsè atomico all'interno di una singola readchiamata di sistema. Quindi, se readl'intero file è tutto in una volta, si ottiene una singola istantanea coerente dei punti di montaggio sul sistema. Tuttavia, se si utilizzano più readchiamate di sistema e se il file è grande, questo è esattamente ciò che accadrà se si utilizzano normali librerie I / O e non si presta particolare attenzione a questo problema: si sarà soggetti a una gara condizione. Non solo non otterrai un'istantanea coerente, ma i punti di montaggio che erano presenti prima di iniziare e che non hanno mai smesso di essere presenti potrebbero non essere presenti in ciò che vedi. Per vedere che è atomico per uno read(), guarda m_start()dentrofs/namespace.c e vedi afferrare un semaforo che custodisce l'elenco dei mountpoint, che mantiene fino a m_stop(), che viene chiamato quando ilread()è fatta. Per vedere cosa può andare storto, vedi questo bug dello scorso anno (lo stesso che ho collegato sopra) in software di alta qualità che leggono allegramente /proc/mounts.

  • /proc/net/tcp, che è quello di cui stai effettivamente chiedendo, è ancora meno coerente di così. È atomico solo all'interno di ogni riga della tabella . Per vedere questo, guarda listening_get_next()dentronet/ipv4/tcp_ipv4.c e established_get_next()subito sotto nello stesso file e vedi i blocchi che tolgono su ogni voce a turno. Non ho un codice repro utile per dimostrare la mancanza di coerenza da una riga all'altra, ma non ci sono blocchi lì (o altro) che lo renderebbero coerente. Il che ha senso se ci pensate: la rete è spesso una parte super occupata del sistema, quindi non vale la pena presentare una visione coerente in questo strumento diagnostico.

L'altro pezzo che mantiene /proc/net/tcpatomica all'interno di ogni riga è il buffer in seq_read(), che potete leggere infs/seq_file.c . Questo assicura che una volta che fai read()parte di una riga, il testo dell'intera riga viene conservato in un buffer in modo che il successivo read()ottenga il resto di quella riga prima di iniziarne una nuova. Lo stesso meccanismo viene utilizzato /proc/mountsper mantenere atomica ogni riga anche se si effettuano più read()chiamate, ed è anche il meccanismo che /proc/uptimenei kernel più recenti utilizza per rimanere atomico. Tale meccanismo non non tampone l'intero file, perché il kernel è cauto circa l'uso della memoria.

La maggior parte dei file /procsarà coerente almeno quanto /proc/net/tcp, con ogni riga un'immagine coerente di una voce in qualunque informazione stiano fornendo, poiché la maggior parte di essi usa la stessa seq_fileastrazione. Come /proc/uptimemostra l' esempio, tuttavia, alcuni file erano ancora in fase di migrazione per essere utilizzati seq_filedi recente fino al 2009; Scommetto che ci sono ancora alcuni che usano meccanismi più vecchi e non hanno nemmeno quel livello di atomicità. Questi avvertimenti sono raramente documentati. Per un determinato file, l'unica garanzia è leggere l'origine.

Nel caso di /proc/net/tcp, puoi leggerlo e analizzare ogni riga senza paura. Ma se provi a trarre conclusioni da più righe contemporaneamente - fai attenzione, altri processi e il kernel lo stanno cambiando mentre lo leggi e probabilmente stai creando un bug.


1
che dire di atomicità readdir? come leggere / proc / self / fd? è sicuro?
socketpair,

Non che risponde alla domanda, ma per aggiungere su come controllare l'uptime è possibile utilizzare clock_gettime(2)con CLOCK_MONOTONIC(anche se forse c'è un dettaglio tecnico che sono a conoscenza di qui, ma io personalmente ho visto solo con il tempo dal momento di avvio). Per Linux hai anche la possibilità di sysinfo(2).
Pryftan,

44

Anche se i file in /procvisualizzati come file normali in userspace, in realtà non sono i file, ma piuttosto gli enti che supportano le operazioni di file standard da userspace ( open, read, close). Si noti che questo è abbastanza diverso dall'avere un normale file su disco che viene modificato dal kernel.

Tutto il kernel fa è stampare il suo stato interno nella propria memoria usando una sprintffunzione simile, e quella memoria viene copiata nello spazio utente ogni volta che si emette una read(2)chiamata di sistema.

Il kernel gestisce queste chiamate in un modo completamente diverso rispetto ai file normali, il che potrebbe significare che l'intera istantanea dei dati che leggerai potrebbe essere pronta nel momento in cui open(2)lo fai , mentre il kernel si assicura che le chiamate simultanee siano coerenti e atomiche. Non l'ho letto da nessuna parte, ma non ha davvero senso essere diversamente.

Il mio consiglio è di dare un'occhiata all'implementazione di un file proc nel tuo particolare sapore Unix. Questo è davvero un problema di implementazione (così come il formato e il contenuto dell'output) che non è regolato da uno standard.

L'esempio più semplice sarebbe l'implementazione del uptimefile proc in Linux. Notare come viene prodotto l'intero buffer nella funzione di callback fornita a single_open.


3
@Ignacio: sto solo indicando l'OP in questa direzione perché mi è rimasta l'impressione che lui pensi che i procfile siano normali file aperti per la scrittura dal kernel.
Blagovest Buyukliev,

4
Il tuo consiglio di esaminare l'implementazione del file specifico è buono. Sfortunatamente l'ipotesi che sia tutto snapshot open()è sbagliata per molti file, e in particolare per i /proc/net/tcpquali l'OP è interessato. Questo ha senso se pensi al costo di fornire quella semantica: dovresti fare qualcosa come bloccare le strutture di dati interne che registrano tutte quelle connessioni TCP, che su un sistema occupato è un disastro anche se tieni solo a lungo abbastanza per scansionare e formattare i dati in un buffer. Vedi la mia risposta per i dettagli su ciò che accade realmente.
Greg Price,

16

/ proc è un file system virtuale: in effetti, offre solo una comoda visione degli interni del kernel. È sicuramente sicuro leggerlo (ecco perché è qui) ma è rischioso a lungo termine, poiché l'interno di questi file virtuali potrebbe evolversi con la versione più recente del kernel.

MODIFICARE

Maggiori informazioni disponibili nella documentazione proc nel documento del kernel Linux , capitolo 1.4 Networking Non riesco a trovare se le informazioni su come si evolvono nel tempo. Ho pensato che fosse congelato all'aperto, ma non posso avere una risposta definitiva.

EDIT2

Secondo Sco doc (non Linux, ma sono abbastanza sicuro che tutte le versioni di * nix si comportino così)

Sebbene lo stato del processo e di conseguenza il contenuto dei file / proc possa cambiare da un istante all'altro, una sola lettura (2) di un file / proc è garantita per restituire una rappresentazione `` sana '' dello stato, ovvero la lettura sarà un'istantanea atomica dello stato del processo. Nessuna garanzia si applica alle letture successive applicate a un file / proc per un processo in esecuzione. Inoltre, l'atomicità non è specificamente garantita per alcun I / O applicato al file as (indirizzo-spazio); il contenuto dello spazio degli indirizzi di qualsiasi processo può essere modificato contemporaneamente da un LWP di quel processo o di qualsiasi altro processo nel sistema.


3
"Penso" ? Sarebbe bello avere una risposta definitiva :)
static_rtti

Data l'implementazione di / proc nel kernel, questo vale anche per Linux. Se leggi un file procfs in una singola chiamata di lettura, è coerente - ovviamente supponendo che il file proc che hai letto sia stato implementato correttamente su kernel.
Erik,

8
Non penso che potresti trovare una fonte di informazione peggiore possibile rispetto a SCO, e provare a trattare proccome se avesse un comportamento simile tra kernel diversi (o anche supponendo che esista - non è necessario in un sistema Unix ) ti farà soffrire un mondo.
Nicholas Knight,

1
@Nicholas: beh, non sono riuscito a trovare una risposta definitiva nel documento del kernel, sentiti libero di indicarlo se lo conosci.
Bruce,

2
Interessante che i documenti SCO lo affermino. Sfortunatamente non è sempre vero in Linux, e in particolare non è vero /proc/net/tcp, che è la principale preoccupazione dell'OP. Piuttosto, solo ogni singola riga nell'output è atomica. Vedi la mia risposta per i dettagli.
Greg Price,

14

L'API procfs nel kernel Linux fornisce un'interfaccia per assicurarsi che le letture restituiscano dati coerenti. Leggi i commenti in __proc_file_read. L'elemento 1) nel blocco dei commenti grandi spiega questa interfaccia.

Detto questo, spetta ovviamente all'implementazione di uno specifico file proc utilizzare correttamente questa interfaccia per assicurarsi che i suoi dati restituiti siano coerenti. Quindi, per rispondere alla tua domanda: no, il kernel non garantisce la coerenza dei file proc durante una lettura ma fornisce i mezzi per implementare tali file.


4
Sfortunatamente, molti file /procin realtà non forniscono coerenza. Vedi la mia risposta per i dettagli.
Greg Price,

3
Inoltre, __proc_file_read()è deprecato a favore di seq_file. Vedi il commento dal suono piuttosto esasperato (di Linus) appena sopra il lungo commento di blocco.
Greg Price,

6

Ho a portata di mano il sorgente per Linux 2.6.27.8 poiché sto attualmente sviluppando driver su un target ARM incorporato.

Il file ... linux-2.6.27.8-lpc32xx/net/ipv4/raw.calla riga 934 contiene, ad esempio

    seq_printf(seq, "%4d: %08X:%04X %08X:%04X"
            " %02X %08X:%08X %02X:%08lX %08X %5d %8d %lu %d %p %d\n",
            i, src, srcp, dest, destp, sp->sk_state,
            atomic_read(&sp->sk_wmem_alloc),
            atomic_read(&sp->sk_rmem_alloc),
            0, 0L, 0, sock_i_uid(sp), 0, sock_i_ino(sp),
            atomic_read(&sp->sk_refcnt), sp, atomic_read(&sp->sk_drops));

quali uscite

[wally@zenetfedora ~]$ cat /proc/net/tcp
  sl  local_address rem_address   st tx_queue rx_queue tr tm->when retrnsmt   uid  timeout inode                                                     
   0: 017AA8C0:0035 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 15160 1 f552de00 299
   1: 00000000:C775 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 13237 1 f552ca00 299
...

nella funzione raw_sock_seq_show()che fa parte di una gerarchia di funzioni di gestione dei processi . Il testo non viene generato fino a quando non read()viene fatta una richiesta del /proc/net/tcpfile, un meccanismo ragionevole poiché le letture di procfs sono sicuramente molto meno comuni dell'aggiornamento delle informazioni.

Alcuni driver (come il mio) implementano la funzione proc_read con un singolo sprintf(). La complicazione aggiuntiva nell'implementazione dei driver core è quella di gestire un output potenzialmente molto lungo che potrebbe non adattarsi al buffer intermedio dello spazio del kernel durante una singola lettura.

L'ho provato con un programma che utilizza un buffer di lettura da 64 KB, ma risulta in un buffer dello spazio del kernel di 3072 byte nel mio sistema affinché proc_read restituisca i dati. Sono necessarie più chiamate con puntatori di avanzamento per ottenere più di quel testo restituito. Non so quale sia il modo giusto per rendere coerenti i dati restituiti quando è necessario più di un I / O. Certamente ogni voce /proc/net/tcpè autoconsistente. È probabile che le linee affiancate siano istantanee in momenti diversi.


Mi dispiace davvero, non ho capito molto. Quindi, vuoi dire che se lo uso ifstream, non sarà sicuro, ma se lo uso readsarà sicuro? O ifstreamutilizza internamente read? E cosa mi consigliate?
Kiril Kirov,

@Kiril: mi dispiace per la confusione. Questa è una spiegazione di come vengono /proc/net/tcpformattati i dati ed è completamente indipendente da come chiunque li legge.
Wallyk,

1
Sì! E la tua ipotesi è corretta che linee diverse (in /proc/net/tcp) non provengono dalla stessa istantanea. Vedi la mia risposta per qualche spiegazione.
Greg Price,

3

A parte i bug sconosciuti, non ci sono condizioni di competizione /procche potrebbero portare alla lettura di dati corrotti o a una combinazione di vecchi e nuovi dati. In questo senso, è sicuro. Tuttavia c'è ancora la condizione di competizione che gran parte dei dati da cui si legge /procè potenzialmente superata non appena viene generata, e anche più quando si arriva a leggerli / elaborarli. Ad esempio i processi possono morire in qualsiasi momento e un nuovo processo può essere assegnato allo stesso pid; gli unici ID di processo che puoi mai usare senza condizioni di gara sono i tuoi processi figlio '. Lo stesso vale per le informazioni di rete (porte aperte, ecc.) E per la maggior parte delle informazioni /proc. Considererei una pratica cattiva e pericolosa fare affidamento su qualsiasi dato in/processere precisi, ad eccezione dei dati sul proprio processo e potenzialmente sui suoi processi figlio. Naturalmente può essere comunque utile presentare altre informazioni /procall'utente / all'amministratore per informazioni / registrazione / ecc. scopi.


Lo sto facendo per ottenere e utilizzare alcune informazioni per il mio processo (per il mio PID, utilizzando getpid()). Quindi, deve essere sicuro.
Kiril Kirov,

1
Sì, lo considererei completamente sicuro.
R .. GitHub smette di aiutare ICE il

Non sono d'accordo sul fatto che i processi figlio si comporterebbero meglio di qualsiasi altro processo. Per quanto riguarda l' /procinterfaccia, hanno tutti gli stessi punti deboli e punti di forza. Ad ogni modo, l'OP chiede informazioni relative al driver del dispositivo, non ai processi.
Wallyk,

1
Se pid Nè il tuo processo figlio, puoi assicurarti che pid faccia Nancora riferimento allo stesso processo (possibilmente terminato) fino a quando non chiami una waitfunzione -family su di esso. Questo assicura che non ci siano gare.
R .. GitHub smette di aiutare ICE il

Cosa c'è con il diluvio di -1 e nessuna spiegazione?
R .. GitHub smette di aiutare ICE

2

Quando leggi da un file / proc, il kernel chiama una funzione che è stata precedentemente registrata per essere la funzione "read" per quel file proc. Vedi la __proc_file_readfunzione in fs / proc / generic.c.

Pertanto, la sicurezza della lettura proc è sicura tanto quanto la funzione che il kernel chiama per soddisfare la richiesta di lettura. Se quella funzione blocca correttamente tutti i dati che tocca e ti restituisce in un buffer, allora è completamente sicuro leggere usando quella funzione. Poiché i file proc come quello utilizzato per soddisfare le richieste di lettura a / proc / net / tcp sono in circolazione da un po 'di tempo e sono stati sottoposti a scrupolosa revisione, sono quasi sicuri come si potrebbe chiedere. In effetti, molte utility Linux comuni si basano sulla lettura dal filesystem proc e sulla formattazione dell'output in modo diverso. (In cima alla mia testa, penso che 'ps' e 'netstat' facciano questo).

Come sempre, non devi credermi sulla parola; puoi guardare la fonte per calmare le tue paure. La seguente documentazione da proc_net_tcp.txt ti dice dove funzionano le funzioni "read" per / proc / net / tcp, così puoi guardare il codice reale che viene eseguito quando leggi da quel file proc e verificare da te che non ci sono rischi di bloccaggio.

Questo documento descrive le interfacce / proc / net / tcp e / proc / net / tcp6.
Si noti che queste interfacce sono deprecate a favore di tcp_diag. Queste interfacce / proc forniscono informazioni sulle connessioni TCP attualmente attive e sono implementate da tcp4_seq_show () in net / ipv4 / tcp_ipv4.c e tcp6_seq_show () in net / ipv6 / tcp_ipv6.c, rispettivamente.

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.