Je veux analyser /proc/net/tcp/
, mais est-ce sûr?
Comment ouvrir et lire des fichiers à partir de /proc/
et n'ayez pas peur, qu'un autre processus (ou le système d'exploitation lui-même) le change en même temps?
En général, non. (Donc, la plupart des réponses ici sont fausses.) Il pourrait être sûr , en fonction de la propriété que vous souhaitez. Mais il est facile de se retrouver avec des bogues dans votre code si vous supposez trop sur la cohérence d'un fichier dans /proc
. Par exemple, voir ce bogue qui provenait de l'hypothèse que /proc/mounts
Était un instantané cohérent .
Par exemple:
/proc/uptime
est 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. Donc, même ce petit fichier trivial était soumis à une condition de concurrence jusque-là et se trouve toujours dans la plupart des noyaux d'entreprise. Voir fs/proc/uptime.c
pour la source actuelle, ou le commit qui l'a rendu atomique . Sur un noyau antérieur à 2.6.30, vous pouvez open
le fichier, read
un peu, puis si vous revenez plus tard et read
à nouveau, la pièce que vous obtenez sera incompatible avec la première pièce. (Je viens de le démontrer - essayez-le vous-même pour le plaisir.)
/proc/mounts
est atomique dans un seul appel système read
. Donc, si vous read
tout le fichier d'un coup, vous obtenez un seul instantané cohérent des points de montage sur le système. Cependant, si vous utilisez plusieurs appels système read
- et si le fichier est volumineux, c'est exactement ce qui se passera si vous utilisez des bibliothèques d'E/S normales et ne portez pas une attention particulière à ce problème - vous sera soumis à une condition de course. 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 manquer dans ce que vous voyez. Pour voir qu'il est atomique pour une read()
, regardez m_start()
dans fs/namespace.c
et voyez-le saisir un sémaphore qui garde la liste des points de montage , qu'il conserve jusqu'à m_stop()
, qui est appelée lorsque read()
est terminée. Pour voir ce qui peut mal tourner, voir ce bug de l'année dernière (le même que celui que j'ai lié ci-dessus) dans un logiciel par ailleurs de haute qualité qui lit allègrement /proc/mounts
.
/proc/net/tcp
, qui est en fait celui que vous demandez, est encore moins cohérent que cela. Il est atomique uniquement dans chaque ligne du tableau . Pour voir cela, regardez listening_get_next()
dans net/ipv4/tcp_ipv4.c
et established_get_next()
juste en dessous dans le même fichier, et voyez les verrous qu'ils prennent sur tour à tour. 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 pas de verrous (ou quoi que ce soit d'autre) qui le rendrait cohérent. Ce qui est logique si vous y pensez - la mise en réseau est souvent une partie très occupée du système, donc cela ne vaut pas la peine de présenter une vue cohérente dans cet outil de diagnostic.
L'autre élément qui maintient atomique /proc/net/tcp
Dans chaque ligne est la mise en mémoire tampon dans seq_read()
, que vous pouvez lire dans fs/seq_file.c
. Cela garantit qu'une fois que vous read()
partie d'une ligne, le texte de la ligne entière est conservé dans un tampon afin que la prochaine read()
obtienne le reste de cette ligne avant de commencer une nouvelle un. Le même mécanisme est utilisé dans /proc/mounts
Pour garder chaque ligne atomique même si vous effectuez plusieurs appels read()
, et c'est également le mécanisme que /proc/uptime
Dans les noyaux plus récents utilise pour rester atomique . Ce mécanisme ne pas tampon tout le fichier, car le noyau est prudent sur l'utilisation de la mémoire.
La plupart des fichiers dans /proc
Seront au moins aussi cohérents que /proc/net/tcp
, Avec chaque ligne une image cohérente d'une entrée dans les informations qu'ils fournissent, car la plupart d'entre eux utilisent la même seq_file
Abstraction. Comme l'illustre l'exemple /proc/uptime
, Certains fichiers étaient toujours en cours de migration pour utiliser seq_file
Aussi récemment qu'en 2009; Je parie qu'il y en a encore qui utilisent des mécanismes plus anciens et 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 le changent pendant que vous le lisez, et vous créez probablement un bogue.
Bien que les fichiers dans /proc
Apparaissent comme des fichiers normaux dans l'espace utilisateur, ce ne sont pas vraiment des fichiers mais plutôt des entités qui prennent en charge les opérations de fichiers standard à partir de l'espace utilisateur (open
, read
, close
). Notez que cela est assez différent que d'avoir un fichier ordinaire sur le disque qui est modifié par le noyau.
Tout ce que le noyau fait est d'imprimer son état interne dans sa propre mémoire en utilisant une fonction semblable à sprintf
, et cette mémoire est copiée dans l'espace utilisateur chaque fois que vous émettez un appel système read(2)
.
Le noyau gère ces appels d'une manière entièrement différente de celle des fichiers normaux, ce qui pourrait signifier que l'intégralité de l'instantané des données que vous lirez pourrait être prêt au moment où vous 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'être autrement.
Mon conseil est de jeter un œil à l'implémentation d'un fichier proc dans votre version Unix particulière. Il s'agit vraiment d'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 fichier proc uptime
sous Linux. Notez comment le tampon entier est produit dans la fonction de rappel fournie à single_open
.
/ proc est un système de fichiers virtuel: en fait, il donne juste une vue pratique des internes du noyau. Il est certainement sûr de le lire (c'est pourquoi il est ici) mais c'est risqué à long terme, car l'intérieur de ces fichiers virtuels peut évoluer avec une version plus récente du noyau.
[~ # ~] modifier [~ # ~]
Plus d'informations disponibles dans documentation proc dans la documentation du noyau Linux , chapitre 1.4 Mise en réseau Je ne trouve pas si les informations sur la façon dont les informations évoluent au fil du temps. Je pensais qu'il était figé sur ouvert, mais ne peut pas avoir de réponse définitive.
EDIT2
Selon Sco doc (pas Linux, mais je suis 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 pour 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 spécifiquement garantie pour aucune E/S appliquée au fichier as (espace d'adressage); le contenu de l'espace d'adressage d'un processus peut être modifié simultanément par un LWP de ce processus ou de tout autre processus du système.
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 sûr à l'implémentation d'un fichier proc spécifique d'utiliser correctement cette interface pour s'assurer que ses données retourné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 permet aux implémentations de ces fichiers d'assurer la cohérence.
J'ai la source pour Linux 2.6.27.8 à portée de main car je fais actuellement le développement de pilotes sur une cible intégrée ARM.
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));
qui sort
[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
...
dans la fonction raw_sock_seq_show()
qui fait partie d'une hiérarchie de procfs fonctions de gestion. Le texte n'est pas généré tant qu'une demande read()
n'est pas faite du fichier /proc/net/tcp
, Un mécanisme raisonnable car 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 une seule sprintf()
. La complication supplémentaire dans l'implémentation des pilotes principaux est de gérer une sortie potentiellement très longue qui peut ne pas tenir dans le tampon d'espace noyau intermédiaire pendant une seule lecture.
J'ai testé cela avec un programme utilisant un tampon de lecture de 64 Ko, mais cela donne un tampon d'espace 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 que la quantité de texte retournée. Je ne sais pas quelle est la bonne façon de rendre les données retournées cohérentes lorsque plusieurs E/S sont nécessaires. Certes, chaque entrée dans /proc/net/tcp
Est cohérente. Il est probable que les lignes côte à côte soient instantanées à différents moments.
À court de bugs inconnus, il n'y a pas de conditions de concurrence dans /proc
qui entraînerait la lecture de données corrompues ou d'un mélange d'anciennes et de nouvelles données. En ce sens, c'est sûr. Cependant, il existe toujours la condition de concurrence qu'une grande partie des données que vous lisez à partir de /proc
est potentiellement obsolète dès qu'il est généré, et même plus au moment où vous arrivez à le lire/le traiter. 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 la plupart des informations dans /proc
. Je considérerais comme une pratique mauvaise et dangereuse de s'appuyer sur des données dans /proc
étant précis, à l'exception des données concernant votre propre processus et potentiellement ses processus enfants. Bien sûr, il peut être utile de présenter d'autres informations de /proc
à l'utilisateur/administrateur pour information/journalisation/etc. fins.
Lorsque vous lisez à partir d'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 revient dans un tampon, alors il est complètement sûr de lire en utilisant cette fonction. Étant donné que les fichiers proc comme celui utilisé pour satisfaire les demandes de lecture à/proc/net/tcp existent depuis un certain temps et ont fait l'objet d'une révision scrupuleuse, 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 ça).
Comme toujours, vous n'avez pas à prendre ma Parole pour cela; vous pouvez regarder la source pour calmer vos peurs. La documentation suivante de proc_net_tcp.txt vous indique où se trouvent les fonctions "read" pour/proc/net/tcp live, 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 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 .