Est-il sûr d'analyser un fichier / proc /?


152

Je veux analyser /proc/net/tcp/, mais est-ce sûr?

Comment ouvrir et lire des fichiers depuis /proc/et ne pas avoir peur qu'un autre processus (ou le système d'exploitation lui-même) le modifie en même temps?


29
+1. C'est une très bonne question. J'aurais seulement aimé avoir la réponse, mais j'ai hâte de la découvrir puisque j'ai déjà fait ce genre de chose un peu plus tôt.
paxdiablo

1
Je suis presque sûr que le simple fait de le lire vous donnera une liste de connexions, ainsi que l'UID qui possède chacune d'entre elles, comme elles l'étaient lorsque vous l'avez ouverte . Cependant, je ne peux pas trouver cela documenté, alors faites-en un commentaire pour le moment.
Tim Post

3
La réponse simple est évidemment oui, car ce n'est pas un fichier - sa lecture doit toujours être sûre. Les réponses peuvent ne pas être cohérentes les fois que vous les lirez, mais elles seront sûres.
Rory Alsop

C'est pourquoi vous devriez utiliser sysctl à la place. (c'est aussi moins d'appels système)
Good Person

@GoodPerson - comment cela peut-il sysctlm'aider à analyser un /proc/net/tcp/fichier, par exemple?
Kiril Kirov

Réponses:


111

En général, non. (La plupart des réponses ici sont donc fausses.) Cela peut être sûr, selon la propriété que vous souhaitez. Mais il est facile de se retrouver avec des bogues dans votre code si vous en pensez trop sur la cohérence d'un fichier dans /proc. Par exemple, voyez ce bogue qui est venu de supposer qu'il /proc/mountss'agissait d'un instantané cohérent .

Par exemple:

  • /proc/uptimeest totalement atomique , comme quelqu'un l'a mentionné dans une autre réponse - mais seulement depuis Linux 2.6.30 , qui a moins de deux ans. Ainsi, même ce petit fichier trivial était soumis à une condition de concurrence jusque-là, et est toujours dans la plupart des noyaux d'entreprise. Consultez fs/proc/uptime.cla source actuelle ou le commit qui l'a rendu atomique . Sur un noyau pré-2.6.30, vous pouvez openle fichier, readun peu de celui-ci, puis si vous revenez plus tard et readencore, le morceau que vous obtenez sera incompatible avec le premier morceau. (Je viens de le démontrer - essayez-le vous-même pour vous amuser.)

  • /proc/mountsest atomique dans un seul readappel système. Donc, si vous avez readtout le fichier en même temps, vous obtenez un seul instantané cohérent des points de montage sur le système. Cependant, si vous utilisez plusieurs readappels système - et si le fichier est gros, c'est exactement ce qui se passera si vous utilisez des bibliothèques d'E / S normales et que vous ne portez pas une attention particulière à ce problème - vous serez soumis à une course état. Non seulement vous n'obtiendrez pas un instantané cohérent, mais les points de montage qui étaient présents avant de commencer et qui n'ont jamais cessé d'être présents peuvent disparaître dans ce que vous voyez. Pour voir qu'il est atomique pour un read(), regardez m_start()dansfs/namespace.c et voir saisir une sémaphores qui protège la liste des points de montage, qu'il conserve jusqu'à ce que m_stop(), qui est appelée lorsque leread()est fait. Pour voir ce qui peut mal tourner, voyez ce bogue de l'année dernière (le même que j'ai lié ci-dessus) dans un logiciel autrement de haute qualité qui lit allègrement /proc/mounts.

  • /proc/net/tcp, qui est celui que vous demandez en fait, est encore moins cohérent que cela. C'est atomique uniquement dans chaque ligne de la table . Pour voir cela, regardez listening_get_next()dansnet/ipv4/tcp_ipv4.c et established_get_next()juste en dessous dans le même fichier, et voyez les verrous qu'ils retirent à chaque entrée à tour de rôle. Je n'ai pas de code de repro à portée de main pour démontrer le manque de cohérence d'une ligne à l'autre, mais il n'y a aucun verrou (ou quoi que ce soit d'autre) qui le rendrait cohérent. Ce qui a du sens si vous y réfléchissez - le réseautage est souvent une partie très occupée du système, il ne vaut donc pas la peine de présenter une vue cohérente dans cet outil de diagnostic.

L'autre pièce qui maintient /proc/net/tcpatomique dans chaque ligne est la mise en mémoire tampon dans seq_read(), que vous pouvez lire dansfs/seq_file.c . Cela garantit qu'une fois que vous faites read()partie d'une ligne, le texte de la ligne entière est conservé dans une mémoire tampon afin que la suivante read()obtienne le reste de cette ligne avant d'en commencer une nouvelle. Le même mécanisme est utilisé /proc/mountspour garder chaque ligne atomique même si vous effectuez plusieurs read()appels, et c'est aussi le mécanisme /proc/uptimeutilisé dans les noyaux plus récents pour rester atomique. Ce mécanisme ne met pas en mémoire tampon tout le fichier, car le noyau est prudent quant à l'utilisation de la mémoire.

La plupart des fichiers /procseront au moins aussi cohérents que /proc/net/tcp, avec chaque ligne, une image cohérente d'une entrée quelle que soit l'information qu'ils fournissent, car la plupart d'entre eux utilisent la même seq_fileabstraction. Comme l' /proc/uptimeillustre l' exemple, cependant, certains fichiers étaient encore en cours de migration pour être utilisés seq_fileaussi récemment qu'en 2009; Je parie qu'il y en a encore qui utilisent des mécanismes plus anciens et qui n'ont même pas ce niveau d'atomicité. Ces mises en garde sont rarement documentées. Pour un fichier donné, votre seule garantie est de lire la source.

Dans le cas de /proc/net/tcp, vous pouvez le lire et analyser chaque ligne sans crainte. Mais si vous essayez de tirer des conclusions à partir de plusieurs lignes à la fois - méfiez - vous, d' autres processus et le noyau sont en train de changer pendant que vous lisez, et vous créez probablement un bug.


1
qu'en est-il de l'atomicité de readdir? comme lire / proc / self / fd? est-ce sûr?
socketpair

Non pas que cela répond à la question , mais d'ajouter sur la façon de vérifier le temps de disponibilité , vous pouvez utiliser clock_gettime(2)avec CLOCK_MONOTONIC(mais peut - être il y a un détail que je ne suis pas au courant d'ici mais je n'ai personnellement vu avec depuis le temps de démarrage). Pour Linux, vous avez également la possibilité de sysinfo(2).
Pryftan

44

Bien que les fichiers /procapparaissent sous forme de fichiers réguliers dans l' espace utilisateur, ils ne sont pas vraiment des fichiers , mais plutôt des entités qui prennent en charge les opérations de fichiers standard depuis l' espace utilisateur ( open, read, close). Notez que c'est tout à fait différent d'avoir un fichier ordinaire sur le disque qui est modifié par le noyau.

Tout ce que le noyau fait, c'est imprimer son état interne dans sa propre mémoire en utilisant une sprintffonction semblable à celle-ci, et cette mémoire est copiée dans l'espace utilisateur chaque fois que vous émettez un read(2)appel système.

Le noyau gère ces appels d'une manière entièrement différente de celle des fichiers normaux, ce qui pourrait signifier que tout l'instantané des données que vous allez lire pourrait être prêt au moment où vous le lirez open(2), tandis que le noyau s'assure que les appels simultanés sont cohérents et atomiques. Je n'ai lu cela nulle part, mais cela n'a pas vraiment de sens d'en être autrement.

Mon conseil est de jeter un œil à l'implémentation d'un fichier proc dans votre propre saveur Unix. C'est vraiment un problème d'implémentation (tout comme le format et le contenu de la sortie) qui n'est pas régi par une norme.

L'exemple le plus simple serait l'implémentation du uptimefichier proc sous Linux. Notez comment le tampon entier est produit dans la fonction de rappel fournie à single_open.


3
@Ignacio: Je pointe juste l'OP dans cette direction car j'ai eu l'impression qu'il pense que les procfichiers sont des fichiers ordinaires ouverts pour l'écriture par le noyau.
Blagovest Buyukliev

4
Votre conseil pour regarder la mise en œuvre du fichier spécifique est bon. Malheureusement, l'hypothèse selon laquelle tout est instantané open()est erronée pour de nombreux fichiers, et en particulier pour ceux /proc/net/tcpqui concernent l'OP. Cela a du sens si vous pensez au coût de fournir cette sémantique - vous devriez faire quelque chose comme verrouiller les structures de données internes qui enregistrent toutes ces connexions TCP, ce qui sur un système occupé est un désastre même si vous ne le tenez que longtemps assez pour parcourir et formater les données dans un tampon. Voir ma réponse pour plus de détails sur ce qui se passe réellement.
Greg Price

16

/ proc est un système de fichiers virtuel: en fait, il donne juste une vue pratique des éléments internes du noyau. Il est certainement sûr de le lire (c'est pourquoi c'est ici) mais c'est risqué sur le long terme, car l'intérieur de ces fichiers virtuels peut évoluer avec la nouvelle version du noyau.

ÉDITER

Plus d'informations disponibles dans la documentation proc dans la documentation du noyau Linux , chapitre 1.4 Réseau Je ne peux pas trouver si les informations sur l'évolution des informations au fil du temps. Je pensais qu'il était figé sur ouvert, mais je ne peux pas avoir de réponse définitive.

EDIT2

Selon Sco doc (pas Linux, mais je suis presque sûr que toutes les saveurs de * nix se comportent comme ça)

Bien que l'état du processus et par conséquent le contenu des fichiers / proc puissent changer d'instant en instant, une seule lecture (2) d'un fichier / proc est garantie de renvoyer une représentation `` saine '' de l'état, c'est-à-dire que la lecture sera un instantané atomique de l'état du processus. Aucune garantie de ce type ne s'applique aux lectures successives appliquées à un fichier / proc pour un processus en cours d'exécution. De plus, l'atomicité n'est pas spécifiquement garantie pour les E / S appliquées au fichier as (adresse-espace); le contenu de l'espace d'adressage de tout processus peut être modifié simultanément par un LWP de ce processus ou par tout autre processus du système.


3
"Je pense" ? Ce serait bien d'avoir une réponse définitive :)
static_rtti

Compte tenu de l'implémentation de / proc dans le noyau, cela est également vrai pour Linux. Si vous lisez un fichier procfs en un seul appel de lecture, il est cohérent - bien sûr en supposant que le fichier proc que vous avez lu a été correctement implémenté côté noyau.
Erik

8
Je ne pense pas que vous pourriez trouver une pire source d'information que SCO, et essayer de traiter proccomme s'il avait un comportement similaire entre différents noyaux (ou même en supposant qu'il existe - ce n'est pas nécessaire dans un système Unix ) va vous faire mal.
Nicholas Knight

1
@Nicholas: eh bien, je n'ai pas trouvé de réponse définitive dans la documentation du noyau, n'hésitez pas à la pointer si vous la connaissez.
Bruce le

2
Intéressant que la documentation SCO dise cela. Malheureusement ce n'est pas toujours vrai sous Linux, et en particulier ce n'est pas vrai pour /proc/net/tcp, qui est la principale préoccupation de l'OP. Au contraire, seule chaque ligne individuelle de la sortie est atomique. Voir ma réponse pour plus de détails.
Greg Price

14

L'API procfs dans le noyau Linux fournit une interface pour s'assurer que les lectures renvoient des données cohérentes. Lisez les commentaires dans __proc_file_read. L'élément 1) dans le grand bloc de commentaires explique cette interface.

Cela étant dit, il appartient bien entendu à l'implémentation d'un fichier proc spécifique d'utiliser correctement cette interface pour s'assurer que ses données renvoyées sont cohérentes. Donc, pour répondre à votre question: non, le noyau ne garantit pas la cohérence des fichiers proc lors d'une lecture mais il fournit les moyens aux implémentations de ces fichiers pour assurer la cohérence.


4
Malheureusement, de nombreux fichiers /procne fournissent pas en fait de cohérence. Voir ma réponse pour plus de détails.
Greg Price

3
De plus, __proc_file_read()est déconseillé au profit de seq_file. Voir le commentaire plutôt exaspéré (de Linus) juste au-dessus du long commentaire de bloc.
Greg Price

6

J'ai la source pour Linux 2.6.27.8 à portée de main puisque je suis en train de développer des pilotes sur une cible ARM intégrée.

Le fichier ... linux-2.6.27.8-lpc32xx/net/ipv4/raw.cà la ligne 934 contient, par exemple

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

quelles sorties

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

en fonction raw_sock_seq_show()qui fait partie d'une hiérarchie de fonctions de traitement procfs . Le texte n'est pas généré tant qu'une read()requête n'est pas faite sur le /proc/net/tcpfichier, un mécanisme raisonnable puisque les lectures procfs sont sûrement beaucoup moins courantes que la mise à jour des informations.

Certains pilotes (comme le mien) implémentent la fonction proc_read avec un seul sprintf(). La complication supplémentaire dans l'implémentation des pilotes de base est de gérer une sortie potentiellement très longue qui peut ne pas tenir dans le tampon intermédiaire de l'espace noyau lors d'une seule lecture.

J'ai testé cela avec un programme utilisant un tampon de lecture de 64 Ko, mais il en résulte un tampon d'espace de noyau de 3072 octets dans mon système pour que proc_read renvoie des données. Plusieurs appels avec des pointeurs avancés sont nécessaires pour obtenir plus de texte renvoyé. Je ne sais pas quelle est la bonne façon de rendre les données renvoyées cohérentes lorsque plus d'une entrée / sortie est nécessaire. Certes, chaque entrée dans /proc/net/tcpest auto-cohérente. Il est probable que les lignes côte à côte soient instantanées à des moments différents.


Vraiment désolé, je n'ai pas beaucoup compris. Alors, voulez-vous dire que si j'utilise ifstream, ce sera dangereux, mais si je l'utilise, readce sera sans danger? Ou ifstreamutilise en interne read? Et que proposez-vous?
Kiril Kirov

@Kiril: Désolé pour la confusion. Ceci est une explication de la façon dont les données /proc/net/tcpsont formatées et est complètement indépendante de la façon dont quiconque les lit.
wallyk

1
Oui! Et votre supposition est correcte que différentes lignes (in /proc/net/tcp) ne proviennent pas du même instantané. Voir ma réponse pour quelques explications.
Greg Price

3

À moins de bogues inconnus, il n'y a pas de conditions de /procconcurrence qui conduiraient à la lecture de données corrompues ou à un mélange d'anciennes et de nouvelles données. En ce sens, c'est sûr. Cependant, il y a toujours la condition de /procconcurrence que la plupart des données que vous lisez sont potentiellement obsolètes dès qu'elles sont générées, et même plus au moment où vous en avez la lecture / le traitement. Par exemple, les processus peuvent mourir à tout moment et un nouveau processus peut se voir attribuer le même pid; les seuls identifiants de processus que vous pouvez utiliser sans conditions de concurrence sont vos propres processus enfants ». Il en va de même pour les informations réseau (ports ouverts, etc.) et vraiment la plupart des informations contenues dans /proc. Je considérerais qu'il est mauvais et dangereux de s'appuyer sur des données/procêtre précis, à l'exception des données sur votre propre processus et potentiellement ses processus enfants. Bien sûr, il peut toujours être utile de présenter d'autres informations /procà l'utilisateur / administrateur à des fins d'information / de journalisation / etc. fins.


Je fais cela pour obtenir et utiliser des informations pour mon propre processus (pour mon PID, en utilisant getpid()). Donc, ça doit être sûr.
Kiril Kirov

1
Oui, je considérerais cela comme totalement sûr.
R .. GitHub STOP AIDER ICE

Je ne suis pas d'accord pour dire que les processus enfants se comportent mieux que tout autre processus. En ce qui concerne l' /procinterface, ils ont tous les mêmes faiblesses et forces. Quoi qu'il en soit, l'OP pose des questions sur les informations relatives au pilote de périphérique, pas sur les processus.
wallyk

1
Si pid Nest votre processus enfant, vous pouvez vous assurer que pid Nfait toujours référence à ce même processus (éventuellement terminé) jusqu'à ce que vous waitappeliez une fonction -family dessus. Cela garantit qu'il n'y a pas de courses.
R .. GitHub STOP AIDER ICE

Qu'est-ce que le déluge de -1 et aucune explication?
R .. GitHub STOP HELPING ICE

2

Lorsque vous lisez un fichier / proc, le noyau appelle une fonction qui a été enregistrée à l'avance pour être la fonction "lecture" de ce fichier proc. Voir le__proc_file_read fonction dans fs / proc / generic.c.

Par conséquent, la sécurité de la lecture proc n'est aussi sûre que la fonction que le noyau appelle pour satisfaire la demande de lecture. Si cette fonction verrouille correctement toutes les données qu'elle touche et vous renvoie dans une mémoire tampon, il est alors totalement sûr de lire en utilisant cette fonction. Puisque les fichiers proc comme celui utilisé pour satisfaire les requêtes de lecture vers / proc / net / tcp existent depuis un certain temps et ont fait l'objet d'un examen scrupuleux, ils sont à peu près aussi sûrs que vous pourriez le demander. En fait, de nombreux utilitaires Linux courants reposent sur la lecture à partir du système de fichiers proc et sur le formatage de la sortie d'une manière différente. (Du haut de ma tête, je pense que «ps» et «netstat» font cela).

Comme toujours, vous n'avez pas à me croire sur parole; vous pouvez regarder la source pour calmer vos peurs. La documentation suivante de proc_net_tcp.txt vous indique où les fonctions "read" pour / proc / net / tcp vivent, afin que vous puissiez regarder le code réel qui est exécuté lorsque vous lisez à partir de ce fichier proc et vérifiez par vous-même qu'il n'y a pas risques de verrouillage.

Ce document décrit les interfaces / proc / net / tcp et / proc / net / tcp6.
Notez que ces interfaces sont obsolètes au profit de tcp_diag. Ces interfaces / proc fournissent des informations sur les connexions TCP actuellement actives, et sont implémentées par tcp4_seq_show () dans net / ipv4 / tcp_ipv4.c et tcp6_seq_show () dans net / ipv6 / tcp_ipv6.c, respectivement.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.