¿Es seguro analizar un archivo / proc /?


152

Quiero analizar /proc/net/tcp/, pero ¿es seguro?

¿Cómo debo abrir y leer archivos /proc/y no tener miedo de que algún otro proceso (o el sistema operativo en sí) lo cambie al mismo tiempo?


29
+1. Esa es una muy buena pregunta. Solo desearía tener la respuesta, pero espero averiguarlo ya que he hecho ese tipo de cosas bastante antes.
paxdiablo

1
Estoy bastante seguro de que solo leerlo le dará una lista de conexiones, más el UID que posee cada una, tal como estaban cuando lo abrió . Sin embargo, no puedo encontrar eso documentado, por lo que hago de este un comentario por ahora.
Tim Post

3
Obviamente, la respuesta simple es sí, ya que no es un archivo; leerlo siempre debe ser seguro. Es posible que las respuestas no sean consistentes las veces que lo lea, pero será seguro.
Rory Alsop

Es por eso que debería usar sysctl en su lugar. (también es menos syscalls)
Good Person

@GoodPerson: ¿cómo puede sysctlayudarme a analizar un /proc/net/tcp/archivo, por ejemplo?
Kiril Kirov

Respuestas:


111

En general, no. (Por lo tanto, la mayoría de las respuestas aquí son incorrectas). Puede ser seguro, dependiendo de la propiedad que desee. Pero es fácil terminar con errores en su código si supone demasiado sobre la consistencia de un archivo /proc. Por ejemplo, vea este error que vino de suponer que /proc/mountsfue una instantánea consistente .

Por ejemplo:

  • /proc/uptimees totalmente atómico , como alguien mencionó en otra respuesta, pero solo desde Linux 2.6.30 , que tiene menos de dos años. Por lo tanto, incluso este pequeño archivo trivial estuvo sujeto a una condición de carrera hasta entonces, y todavía está en la mayoría de los núcleos empresariales. Consulte fs/proc/uptime.cla fuente actual o el commit que lo convirtió en atómico . En un kernel anterior a 2.6.30, puede guardar openel archivo, readun poco, luego, si luego regresa una y readotra vez, la pieza que obtenga será inconsistente con la primera pieza. (Acabo de demostrar esto, pruébalo tú mismo por diversión).

  • /proc/mountses atómico dentro de una sola readllamada al sistema. Entonces, si tiene readtodo el archivo de una vez, obtendrá una única instantánea consistente de los puntos de montaje en el sistema. Sin embargo, si usa varias readllamadas al sistema, y ​​si el archivo es grande, esto es exactamente lo que sucederá si usa bibliotecas de E / S normales y no presta especial atención a este problema: estará sujeto a una carrera condición. No solo no obtendrá una instantánea consistente, sino que los puntos de montaje que estaban presentes antes de comenzar y que nunca dejaron de estar presentes podrían perderse en lo que ve. Para ver que es atómica por una read(), ver m_start()enfs/namespace.c y verlo agarrar un semáforo que los guardias de la lista de puntos de montaje, que se mantiene hasta m_stop()que se llama cuando elread()está hecho. Para ver qué puede salir mal, vea este error del año pasado (el mismo que vinculé anteriormente) en otro software de alta calidad que se lee alegremente /proc/mounts.

  • /proc/net/tcp, que es el que realmente estás preguntando, es incluso menos consistente que eso. Es atómico solo dentro de cada fila de la tabla . Para ver esto, mire listening_get_next()adentronet/ipv4/tcp_ipv4.c y established_get_next()justo debajo en el mismo archivo, y vea los bloqueos que sacan en cada entrada a su vez. No tengo un código de repro a mano para demostrar la falta de coherencia de fila a fila, pero no hay bloqueos allí (o cualquier otra cosa) que lo haga coherente. Lo que tiene sentido si lo piensa: las redes a menudo son una parte súper ocupada del sistema, por lo que no vale la pena la sobrecarga para presentar una vista coherente en esta herramienta de diagnóstico.

La otra pieza que mantiene /proc/net/tcpatómica dentro de cada fila es el almacenamiento temporal en seq_read()que se puede leer enfs/seq_file.c . Esto garantiza que una vez que formes read()parte de una fila, el texto de toda la fila se mantenga en un búfer para que la siguiente read()obtenga el resto de esa fila antes de comenzar una nueva. Se utiliza el mismo mecanismo /proc/mountspara mantener atómica cada fila, incluso si se realizan varias read()llamadas, y también es el mecanismo que /proc/uptimeen los núcleos más nuevos se utiliza para mantenerse atómico. Ese mecanismo no almacena todo el archivo en el búfer, porque el núcleo es cauteloso con el uso de la memoria.

La mayoría de los archivos /procserán al menos tan consistentes como /proc/net/tcp, con cada fila una imagen consistente de una entrada en cualquier información que proporcionen, porque la mayoría de ellos usan la misma seq_fileabstracción. Sin /proc/uptimeembargo, como lo ilustra el ejemplo, algunos archivos todavía se estaban migrando para su uso seq_fileen 2009; Apuesto a que todavía hay algunos que usan mecanismos más antiguos y ni siquiera tienen ese nivel de atomicidad. Estas advertencias rara vez se documentan. Para un archivo dado, su única garantía es leer la fuente.

En el caso de /proc/net/tcp, puede leerlo y analizar cada línea sin temor. Pero si intenta sacar conclusiones de varias líneas a la vez, tenga cuidado, otros procesos y el núcleo lo están cambiando mientras lo lee, y probablemente esté creando un error.


1
¿Qué pasa con readdir atomicity? como leer / proc / self / fd? ¿es seguro?
socketpair

No es que responde a la pregunta, pero para añadir acerca de cómo comprobar el tiempo de funcionamiento se puede utilizar clock_gettime(2)con CLOCK_MONOTONIC(aunque tal vez hay un detalle técnico que estoy al tanto de aquí, pero yo personalmente sólo he visto que con el tiempo ya de arranque). Para Linux también tiene la opción de sysinfo(2).
Pryftan

44

Aunque los archivos en /procaparecen como archivos normales en el espacio de usuario, que no son realmente los archivos, sino más bien entidades que apoyan las operaciones de archivo estándar de espacio de usuario ( open, read, close). Tenga en cuenta que esto es muy diferente a tener un archivo ordinario en el disco que el núcleo está cambiando.

Todo lo que hace el kernel es imprimir su estado interno en su propia memoria utilizando una sprintffunción similar, y esa memoria se copia en el espacio del usuario cada vez que emite una read(2)llamada al sistema.

El núcleo maneja estas llamadas de una manera completamente diferente a la de los archivos normales, lo que podría significar que la instantánea completa de los datos que leerá podría estar lista en ese momento open(2), mientras que el núcleo se asegura de que las llamadas concurrentes sean consistentes y atómicas. No lo he leído en ninguna parte, pero en realidad no tiene sentido ser de otra manera.

Mi consejo es que eche un vistazo a la implementación de un archivo proc en su sabor particular de Unix. Esto es realmente un problema de implementación (como es el formato y el contenido de la salida) que no se rige por un estándar.

El ejemplo más simple sería la implementación del uptimearchivo proc en Linux. Observe cómo se produce todo el búfer en la función de devolución de llamada suministrada a single_open.


3
@Ignacio: Solo estoy apuntando el OP en esta dirección porque me dejó la impresión de que él piensa que los procarchivos son archivos ordinarios abiertos para ser escritos por el núcleo.
Blagovest Buyukliev

44
Su consejo para mirar la implementación del archivo específico es bueno. Desafortunadamente, la suposición de que todo está capturado open()es incorrecto para muchos archivos, y en particular para lo /proc/net/tcpque le preocupa al OP. Esto tiene sentido si piensa en el costo de proporcionar esa semántica: tendría que hacer algo como bloquear las estructuras de datos internas que registran todas esas conexiones TCP, lo que en un sistema ocupado es un desastre, incluso si solo lo mantiene durante mucho tiempo suficiente para escanear y formatear los datos en un búfer. Vea mi respuesta para obtener detalles sobre lo que realmente sucede.
Greg Price

16

/ proc es un sistema de archivos virtual: de hecho, solo ofrece una vista conveniente de los componentes internos del núcleo. Definitivamente es seguro leerlo (es por eso que está aquí) pero es arriesgado a largo plazo, ya que la parte interna de estos archivos virtuales puede evolucionar con la versión más nueva del kernel.

EDITAR

Más información disponible en la documentación del proceso en Linux kernel doc , capítulo 1.4 Redes No puedo encontrar si la información evoluciona con el tiempo. Pensé que estaba congelado al abrir, pero no puedo tener una respuesta definitiva.

EDIT2

Según Sco doc (no Linux, pero estoy bastante seguro de que todos los sabores de * nix se comportan así)

Aunque el estado del proceso y, en consecuencia, el contenido de los archivos / proc pueden cambiar de un instante a otro, se garantiza que una sola lectura (2) de un archivo / proc devuelva una representación de estado `` sana '', es decir, la lectura será Una instantánea atómica del estado del proceso. No se aplica dicha garantía a las lecturas sucesivas aplicadas a un archivo / proc para un proceso en ejecución. Además, la atomicidad no está específicamente garantizada para ninguna E / S aplicada al archivo as (espacio de direcciones); el contenido del espacio de direcciones de cualquier proceso puede ser modificado simultáneamente por un LWP de ese proceso o cualquier otro proceso en el sistema.


3
"Yo creo que" ? Sería bueno tener una respuesta definitiva :)
static_rtti

Dada la implementación de / proc en el kernel, esto también es válido para Linux. Si lee un archivo procfs en una sola llamada de lectura, es coherente, por supuesto, suponiendo que el archivo proc que leyó se haya implementado correctamente en el kernelside.
Erik

8
No creo que pueda encontrar una fuente de información peor que SCO, y tratar de tratarlo proccomo si tuviera un comportamiento similar entre diferentes núcleos (o incluso suponiendo que exista, no tiene que hacerlo en un sistema Unix ) te dará un mundo de dolor.
Nicholas Knight el

1
@Nicholas: bueno, no pude encontrar alguna respuesta definitiva en el documento del kernel, siéntase libre de señalarlo si lo sabe.
Bruce

2
Es interesante que los documentos de la OCS digan eso. Desafortunadamente, no siempre es cierto en Linux, y en particular no es cierto para /proc/net/tcp, que es la principal preocupación del OP. Por el contrario, solo cada fila individual en la salida es atómica. Vea mi respuesta para más detalles.
Greg Price

14

La API procfs en el kernel de Linux proporciona una interfaz para asegurarse de que las lecturas devuelven datos consistentes. Lee los comentarios en __proc_file_read. El elemento 1) en el bloque de comentarios grandes explica esta interfaz.

Dicho esto, por supuesto, depende de la implementación de un archivo de proceso específico utilizar esta interfaz correctamente para asegurarse de que los datos devueltos sean consistentes. Entonces, para responder a su pregunta: no, el núcleo no garantiza la coherencia de los archivos de proceso durante una lectura, pero proporciona los medios para que las implementaciones de esos archivos proporcionen coherencia.


44
Desafortunadamente, muchos archivos /procno proporcionan consistencia. Vea mi respuesta para más detalles.
Greg Price

3
Además, __proc_file_read()está en desuso a favor de seq_file. Vea el comentario bastante exasperado (de Linus) justo arriba del comentario de bloque largo.
Greg Price

6

Tengo a mano la fuente de Linux 2.6.27.8, ya que actualmente estoy desarrollando controladores en un objetivo ARM incorporado.

El archivo ... linux-2.6.27.8-lpc32xx/net/ipv4/raw.cen la línea 934 contiene, por ejemplo

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

que salidas

[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 la función raw_sock_seq_show()que forma parte de una jerarquía de funciones de manejo de procfs . El texto no se genera hasta que una read()petición es hecha del /proc/net/tcparchivo, un mecanismo razonable, ya que procfs lee seguramente son mucho menos comunes que la actualización de la información.

Algunos controladores (como el mío) implementan la función proc_read con un solo sprintf(). La complicación adicional en la implementación de los controladores centrales es manejar una salida potencialmente muy larga que puede no encajar en el búfer intermedio del espacio del núcleo durante una sola lectura.

Probé eso con un programa que usa un búfer de lectura de 64K pero da como resultado un búfer de espacio en el núcleo de 3072 bytes en mi sistema para que proc_read devuelva datos. Se necesitan múltiples llamadas con punteros avanzados para obtener más que ese texto devuelto. No sé cuál es la forma correcta de hacer que los datos devueltos sean consistentes cuando se necesita más de una E / S. Ciertamente, cada entrada /proc/net/tcpes autoconsistente. Existe cierta probabilidad de que las líneas una al lado de la otra sean instantáneas en diferentes momentos.


Realmente lo siento, no lo entendí mucho. Entonces, ¿quiere decir que si lo uso ifstream, será inseguro, pero si lo uso readserá seguro? O ifstreamutiliza internamente read? ¿Y qué sugieres?
Kiril Kirov

@ Kiril: Perdón por la confusión. Esta es una explicación de cómo /proc/net/tcpse formatean los datos y es completamente independiente de cómo alguien los lea.
wallyk

1
¡Sí! Y su suposición es correcta de que las diferentes líneas (in /proc/net/tcp) no provienen de la misma instantánea. Vea mi respuesta para alguna explicación.
Greg Price

3

A falta de errores desconocidos, no hay condiciones de carrera /procque conduzcan a la lectura de datos corruptos o una combinación de datos antiguos y nuevos. En este sentido, es seguro. Sin embargo, todavía existe la condición de carrera de que gran parte de los datos que lees /procestán potencialmente desactualizados tan pronto como se generan, e incluso más para cuando los leas / proceses. Por ejemplo, los procesos pueden morir en cualquier momento y se puede asignar un nuevo proceso al mismo pid; los únicos identificadores de proceso que puedes usar sin condiciones de carrera son tus propios procesos secundarios '. Lo mismo ocurre con la información de red (puertos abiertos, etc.) y realmente la mayoría de la información en /proc. Consideraría una práctica mala y peligrosa confiar en cualquier información en/procser exacto, excepto los datos sobre su propio proceso y potencialmente sus procesos secundarios. Por supuesto, aún puede ser útil presentar otra información /procal usuario / administrador para información / registro / etc. propósitos


Estoy haciendo esto para obtener y usar información para mi propio proceso (para mi PID, usando getpid()). Entonces, tiene que estar a salvo.
Kiril Kirov el

1
Sí, lo consideraría completamente seguro.
R .. GitHub DEJA DE AYUDAR AL HIELO

No estoy de acuerdo con que los procesos secundarios se comporten mejor que cualquier otro proceso. En lo que respecta a la /procinterfaz, todos tienen las mismas debilidades y fortalezas. De todos modos, el OP pregunta por la información relacionada con el controlador del dispositivo, no por los procesos.
wallyk

1
Si pid Nes su proceso hijo, puede asegurarse de que pid Ntodavía se refiera a ese mismo proceso (posiblemente terminado) hasta que llame a una waitfunción -family en él. Esto asegura que no hay carreras.
R .. GitHub DEJA DE AYUDAR AL HIELO

¿Qué pasa con la avalancha de -1 y sin explicación?
R .. GitHub DEJA DE AYUDAR A ICE

2

Cuando lee desde un archivo / proc, el núcleo está llamando a una función que se ha registrado previamente para que sea la función de "lectura" para ese archivo proc. Vea la __proc_file_readfunción en fs / proc / generic.c.

Por lo tanto, la seguridad de la lectura de proceso es tan segura como la función que el núcleo llama para satisfacer la solicitud de lectura. Si esa función bloquea correctamente todos los datos que toca y te los devuelve en un búfer, entonces es completamente seguro leerlos usando esa función. Dado que los archivos proc como el que se utiliza para satisfacer las solicitudes de lectura a / proc / net / tcp han existido por un tiempo y han sido objeto de una revisión escrupulosa, son tan seguros como podría pedir. De hecho, muchas utilidades comunes de Linux se basan en la lectura del sistema de archivos proc y el formato de la salida de una manera diferente. (Fuera de mi cabeza, creo que 'ps' y 'netstat' hacen esto).

Como siempre, no tienes que creer mi palabra; puedes mirar la fuente para calmar tus miedos. La siguiente documentación de proc_net_tcp.txt le dice dónde funcionan las funciones de "lectura" para / proc / net / tcp, para que pueda ver el código real que se ejecuta cuando lee de ese archivo de proceso y verificar por sí mismo que no hay Riesgos de bloqueo.

Este documento describe las interfaces / proc / net / tcp y / proc / net / tcp6.
Tenga en cuenta que estas interfaces están en desuso a favor de tcp_diag. Estas interfaces / proc proporcionan información sobre las conexiones TCP actualmente activas y se implementan mediante tcp4_seq_show () en net / ipv4 / tcp_ipv4.c y tcp6_seq_show () en net / ipv6 / tcp_ipv6.c, respectivamente.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.