Una vez, durante una entrevista, me preguntaron: ¿qué harás si encuentras un servicio que no funciona porque el disco se ha quedado sin espacio?
Por supuesto, le respondí que vería lo que ocupaba este lugar y, si era posible, limpiaría el lugar.
Entonces el entrevistador preguntó: ¿Qué pasa si no hay espacio libre en la partición, pero tampoco ves ningún archivo que ocupe todo el espacio?
A esto le dije que siempre puede mirar los descriptores de archivos abiertos, por ejemplo con el comando lsof, y comprender qué aplicación ha ocupado todo el espacio disponible, y luego puede actuar según las circunstancias, dependiendo de si los datos son necesarios. .
El entrevistador me interrumpió en la última palabra y agregó a su pregunta: "¿Supongamos que no necesitamos los datos, es solo un registro de depuración, pero la aplicación no funciona porque no puede escribir una depuración"?
"Está bien", respondí, "podemos desactivar la depuración en la configuración de la aplicación y reiniciarla".
El entrevistador objetó: "No, no podemos reiniciar la aplicación, todavía tenemos datos importantes almacenados en la memoria y clientes importantes están conectados al servicio en sí, a los que no podemos obligar a volver a conectarse".
"Está bien", dije, "si no podemos reiniciar la aplicación y los datos no son importantes para nosotros, entonces simplemente podemos borrar este archivo abierto a través del descriptor de archivo, incluso si no lo vemos en el comando ls". en el sistema de archivos”.
El entrevistador estaba contento, pero yo no.
Entonces pensé, ¿por qué la persona que prueba mis conocimientos no profundiza más? ¿Pero qué pasa si los datos son importantes después de todo? ¿Qué pasa si no podemos reiniciar un proceso y el proceso escribe en el sistema de archivos en una partición que no tiene espacio libre? ¿Qué pasa si no podemos perder no solo los datos que ya se han escrito, sino también los datos que este proceso escribe o intenta escribir?
Sombrero grande
Al principio de mi carrera, intenté crear una pequeña aplicación que necesitaba almacenar información del usuario. Y luego pensé, ¿cómo puedo hacer coincidir al usuario con sus datos? Por ejemplo, tengo a Ivanov Ivan Ivanovich y él tiene cierta información, pero ¿cómo puedo hacerme amigo de ellos? Puedo señalar directamente que el perro llamado “Tuzik” pertenece a este mismo Iván. Pero, ¿qué pasa si cambia su nombre y en lugar de Ivan se convierte, por ejemplo, en Olya? Entonces resultará que nuestra Olya Ivanovna Ivanova ya no tendrá perro y nuestro Tuzik seguirá perteneciendo al inexistente Iván. Una base de datos que le daba a cada usuario un identificador único (ID) ayudó a resolver este problema, y mi Tuzik estaba vinculado a este ID, que, de hecho, era solo un número de serie. Por lo tanto, el dueño del as tenía el número de identificación 2, y en algún momento Iván estuvo bajo este ID, y luego Olya pasó a tener el mismo ID. El problema de la humanidad y la ganadería quedó prácticamente resuelto.
Descriptor de archivo
El problema del archivo y del programa que trabaja con este archivo es aproximadamente el mismo que el de nuestro perro y nuestro hombre. Supongamos que abrí un archivo llamado ivan.txt y comencé a escribir la palabra tuzik en él, pero solo logré escribir la primera letra "t" en el archivo, y alguien le cambió el nombre a este archivo, por ejemplo, a olya.txt. Pero el archivo sigue siendo el mismo y todavía quiero registrar mi as en él. Cada vez que una llamada al sistema abre un archivo
En Linux, la biblioteca libc abre 3 archivos descriptores para cada aplicación (proceso) en ejecución, numerados 0,1,2. Más información se puede encontrar en los enlaces.
- El descriptor de archivo 0 se llama STDIN y está asociado con la entrada de la aplicación.
- El descriptor de archivo 1 se llama STDOUT y lo utilizan las aplicaciones para generar datos, como comandos de impresión.
- El descriptor de archivo 2 se llama STDERR y las aplicaciones lo utilizan para generar mensajes de error.
Si en su programa abre algún archivo para leer o escribir, lo más probable es que obtenga la primera identificación gratuita y será la número 3.
La lista de descriptores de archivos se puede ver para cualquier proceso si conoce su PID.
Por ejemplo, abramos la consola bash y miremos el PID de nuestro proceso.
[user@localhost ]$ echo $$
15771
En la segunda consola ejecutemos
[user@localhost ]$ ls -lah /proc/15771/fd/
total 0
dr-x------ 2 user user 0 Oct 7 15:42 .
dr-xr-xr-x 9 user user 0 Oct 7 15:42 ..
lrwx------ 1 user user 64 Oct 7 15:42 0 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:42 1 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:42 2 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:42 255 -> /dev/pts/21
Puede ignorar con seguridad el descriptor de archivo número 255 para los fines de este artículo; fue abierto para sus necesidades por bash y no por la biblioteca vinculada.
Ahora los 3 archivos descriptores están asociados con el dispositivo pseudo terminal
[user@localhost ]$ echo "hello world" > /proc/15771/fd/0
Y en la primera consola veremos.
[user@localhost ]$ hello world
Redireccionamiento y canalización
Puede anular fácilmente estos 3 archivos descriptores en cualquier proceso, incluido bash, por ejemplo, a través de una tubería que conecta dos procesos, consulte
[user@localhost ]$ cat /dev/zero | sleep 10000
Puede ejecutar este comando usted mismo con trazo -f y ver qué pasa dentro, pero te lo contaré brevemente.
Nuestro proceso bash principal con PID 15771 analiza nuestro comando y comprende exactamente cuántos comandos queremos ejecutar; en nuestro caso hay dos: cat y sleep. Bash sabe que necesita crear dos procesos secundarios y fusionarlos en una sola tubería. En total, bash necesitará 2 procesos secundarios y una tubería.
Bash ejecuta una llamada al sistema antes de crear procesos secundarios
Para el proceso principal, parece que ya existe una tubería, pero aún no hay procesos secundarios:
PID command
15771 bash
lrwx------ 1 user user 64 Oct 7 15:42 0 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:42 1 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:42 2 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:42 3 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct 7 15:42 4 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct 7 15:42 255 -> /dev/pts/21
Luego usando la llamada al sistema
PID command
15771 bash
lrwx------ 1 user user 64 Oct 7 15:42 0 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:42 1 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:42 2 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:42 3 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct 7 15:42 4 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct 7 15:42 255 -> /dev/pts/21
PID command
9004 bash
lrwx------ 1 user user 64 Oct 7 15:57 0 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:57 1 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:57 2 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:57 3 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct 7 15:57 4 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct 7 15:57 255 -> /dev/pts/21
PID command
9005 bash
lrwx------ 1 user user 64 Oct 7 15:57 0 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:57 1 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:57 2 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:57 3 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct 7 15:57 4 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct 7 15:57 255 -> /dev/pts/21
No olvides que clone clona el proceso junto con todos los descriptores de archivos, por lo que serán los mismos en el proceso padre y en los hijos. El trabajo del proceso principal con PID 15771 es monitorear los procesos secundarios, por lo que simplemente espera una respuesta de los hijos.
Por lo tanto, no necesita tubería y cierra los descriptores de archivo numerados 3 y 4.
En el primer proceso child bash con PID 9004, la llamada al sistema
En el segundo proceso hijo con PID 9005, bash usa dup2 para cambiar el descriptor de archivo STDIN número 0. Ahora todo lo que leerá nuestro segundo bash con PID 9005 se leerá desde la tubería.
Después de esto, los descriptores de archivos numerados 3 y 4 también se cierran en los procesos secundarios, ya que ya no se utilizan.
Ignoro deliberadamente el descriptor de archivo 255; el propio bash lo utiliza para fines internos y también se cerrará en los procesos secundarios.
A continuación, en el primer proceso hijo con PID 9004, bash comienza a utilizar una llamada al sistema.
En el segundo proceso hijo con PID 9005, bash ejecuta el segundo ejecutable que especificamos, en nuestro caso /usr/bin/sleep.
La llamada al sistema exec no cierra los identificadores de archivos a menos que se hayan abierto con el indicador O_CLOEXEC en el momento en que se realizó la llamada abierta. En nuestro caso, después de ejecutar los archivos ejecutables, se guardarán todos los descriptores de archivos actuales.
Consultar en la consola:
[user@localhost ]$ pgrep -P 15771
9004
9005
[user@localhost ]$ ls -lah /proc/15771/fd/
total 0
dr-x------ 2 user user 0 Oct 7 15:42 .
dr-xr-xr-x 9 user user 0 Oct 7 15:42 ..
lrwx------ 1 user user 64 Oct 7 15:42 0 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:42 1 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:42 2 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:42 255 -> /dev/pts/21
[user@localhost ]$ ls -lah /proc/9004/fd
total 0
dr-x------ 2 user user 0 Oct 7 15:57 .
dr-xr-xr-x 9 user user 0 Oct 7 15:57 ..
lrwx------ 1 user user 64 Oct 7 15:57 0 -> /dev/pts/21
l-wx------ 1 user user 64 Oct 7 15:57 1 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct 7 15:57 2 -> /dev/pts/21
lr-x------ 1 user user 64 Oct 7 15:57 3 -> /dev/zero
[user@localhost ]$ ls -lah /proc/9005/fd
total 0
dr-x------ 2 user user 0 Oct 7 15:57 .
dr-xr-xr-x 9 user user 0 Oct 7 15:57 ..
lr-x------ 1 user user 64 Oct 7 15:57 0 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct 7 15:57 1 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:57 2 -> /dev/pts/21
[user@localhost ]$ ps -up 9004
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
user 9004 0.0 0.0 107972 620 pts/21 S+ 15:57 0:00 cat /dev/zero
[user@localhost ]$ ps -up 9005
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
user 9005 0.0 0.0 107952 360 pts/21 S+ 15:57 0:00 sleep 10000
Como puedes ver, el número único de nuestra tubería es el mismo en ambos procesos. Así tenemos una conexión entre dos procesos diferentes con el mismo padre.
Para aquellos que no están familiarizados con las llamadas al sistema que usa bash, les recomiendo ejecutar los comandos a través de strace y ver qué sucede internamente, por ejemplo así:
strace -s 1024 -f bash -c "ls | grep hello"
Volvamos a nuestro problema con poco espacio en disco e intentamos guardar datos sin reiniciar el proceso. Escribamos un pequeño programa que escriba aproximadamente 1 megabyte por segundo en el disco. Además, si por alguna razón no pudimos escribir datos en el disco, simplemente lo ignoraremos e intentaremos escribir los datos nuevamente en un segundo. En el ejemplo que estoy usando Python, puedes usar cualquier otro lenguaje de programación.
[user@localhost ]$ cat openforwrite.py
import datetime
import time
mystr="a"*1024*1024+"n"
with open("123.txt", "w") as f:
while True:
try:
f.write(str(datetime.datetime.now()))
f.write(mystr)
f.flush()
time.sleep(1)
except:
pass
Ejecutemos el programa y miremos los descriptores de archivos.
[user@localhost ]$ python openforwrite.py &
[1] 3762
[user@localhost ]$ ps axuf | grep [o]penforwrite
user 3762 0.0 0.0 128600 5744 pts/22 S+ 16:28 0:00 | _ python openforwrite.py
[user@localhost ]$ ls -la /proc/3762/fd
total 0
dr-x------ 2 user user 0 Oct 7 16:29 .
dr-xr-xr-x 9 user user 0 Oct 7 16:29 ..
lrwx------ 1 user user 64 Oct 7 16:29 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 7 16:29 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 7 16:29 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct 7 16:29 3 -> /home/user/123.txt
Como puede ver, tenemos nuestros 3 descriptores de archivos estándar y uno más que abrimos. Comprobemos el tamaño del archivo:
[user@localhost ]$ ls -lah 123.txt
-rw-rw-r-- 1 user user 117M Oct 7 16:30 123.txt
Los datos se están escribiendo, intentamos cambiar los permisos en el archivo:
[user@localhost ]$ sudo chown root: 123.txt
[user@localhost ]$ ls -lah 123.txt
-rw-rw-r-- 1 root root 168M Oct 7 16:31 123.txt
[user@localhost ]$ ls -lah 123.txt
-rw-rw-r-- 1 root root 172M Oct 7 16:31 123.txt
Vemos que los datos aún se están escribiendo, aunque nuestro usuario no tiene permiso para escribir en el archivo. Intentemos eliminarlo:
[user@localhost ]$ sudo rm 123.txt
[user@localhost ]$ ls 123.txt
ls: cannot access 123.txt: No such file or directory
¿Dónde están escritos los datos? ¿Y están escritos? Verificamos:
[user@localhost ]$ ls -la /proc/3762/fd
total 0
dr-x------ 2 user user 0 Oct 7 16:29 .
dr-xr-xr-x 9 user user 0 Oct 7 16:29 ..
lrwx------ 1 user user 64 Oct 7 16:29 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 7 16:29 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 7 16:29 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct 7 16:29 3 -> /home/user/123.txt (deleted)
Sí, nuestro descriptor de archivo todavía existe y podemos tratar este descriptor de archivo como nuestro archivo anterior, podemos leerlo, borrarlo y copiarlo.
Veamos el tamaño del archivo:
[user@localhost ]$ lsof | grep 123.txt
python 31083 user 3w REG 8,5 19923457 2621522 /home/user/123.txt
El tamaño del archivo es 19923457. Intentemos borrar el archivo:
[user@localhost ]$ truncate -s 0 /proc/31083/fd/3
[user@localhost ]$ lsof | grep 123.txt
python 31083 user 3w REG 8,5 136318390 2621522 /home/user/123.txt
Como puede ver, el tamaño del archivo solo aumenta y nuestro baúl no funcionó. Veamos la documentación de llamadas al sistema.
with open("123.txt", "w") as f:
tenemos que poner
with open("123.txt", "a") as f:
Comprobando con la bandera "w"
[user@localhost ]$ strace -e trace=open python openforwrite.py 2>&1| grep 123.txt
open("123.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
y con la bandera "a"
[user@localhost ]$ strace -e trace=open python openforwrite.py 2>&1| grep 123.txt
open("123.txt", O_WRONLY|O_CREAT|O_APPEND, 0666) = 3
Programar un proceso que ya se está ejecutando
A menudo, los programadores, al crear y probar programas, utilizan depuradores (por ejemplo, GDB) o varios niveles de inicio de sesión en la aplicación. Linux ofrece la capacidad de escribir y cambiar un programa que ya se está ejecutando, por ejemplo, cambiar los valores de las variables, establecer un punto de interrupción, etc., etc.
Volviendo a la pregunta original sobre la falta de espacio en disco para escribir un archivo, intentemos simular el problema.
Creemos un archivo para nuestra partición, que montaremos como un disco separado:
[user@localhost ~]$ dd if=/dev/zero of=~/tempfile_for_article.dd bs=1M count=10
10+0 records in
10+0 records out
10485760 bytes (10 MB) copied, 0.00525929 s, 2.0 GB/s
[user@localhost ~]$
Creemos un sistema de archivos:
[user@localhost ~]$ mkfs.ext4 ~/tempfile_for_article.dd
mke2fs 1.42.9 (28-Dec-2013)
/home/user/tempfile_for_article.dd is not a block special device.
Proceed anyway? (y,n) y
...
Writing superblocks and filesystem accounting information: done
[user@localhost ~]$
Monte el sistema de archivos:
[user@localhost ~]$ sudo mount ~/tempfile_for_article.dd /mnt/
[sudo] password for user:
[user@localhost ~]$ df -h | grep mnt
/dev/loop0 8.7M 172K 7.9M 3% /mnt
Creamos un directorio con nuestro propietario:
[user@localhost ~]$ sudo mkdir /mnt/logs
[user@localhost ~]$ sudo chown user: /mnt/logs
Abramos el archivo para escribir solo en nuestro programa:
with open("/mnt/logs/123.txt", "w") as f:
Lanzamos
[user@localhost ]$ python openforwrite.py
Esperamos unos segundos.
[user@localhost ~]$ df -h | grep mnt
/dev/loop0 8.7M 8.0M 0 100% /mnt
Entonces, tenemos el problema descrito al principio de este artículo. Espacio libre 0, 100% ocupado.
Recordamos que según las condiciones de la tarea, intentamos registrar datos muy importantes que no se pueden perder. Y al mismo tiempo, necesitamos arreglar el servicio sin reiniciar el proceso.
Digamos que todavía tenemos espacio en disco, pero en una partición diferente, por ejemplo en /home.
Intentemos “reprogramar sobre la marcha” nuestro código.
Veamos el PID de nuestro proceso, que ha consumido todo el espacio en disco:
[user@localhost ~]$ ps axuf | grep [o]penfor
user 10078 27.2 0.0 128600 5744 pts/22 R+ 11:06 0:02 | _ python openforwrite.py
Conéctese al proceso a través de gdb
[user@localhost ~]$ gdb -p 10078
...
(gdb)
Veamos los descriptores de archivos abiertos:
(gdb) shell ls -lah /proc/10078/fd/
total 0
dr-x------ 2 user user 0 Oct 8 11:06 .
dr-xr-xr-x 9 user user 0 Oct 8 11:06 ..
lrwx------ 1 user user 64 Oct 8 11:09 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:09 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:06 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct 8 11:09 3 -> /mnt/logs/123.txt
Miramos la información sobre el descriptor de archivo número 3, que nos interesa.
(gdb) shell cat /proc/10078/fdinfo/3
pos: 8189952
flags: 0100001
mnt_id: 482
Teniendo en cuenta qué llamada al sistema hace Python (ver arriba donde ejecutamos strace y encontramos la llamada abierta), cuando procesamos nuestro código para abrir un archivo, hacemos lo mismo nosotros mismos en nombre de nuestro proceso, pero necesitamos O_WRONLY|O_CREAT| Los bits O_TRUNC se reemplazan con un valor numérico. Para hacer esto, abra las fuentes del kernel, por ejemplo
#definir O_WRONLY 00000001
#definir O_CREAT 00000100
#definir O_TRUNC 00001000
Combinamos todos los valores en uno, obtenemos 00001101
Ejecutamos nuestra llamada desde gdb
(gdb) call open("/home/user/123.txt", 00001101,0666)
$1 = 4
Entonces tenemos un nuevo descriptor de archivo con el número 4 y un nuevo archivo abierto en otra partición, verificamos:
(gdb) shell ls -lah /proc/10078/fd/
total 0
dr-x------ 2 user user 0 Oct 8 11:06 .
dr-xr-xr-x 9 user user 0 Oct 8 11:06 ..
lrwx------ 1 user user 64 Oct 8 11:09 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:09 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:06 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct 8 11:09 3 -> /mnt/logs/123.txt
l-wx------ 1 user user 64 Oct 8 11:15 4 -> /home/user/123.txt
Recordamos el ejemplo con pipe: cómo bash cambia los descriptores de archivos y ya hemos aprendido la llamada al sistema dup2.
Intentamos reemplazar un descriptor de archivo por otro.
(gdb) call dup2(4,3)
$2 = 3
Comprobamos:
(gdb) shell ls -lah /proc/10078/fd/
total 0
dr-x------ 2 user user 0 Oct 8 11:06 .
dr-xr-xr-x 9 user user 0 Oct 8 11:06 ..
lrwx------ 1 user user 64 Oct 8 11:09 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:09 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:06 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct 8 11:09 3 -> /home/user/123.txt
l-wx------ 1 user user 64 Oct 8 11:15 4 -> /home/user/123.txt
Cerramos el descriptor de archivo 4, ya que no lo necesitamos:
(gdb) call close (4)
$1 = 0
Y salir de gdb
(gdb) quit
A debugging session is active.
Inferior 1 [process 10078] will be detached.
Quit anyway? (y or n) y
Detaching from program: /usr/bin/python2.7, process 10078
Comprobando el nuevo archivo:
[user@localhost ~]$ ls -lah /home/user/123.txt
-rw-rw-r-- 1 user user 5.1M Oct 8 11:18 /home/user/123.txt
[user@localhost ~]$ ls -lah /home/user/123.txt
-rw-rw-r-- 1 user user 7.1M Oct 8 11:18 /home/user/123.txt
Como puede ver, los datos se escriben en un archivo nuevo, revisemos el anterior:
[user@localhost ~]$ ls -lah /mnt/logs/123.txt
-rw-rw-r-- 1 user user 7.9M Oct 8 11:08 /mnt/logs/123.txt
No se pierden datos, la aplicación funciona y los registros se escriben en una nueva ubicación.
Compliquemos un poco la tarea.
Imaginemos que los datos son importantes para nosotros, pero no tenemos espacio en ninguna de las particiones y no podemos conectar el disco.
Lo que podemos hacer es redirigir nuestros datos a algún lugar, por ejemplo a pipe, y a su vez redirigir datos de pipe a la red a través de algún programa, por ejemplo netcat.
Podemos crear una tubería con nombre con el comando mkfifo. Creará un pseudoarchivo en el sistema de archivos incluso si no hay espacio libre en él.
Reinicie la aplicación y verifique:
[user@localhost ]$ python openforwrite.py
[user@localhost ~]$ ps axuf | grep [o]pen
user 5946 72.9 0.0 128600 5744 pts/22 R+ 11:27 0:20 | _ python openforwrite.py
[user@localhost ~]$ ls -lah /proc/5946/fd
total 0
dr-x------ 2 user user 0 Oct 8 11:27 .
dr-xr-xr-x 9 user user 0 Oct 8 11:27 ..
lrwx------ 1 user user 64 Oct 8 11:28 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:28 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:27 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct 8 11:28 3 -> /mnt/logs/123.txt
[user@localhost ~]$ df -h | grep mnt
/dev/loop0 8.7M 8.0M 0 100% /mnt
No hay espacio en disco, pero creamos con éxito una canalización con nombre allí:
[user@localhost ~]$ mkfifo /mnt/logs/megapipe
[user@localhost ~]$ ls -lah /mnt/logs/megapipe
prw-rw-r-- 1 user user 0 Oct 8 11:28 /mnt/logs/megapipe
Ahora necesitamos de alguna manera empaquetar todos los datos que entran en esta tubería a otro servidor a través de la red; el mismo netcat es adecuado para esto.
En el servidor servidor-remoto.ejemplo.com ejecutamos
[user@localhost ~]$ nc -l 7777 > 123.txt
En nuestro servidor problemático lo iniciamos en una terminal separada.
[user@localhost ~]$ nc remote-server.example.com 7777 < /mnt/logs/megapipe
Ahora todos los datos que terminen en la tubería irán automáticamente a la entrada estándar de netcat, que los enviará a la red en el puerto 7777.
Todo lo que tenemos que hacer es comenzar a escribir nuestros datos en esta canalización con nombre.
Ya tenemos la aplicación ejecutándose:
[user@localhost ~]$ ps axuf | grep [o]pen
user 5946 99.8 0.0 128600 5744 pts/22 R+ 11:27 169:27 | _ python openforwrite.py
[user@localhost ~]$ ls -lah /proc/5946/fd
total 0
dr-x------ 2 user user 0 Oct 8 11:27 .
dr-xr-xr-x 9 user user 0 Oct 8 11:27 ..
lrwx------ 1 user user 64 Oct 8 11:28 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:28 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:27 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct 8 11:28 3 -> /mnt/logs/123.txt
De todas las banderas, solo necesitamos O_WRONLY ya que el archivo ya existe y no necesitamos borrarlo.
[user@localhost ~]$ gdb -p 5946
...
(gdb) call open("/mnt/logs/megapipe", 00000001,0666)
$1 = 4
(gdb) shell ls -lah /proc/5946/fd
total 0
dr-x------ 2 user user 0 Oct 8 11:27 .
dr-xr-xr-x 9 user user 0 Oct 8 11:27 ..
lrwx------ 1 user user 64 Oct 8 11:28 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:28 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:27 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct 8 11:28 3 -> /mnt/logs/123.txt
l-wx------ 1 user user 64 Oct 8 14:20 4 -> /mnt/logs/megapipe
(gdb) call dup2(4,3)
$2 = 3
(gdb) shell ls -lah /proc/5946/fd
total 0
dr-x------ 2 user user 0 Oct 8 11:27 .
dr-xr-xr-x 9 user user 0 Oct 8 11:27 ..
lrwx------ 1 user user 64 Oct 8 11:28 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:28 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:27 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct 8 11:28 3 -> /mnt/logs/megapipe
l-wx------ 1 user user 64 Oct 8 14:20 4 -> /mnt/logs/megapipe
(gdb) call close(4)
$3 = 0
(gdb) shell ls -lah /proc/5946/fd
total 0
dr-x------ 2 user user 0 Oct 8 11:27 .
dr-xr-xr-x 9 user user 0 Oct 8 11:27 ..
lrwx------ 1 user user 64 Oct 8 11:28 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:28 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:27 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct 8 11:28 3 -> /mnt/logs/megapipe
(gdb) quit
A debugging session is active.
Inferior 1 [process 5946] will be detached.
Quit anyway? (y or n) y
Detaching from program: /usr/bin/python2.7, process 5946
Comprobando el servidor remoto servidor-remoto.ejemplo.com
[user@localhost ~]$ ls -lah 123.txt
-rw-rw-r-- 1 user user 38M Oct 8 14:21 123.txt
Los datos están llegando, comprobamos el servidor problemático.
[user@localhost ~]$ ls -lah /mnt/logs/
total 7.9M
drwxr-xr-x 2 user user 1.0K Oct 8 11:28 .
drwxr-xr-x 4 root root 1.0K Oct 8 10:55 ..
-rw-rw-r-- 1 user user 7.9M Oct 8 14:17 123.txt
prw-rw-r-- 1 user user 0 Oct 8 14:22 megapipe
Los datos se guardan, el problema está resuelto.
Aprovecho para saludar a mis compañeros de Degiro.
Escuche los podcasts de Radio T.
Todo bien
Como tarea, te sugiero que pienses en lo que habrá en los descriptores del archivo de proceso cat y sleep si ejecutas el siguiente comando:
[user@localhost ~]$ cat /dev/zero 2>/dev/null| sleep 10000
Fuente: habr.com