Ist es sicher, eine / proc / -Datei zu analysieren?

152

Ich möchte analysieren /proc/net/tcp/, aber ist es sicher?

Wie soll ich Dateien öffnen und lesen /proc/und keine Angst haben, dass ein anderer Prozess (oder das Betriebssystem selbst) sie gleichzeitig ändert?

Kiril Kirov
quelle
29
+1. Das ist eine verdammt gute Frage. Ich wünschte nur, ich hätte die Antwort, aber ich freue mich darauf, es herauszufinden, da ich so etwas schon ziemlich oft gemacht habe.
Paxdiablo
1
Ich bin mir ziemlich sicher , dass Sie beim Lesen eine Liste der Verbindungen sowie die UID erhalten, zu der jede gehört, wie beim Öffnen . Ich kann das jedoch nicht dokumentiert finden, daher mache ich dies vorerst zu einem Kommentar.
Tim Post
3
Die einfache Antwort lautet natürlich Ja, da es sich nicht um eine Datei handelt. Das Lesen sollte immer sicher sein. Die Antworten sind möglicherweise nicht konsistent, wenn Sie sie später lesen, aber sie sind sicher.
Rory Alsop
Aus diesem Grund sollten Sie stattdessen sysctl verwenden. (es ist auch weniger Systemaufrufe)
Gute Person
@GoodPerson - wie kann sysctlich /proc/net/tcp/zum Beispiel eine Datei analysieren ?
Kiril Kirov

Antworten:

111

Im Allgemeinen nein. (Die meisten Antworten hier sind also falsch.) Je nachdem, welche Eigenschaft Sie möchten, ist dies möglicherweise sicher. Es ist jedoch leicht, Fehler in Ihrem Code zu finden, wenn Sie zu viel von der Konsistenz einer Datei in annehmen /proc. Sehen Sie sich zum Beispiel diesen Fehler an, der von der Annahme herrührt, dass /proc/mountses sich um einen konsistenten Schnappschuss handelt .

Beispielsweise:

  • /proc/uptimeist total atomar , wie jemand in einer anderen Antwort erwähnt hat - aber erst seit Linux 2.6.30 , das weniger als zwei Jahre alt ist. Selbst diese winzige, triviale Datei unterlag bis dahin einer Race-Bedingung und befindet sich immer noch in den meisten Unternehmenskernen. Siehe fs/proc/uptime.cfür die aktuelle Quelle oder das Commit, das es atomar gemacht hat . Auf einem Kernel vor 2.6.30 können Sie opendie Datei, readein bisschen davon, und wenn Sie später immer wieder zurückkommen read, wird das Stück, das Sie erhalten, nicht mit dem ersten Stück übereinstimmen. (Ich habe das gerade demonstriert - probieren Sie es selbst zum Spaß.)

  • /proc/mountsist innerhalb eines einzelnen readSystemaufrufs atomar . Wenn Sie also readdie gesamte Datei auf einmal erstellen, erhalten Sie einen einzigen konsistenten Snapshot der Mount-Punkte auf dem System. Wenn Sie jedoch mehrere readSystemaufrufe verwenden - und wenn die Datei groß ist, passiert genau dies, wenn Sie normale E / A-Bibliotheken verwenden und diesem Problem keine besondere Aufmerksamkeit schenken -, werden Sie einem Rennen ausgesetzt sein Bedingung. Sie erhalten nicht nur keinen konsistenten Schnappschuss, sondern Mount-Punkte, die vor dem Start vorhanden waren und nie aufgehört haben, vorhanden zu sein, können in dem, was Sie sehen, verloren gehen. Um zu sehen, dass es für einen atomar ist read(), schauen Sie m_start()infs/namespace.c und sehen Sie, wie es ein Semaphor ergreift, das die Liste der Mountpunkte schützt, die es bis dahin aufbewahrt m_stop()und die aufgerufen wird, wenn dasread()fertig. Um zu sehen, was schief gehen kann, lesen Sie diesen Fehler aus dem letzten Jahr (derselbe, den ich oben verlinkt habe) in einer ansonsten hochwertigen Software, die munter gelesen wird /proc/mounts.

  • /proc/net/tcp, nach dem Sie tatsächlich fragen, ist noch weniger konsistent. Es ist nur in jeder Zeile der Tabelle atomar . Um dies zu sehen, schauen Sie listening_get_next()innet/ipv4/tcp_ipv4.c und established_get_next()direkt darunter in derselben Datei nach und sehen Sie die Sperren, die sie nacheinander für jeden Eintrag aufheben. Ich habe keinen Repro-Code zur Hand, um die mangelnde Konsistenz von Zeile zu Zeile zu demonstrieren, aber es gibt dort keine Sperren (oder irgendetwas anderes), die ihn konsistent machen würden. Was Sinn macht, wenn Sie darüber nachdenken - Netzwerke sind oft ein sehr geschäftiger Teil des Systems, daher lohnt es sich nicht, eine konsistente Ansicht in diesem Diagnosetool zu präsentieren.

Das andere Stück , das hält /proc/net/tcpinnerhalb jeder Reihe ist die Atom-Pufferung in seq_read(), die man lesen kann infs/seq_file.c . Auf diese Weise wird sichergestellt, dass read()der Text der gesamten Zeile in einem Puffer gespeichert wird, sobald Sie Teil einer Zeile sind, sodass die nächste read()Zeile den Rest dieser Zeile abruft, bevor eine neue Zeile gestartet wird. Der gleiche Mechanismus wird verwendet /proc/mounts, um jede Zeile atomar zu halten, selbst wenn Sie mehrere read()Aufrufe ausführen, und es ist auch der Mechanismus, den /proc/uptimein neueren Kerneln verwendet wird, um atomar zu bleiben. Dieser Mechanismus puffert nicht die gesamte Datei, da der Kernel bei der Speichernutzung vorsichtig ist.

Die meisten Dateien in /procsind mindestens so konsistent wie /proc/net/tcp, wobei jede Zeile ein konsistentes Bild eines Eintrags in den von ihnen bereitgestellten Informationen enthält, da die meisten von ihnen dieselbe seq_fileAbstraktion verwenden. Wie das /proc/uptimeBeispiel zeigt, wurden einige Dateien noch 2009 migriert, um sie zu verwenden seq_file. Ich wette, es gibt immer noch einige, die ältere Mechanismen verwenden und nicht einmal diesen Grad an Atomizität haben. Diese Vorbehalte sind selten dokumentiert. Für eine bestimmte Datei besteht Ihre einzige Garantie darin, die Quelle zu lesen.

Im Fall von /proc/net/tcpkönnen Sie es lesen und jede Zeile ohne Angst analysieren. Aber wenn Sie versuchen , irgendwelche Schlüsse zu aus mehreren Linien zu zeichnen einmal - Vorsicht, andere Prozesse und der Kern sind zu ändern, während Sie es lesen, und Sie erstellen möglicherweise einen Fehler.

Greg Price
quelle
1
Was ist mit Readdir Atomizität? wie lesen / proc / self / fd? ist es sicher?
Socketpair
Nicht, dass es die Frage beantwortet, sondern um hinzuzufügen, wie die Verfügbarkeit überprüft werden kann, clock_gettime(2)mit der Sie arbeiten können CLOCK_MONOTONIC(obwohl es hier möglicherweise eine Technik gibt, die mir hier nicht bekannt ist, die ich aber persönlich erst seit dem Start gesehen habe). Für Linux haben Sie auch die Möglichkeit sysinfo(2).
Pryftan
44

Obwohl die Dateien in /procals reguläre Dateien in User - Space erscheinen, sie sind nicht wirklich Dateien , sondern Unternehmen , die die Standard - Dateioperationen von Anwenderseite unterstützen ( open, read, close). Beachten Sie, dass dies ganz anders ist als eine normale Datei auf der Festplatte, die vom Kernel geändert wird.

Der Kernel druckt lediglich seinen internen Status mithilfe einer sprintfähnlichen Funktion in seinen eigenen Speicher. Dieser Speicher wird bei jedem read(2)Systemaufruf in den Benutzerbereich kopiert .

Der Kernel behandelt diese Aufrufe ganz anders als bei normalen Dateien. Dies kann bedeuten, dass der gesamte Snapshot der Daten, die Sie lesen, zum Zeitpunkt der Ausführung bereit open(2)ist, während der Kernel sicherstellt, dass gleichzeitige Aufrufe konsistent und atomar sind. Ich habe das nirgendwo gelesen, aber es macht keinen Sinn, anders zu sein.

Mein Rat ist, einen Blick auf die Implementierung einer Proc-Datei in Ihrer speziellen Unix-Variante zu werfen. Dies ist wirklich ein Implementierungsproblem (ebenso wie das Format und der Inhalt der Ausgabe), das nicht durch einen Standard geregelt wird.

Das einfachste Beispiel wäre die Implementierung der uptimeproc-Datei unter Linux. Beachten Sie, wie der gesamte Puffer in der Rückruffunktion erzeugt wird, die an geliefert wird single_open.

Blagovest Buyukliev
quelle
3
@Ignacio: Ich zeige das OP nur in diese Richtung, weil ich den Eindruck hatte, dass er glaubt, dass procDateien normale Dateien sind, die vom Kernel zum Schreiben geöffnet wurden.
Blagovest Buyukliev
4
Ihr Rat, sich die Implementierung der spezifischen Datei anzusehen, ist gut. Leider ist die Vermutung, dass alles mit einem Schnappschuss versehen open()ist, für viele Dateien falsch, insbesondere für die /proc/net/tcp, mit denen sich das OP befasst. Dies ist sinnvoll, wenn Sie über die Kosten für die Bereitstellung dieser Semantik nachdenken. Sie müssten beispielsweise die internen Datenstrukturen sperren, die alle TCP-Verbindungen aufzeichnen. Dies ist auf einem ausgelasteten System eine Katastrophe, selbst wenn Sie es nur lange halten genug, um die Daten zu scannen und in einen Puffer zu formatieren. Siehe meine Antwort für Details darüber, was tatsächlich passiert.
Greg Price
16

/ proc ist ein virtuelles Dateisystem: Tatsächlich bietet es nur eine bequeme Ansicht der Kernel-Interna. Es ist definitiv sicher, es zu lesen (deshalb ist es hier), aber es ist auf lange Sicht riskant, da sich das Innere dieser virtuellen Dateien mit einer neueren Version des Kernels weiterentwickeln kann.

BEARBEITEN

Weitere Informationen finden Sie in der Proc-Dokumentation im Linux-Kernel-Dokument , Kapitel 1.4. Netzwerk Ich kann nicht finden, ob sich die Informationen darüber entwickeln, wie sich die Informationen im Laufe der Zeit entwickeln. Ich dachte, es sei eingefroren, kann aber keine eindeutige Antwort haben.

EDIT2

Laut Sco doc (nicht Linux, aber ich bin mir ziemlich sicher, dass sich alle Varianten von * nix so verhalten)

Obwohl sich der Prozessstatus und folglich der Inhalt von / proc-Dateien von Augenblick zu Augenblick ändern kann, wird bei einem einzelnen Lesevorgang (2) einer / proc-Datei garantiert, dass eine "vernünftige" Darstellung des Zustands zurückgegeben wird, dh der Lesevorgang wird ausgeführt eine atomare Momentaufnahme des Status des Prozesses. Eine solche Garantie gilt nicht für aufeinanderfolgende Lesevorgänge, die auf eine / proc-Datei für einen laufenden Prozess angewendet werden. Darüber hinaus ist die Atomizität für E / A, die auf die as-Datei (Adressraum) angewendet werden, nicht garantiert. Der Inhalt des Adressraums eines Prozesses kann gleichzeitig von einem LWP dieses Prozesses oder eines anderen Prozesses im System geändert werden.

Bruce
quelle
3
"Meiner Ansicht nach" ? Es wäre schön, eine endgültige Antwort zu haben :)
static_rtti
Angesichts der Implementierung von / proc im Kernel gilt dies auch für Linux. Wenn Sie eine procfs-Datei in einem einzigen Leseaufruf lesen, ist dies konsistent - natürlich vorausgesetzt, die von Ihnen gelesene proc-Datei wurde kernelside korrekt implementiert.
Erik
8
Ich glaube nicht, dass Sie eine schlechtere mögliche Informationsquelle als SCO finden und versuchen könnten, so zu behandeln, procals ob es ein ähnliches Verhalten zwischen verschiedenen Kerneln gibt (oder sogar anzunehmen, dass es existiert - es muss nicht in einem Unix-System sein ) wird dir eine Welt voller Verletzungen bescheren.
Nicholas Knight
1
@Nicholas: Nun, ich konnte keine endgültige Antwort im Kernel-Dokument finden. Sie können gerne darauf hinweisen, wenn Sie es wissen.
Bruce
2
Interessant, dass die SCO-Dokumente das sagen. Leider ist dies unter Linux nicht immer der Fall, und insbesondere gilt dies nicht für /proc/net/tcpdas Hauptanliegen des OP. Vielmehr ist nur jede einzelne Zeile in der Ausgabe atomar. Siehe meine Antwort für Details.
Greg Price
14

Die procfs-API im Linux-Kernel bietet eine Schnittstelle, um sicherzustellen, dass Lesevorgänge konsistente Daten zurückgeben. Lesen Sie die Kommentare in __proc_file_read. Punkt 1) im großen Kommentarblock erklärt diese Schnittstelle.

Abgesehen davon ist es natürlich Sache der Implementierung einer bestimmten Proc-Datei, diese Schnittstelle korrekt zu verwenden, um sicherzustellen, dass die zurückgegebenen Daten konsistent sind. Um Ihre Frage zu beantworten: Nein, der Kernel garantiert keine Konsistenz der Proc-Dateien während eines Lesevorgangs, bietet jedoch die Möglichkeit, dass die Implementierungen dieser Dateien Konsistenz gewährleisten.

Job
quelle
4
Leider /procbieten viele Dateien in tatsächlich keine Konsistenz. Siehe meine Antwort für Details.
Greg Price
3
Auch __proc_file_read()wird zugunsten von veraltet seq_file. Siehe den ziemlich verärgert klingenden Kommentar (von Linus) direkt über dem langen Blockkommentar.
Greg Price
6

Ich habe die Quelle für Linux 2.6.27.8 zur Hand, da ich derzeit die Treiberentwicklung auf einem eingebetteten ARM-Ziel durchführe.

Die Datei ... linux-2.6.27.8-lpc32xx/net/ipv4/raw.cin Zeile 934 enthält zum Beispiel

    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));

welche Ausgänge

[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
...

in function, raw_sock_seq_show()die Teil einer Hierarchie von procfs- Handling-Funktionen ist. Der Text wird erst generiert, wenn eine read()Anfrage an die /proc/net/tcpDatei gestellt wird. Dies ist ein vernünftiger Mechanismus, da Procfs- Lesevorgänge sicherlich viel seltener sind als das Aktualisieren der Informationen.

Einige Treiber (wie meine) implementieren die Funktion proc_read mit einem einzigen sprintf() . Die zusätzliche Komplikation bei der Implementierung der Kerntreiber besteht darin, möglicherweise sehr lange Ausgaben zu verarbeiten, die während eines einzelnen Lesevorgangs möglicherweise nicht in den dazwischen liegenden Kernel-Space-Puffer passen.

Ich habe das mit einem Programm getestet, das einen 64-KB-Lesepuffer verwendet, aber es führt zu einem Kernel-Space-Puffer von 3072 Bytes in meinem System, damit proc_read Daten zurückgibt. Es sind mehrere Aufrufe mit fortschreitenden Zeigern erforderlich, um mehr als so viel Text zurückzugeben. Ich weiß nicht, wie ich die zurückgegebenen Daten richtig konsistent machen kann, wenn mehr als eine E / A benötigt wird. Sicherlich ist jeder Eintrag in /proc/net/tcpsich selbst konsistent. Es besteht eine gewisse Wahrscheinlichkeit, dass Zeilen nebeneinander zu unterschiedlichen Zeiten als Schnappschuss erstellt werden.

Wallyk
quelle
Es tut mir wirklich leid, ich habe nicht viel verstanden. Meinen Sie damit, dass ifstreames unsicher ist, wenn ich es benutze, aber wenn ich es benutze, ist reades sicher? Oder ifstreamintern verwendet read? Und was schlagen Sie vor?
Kiril Kirov
@ Kiril: Entschuldigung für die Verwirrung. Dies ist eine Erklärung dafür, wie die Daten für /proc/net/tcpformatiert sind und völlig unabhängig davon, wie jemand sie liest.
Wallyk
1
Ja! Und Ihre Vermutung ist richtig, dass verschiedene Zeilen (in /proc/net/tcp) nicht aus demselben Schnappschuss stammen. Siehe meine Antwort für eine Erklärung.
Greg Price
3

Ohne unbekannte Fehler gibt es keine Rennbedingungen /proc, die zum Lesen beschädigter Daten oder einer Mischung aus alten und neuen Daten führen würden. In diesem Sinne ist es sicher. Es gibt jedoch immer noch die Rennbedingung, dass ein Großteil der Daten, aus denen Sie lesen, /procmöglicherweise veraltet ist, sobald sie generiert wurden, und sogar mehr, wenn Sie sie lesen / verarbeiten. Zum Beispiel können Prozesse jederzeit sterben und einem neuen Prozess kann dieselbe PID zugewiesen werden. Die einzigen Prozess-IDs, die Sie jemals ohne Race-Bedingungen verwenden können, sind Ihre eigenen untergeordneten Prozesse. Gleiches gilt für Netzwerkinformationen (offene Ports usw.) und die meisten Informationen in /proc. Ich würde es als schlechte und gefährliche Praxis betrachten, sich auf Daten in zu verlassen/procGenauigkeit, außer Daten über Ihren eigenen Prozess und möglicherweise seine untergeordneten Prozesse. Natürlich kann es weiterhin nützlich sein, /procdem Benutzer / Administrator andere Informationen zur Information / Protokollierung / etc. Zu präsentieren. Zwecke.

R .. GitHub HÖREN SIE AUF, EIS ZU HELFEN
quelle
Ich mache dies, um einige Informationen für meinen eigenen Prozess zu erhalten und zu verwenden (für meine PID, mit ). Also muss es sicher sein. getpid()
Kiril Kirov
1
Ja, das würde ich für absolut sicher halten.
R .. GitHub STOP HELPING ICE
Ich bin nicht der Meinung, dass sich untergeordnete Prozesse besser verhalten würden als jeder andere Prozess. In /procBezug auf die Schnittstelle haben alle die gleichen Schwächen und Stärken. Auf jeden Fall fragt das OP nach Informationen zum Gerätetreiber, nicht nach Prozessen.
Wallyk
1
Wenn pid NIhr untergeordneter Prozess ist, können Sie sicherstellen, dass pid Nimmer noch auf denselben (möglicherweise beendeten) Prozess verweist, bis Sie eine wait-family-Funktion darauf aufrufen. Dies stellt sicher, dass es keine Rennen gibt.
R .. GitHub STOP HELPING ICE
Was ist mit der Flut von -1 und ohne Erklärung?
R .. GitHub STOP HELPING ICE
2

Wenn Sie aus einer / proc-Datei lesen, ruft der Kernel eine Funktion auf, die zuvor als "Lese" -Funktion für diese proc-Datei registriert wurde. Siehe die __proc_file_readFunktion in fs / proc / generic.c.

Daher ist die Sicherheit des Prozesslesens nur so sicher wie die Funktion, die der Kernel aufruft, um die Leseanforderung zu erfüllen. Wenn diese Funktion alle berührten Daten ordnungsgemäß sperrt und in einem Puffer an Sie zurückgibt, ist das Lesen mit dieser Funktion absolut sicher. Da Proc-Dateien wie die, die zum Erfüllen von Leseanforderungen an / proc / net / tcp verwendet werden, schon eine Weile existieren und einer sorgfältigen Prüfung unterzogen wurden, sind sie ungefähr so ​​sicher, wie Sie es sich wünschen können. Tatsächlich verlassen sich viele gängige Linux-Dienstprogramme darauf, aus dem proc-Dateisystem zu lesen und die Ausgabe auf andere Weise zu formatieren. (Auf den ersten Blick denke ich, dass 'ps' und 'netstat' dies tun).

Wie immer musst du nicht mein Wort dafür nehmen; Sie können auf die Quelle schauen, um Ihre Ängste zu beruhigen. In der folgenden Dokumentation aus proc_net_tcp.txt erfahren Sie, wo die "Lese" -Funktionen für / proc / net / tcp aktiv sind. Sie können sich also den tatsächlichen Code ansehen, der beim Lesen aus dieser Proc-Datei ausgeführt wird, und selbst überprüfen, ob keine vorhanden sind Verriegelungsgefahren.

Dieses Dokument beschreibt die Schnittstellen / proc / net / tcp und / proc / net / tcp6.
Beachten Sie, dass diese Schnittstellen zugunsten von tcp_diag veraltet sind. Diese / proc-Schnittstellen bieten Informationen zu derzeit aktiven TCP-Verbindungen und werden von tcp4_seq_show () in net / ipv4 / tcp_ipv4.c bzw. tcp6_seq_show () in net / ipv6 / tcp_ipv6.c implementiert.

Heide
quelle