¿Por qué no puedo `tail -f / proc / $ pid / fd / 1`?

10

Escribí un script simple que es echosu PID:

#/bin/bash

while true; do
    echo $$;
    sleep 0.5;
done

Estoy ejecutando dicho script (dice 3844una y otra vez) en una terminal e intento tailel descriptor de archivo en otra:

$ tail -f /proc/3844/fd/1

No imprime nada en la pantalla y se cuelga hasta ^c. ¿Por qué?

Además, todos los descriptores de archivo STD (IN / OUT / ERR) enlazan a los mismos pts:

$ ls -l /proc/3844/fd/
total 0
lrwx------ 1 mg mg 64 sie 29 13:42 0 -> /dev/pts/14
lrwx------ 1 mg mg 64 sie 29 13:42 1 -> /dev/pts/14
lrwx------ 1 mg mg 64 sie 29 13:42 2 -> /dev/pts/14
lr-x------ 1 mg mg 64 sie 29 13:42 254 -> /home/user/test.sh
lrwx------ 1 mg mg 64 sie 29 13:42 255 -> /dev/pts/14

¿Esto es normal?

Ejecutando Ubuntu GNOME 14.04.

Si cree que esta pregunta pertenece a SO o SU en lugar de UL, dígaselo.

cprn
fuente

Respuestas:

13

Hacer una stracede las tail -f, que explica todo. La parte interesante:

13791 fstat(3, {st_mode=S_IFREG|0644, st_size=139, ...}) = 0
13791 fstatfs(3, {...}) = 0
13791 inotify_init()                    = 4
13791 inotify_add_watch(4, "/path/to/file", IN_MODIFY|IN_ATTRIB|IN_DELETE_SELF|IN_MOVE_SELF) = 1
13791 fstat(3, {st_mode=S_IFREG|0644, st_size=139, ...}) = 0
13791 read(4, 0xd981c0, 26)             = -1 EINTR (Interrupted system call)

¿Que hace? Configura un inotifycontrolador para el archivo y luego espera hasta que ocurra algo con este archivo. Si el núcleo dice a tailtravés de este controlador inotify, que el archivo cambió (normalmente, se adjuntó), entonces tail1) busca 2) lee los cambios 3) los escribe en la pantalla.

/proc/3844/fd/1en su sistema hay un enlace simbólico /dev/pts/14, que es un dispositivo de caracteres. No existe algo así como un "mapa de memoria", al que se pueda acceder con eso. Por lo tanto, no hay nada cuyos cambios se puedan firmar en la notificación, ya que no hay un disco o área de memoria a la que se pueda acceder.

Este dispositivo de caracteres es un terminal virtual, que prácticamente funciona como si fuera un socket de red. Los programas que se ejecutan en este terminal virtual se conectan a este dispositivo (como si se conectara a un puerto TCP) y escriben en lo que quieren escribir. También hay cosas más complejas, por ejemplo, bloquear la pantalla, secuencias de control de terminal y demás, que normalmente se manejan mediante ioctl()llamadas.

Creo que de alguna manera quieres ver una terminal virtual. Se puede hacer en Linux, pero no es tan simple, necesita algunas funciones de proxy de red y un poco de uso complicado de estas ioctl()llamadas. Pero hay herramientas que pueden hacer eso.

Actualmente no puedo recordar qué paquete de Debian tiene la herramienta para este objetivo, pero con un poco de búsqueda en Google probablemente pueda encontrarlo fácilmente.

Extensión: como @Jajesh mencionó aquí (dale un +1 si me diste), la herramienta se llama watch.

Extensión # 2: @kelnos mencionó, un simple cat /dev/pts/14también fue suficiente. Lo intenté, y sí, funcionó, pero no correctamente. No experimentamos mucho con eso, pero me parece como si una salida de entrar en esa terminal virtual ido bien a la catorden, o en su ubicación original, y nunca a ambos. Pero no es seguro.

peterh - Restablece a Monica
fuente
la respuesta de Peter acerca de tailes correcta (el bit de observación de inotify), pero es incorrecto porque en realidad es muy simple hacer lo que quieres: simplemente usa en catlugar de tail.
kelnos
@kelnos Gracias, lo intentaré y extenderé mi respuesta con los resultados.
peterh - Restablece a Mónica el
@kelnos cattampoco funciona para mí, se cuelga de la misma manera que la cola y todo lo que puedo hacer es hacerlo ctrl+c.
cprn
1
Aún no lo entiendo. Cambié echo $$a, echo $$ >> fooasí que ahora hay un archivo y el proceso lo abre y lo agrega cada 0.5 segundos. Todavía no puedo acceder a través del descriptor de archivo y todos los descriptores de archivo en /proc/$pid/fd/(pero 254 que enlaza con el test.shscript en sí) enlazan /dev/pts/14. ¿Cómo funciona el acceso a bash en el fooque escribe?
cprn
1
extraño, parece que solo funciona en algunas situaciones. usando el script en la pregunta, no funciona. pero si hago "echo $$" en un shell, y luego cat FD 1 en ese pid en otro shell, todo lo que escribo en el primer shell se repite en el segundo.
kelnos
4

Los archivos /dev/ptsno son archivos normales, son controladores para terminales virtuales. Un ptscomportamiento para leer y escribir no es simétrico (es decir, lo que está escrito allí puede leerse más tarde, como un archivo normal o un fifo / pipe), sino que está mediado por el proceso que creó el terminal virtual: algunos comunes son xterm o ssh o agetty o screen. El proceso de control generalmente enviará pulsaciones de teclas a procesos que leen el ptsarchivo y muestran en pantalla lo que escriben en el pts.

Por lo tanto, tail -f /dev/pts/14imprimirá las teclas que toque en el terminal desde el que comenzó su script, y si lo hace, echo meh > /dev/pts/14el mehmensaje aparecerá en el terminal.

pqnet
fuente
Tienes razón al decir que puedo escribir en el dispositivo pts pero parece que no puedo leerlo. Como en: tail -f /dev/pts/14no imprime las teclas que toco en ese terminal. Sin embargo, es una respuesta interesante. Gracias.
cprn
0

Hace algún tiempo me encontré con un poco solución que a veces responde a la necesidad de revisar lo que se da salida a la salida estándar, suponiendo que tiene una piddel proceso y se puede desnudar los ojos hostiles resultados:

sudo strace -p $pid 2>&1 | grep write\(
cprn
fuente
-2

Supongo que, para esto, en lugar de seguir, lo que debes hacer es mirar la salida.

$ watch -n2 ls -l /proc/3844/fd/

Espero que esto sea lo que necesitas.

Jayesh
fuente
3
Este comando mostrará la lista de fds abiertos cada 2 segundos, no la salida de contenido en stdout.
Ángel
Ángel, cierto. Podría usar el reloj con un gato para ver el resultado en el descriptor que desea monitorear. Supongo que @ Peter-Horvath, dio la explicación perfecta para la pregunta.
Jayesh
Yo sé watch. Lo que intento hacer es echar un vistazo a la salida del proceso que ya se está ejecutando, por lo watchque no ayuda.
cprn