Una vez, en una entrevista, me preguntaron qué harías si encuentras un servicio que no funciona porque el disco se ha quedado sin espacio.
Por supuesto, respondí que vería qué ocupaba este lugar y, si era posible, limpiaría el lugar.
Luego, el entrevistador preguntó: ¿qué pasa si no hay espacio libre en la sección, pero tampoco ve los archivos que ocuparían todo el lugar?
A esto, le dije que siempre puede mirar los descriptores de archivos abiertos, por ejemplo con el comando lsof y comprender qué aplicación ocupaba todo el espacio disponible, y luego puede proceder de acuerdo con las circunstancias, dependiendo de si se necesitan los datos.
El entrevistador me interrumpió en la última palabra y agregó mi pregunta: "Supongamos que no necesitamos los datos, es solo un registro de depuración, pero la aplicación no funciona porque no puede grabar la 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, los datos importantes aún se almacenan en nuestra memoria y los clientes importantes están conectados al servicio en sí, que no podemos obligar a volver a conectar".
"Bueno", le 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 archivos, 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 mi conocimiento no profundiza? Pero, ¿y si los datos siguen siendo importantes? ¿Qué sucede si no podemos reiniciar el proceso y, al mismo tiempo, este proceso escribe en el sistema de archivos en una sección en la que no hay espacio libre? ¿Qué sucede si no podemos perder no solo los datos ya grabados, sino también los datos que este proceso escribe o intenta grabar?
Tuzik
Al comienzo de mi carrera intenté crear una pequeña aplicación en la que era necesario almacenar información sobre los usuarios. Y luego pensé, ¿cómo puedo asignar el usuario a sus datos? Por ejemplo, tengo a Ivan Ivanov Ivanich, y él tiene algunos datos, pero ¿cómo hacer amistad con ellos? Puedo indicar directamente que el perro llamado "Tuzik" pertenece a este mismo Ivan. Pero, ¿qué pasa si cambia su nombre y en lugar de Ivan se convierte, por ejemplo, en Olya? Entonces resulta que nuestra Olya Ivanovna Ivanova ya no tendrá un perro, y nuestro Tuzik seguirá perteneciendo al Iván inexistente. Esta base de datos fue ayudada por una base de datos que le dio a cada usuario un identificador único (ID), y mi Tuzik se adjuntó a esta ID, que, de hecho, era solo un número ordinal. Por lo tanto, el propietario del tuzik tenía el número de identificación 2 y, en algún momento, Ivan tenía esta identificación, y luego Olya se convirtió en la misma identificación. El problema de la humanidad y la cría de animales ha sido prácticamente resuelto.
Descriptor de archivo
El problema del archivo y el programa que trabaja con este archivo es casi el mismo que el de nuestro perro y nuestra persona. Supongamos que abrí un archivo con el nombre ivan.txt y comencé a escribir la palabra tuzik en él, pero logré escribir solo la primera letra "t" en el archivo, y alguien cambió el nombre de este archivo, por ejemplo, olya.txt. Pero el archivo seguía siendo el mismo, y todavía quiero escribir mi as en él. Cada vez que abro un archivo con la llamada al sistema abierto en cualquier lenguaje de programación, obtengo una ID única que me señala un archivo, esta ID es un descriptor de archivo. Y no importa lo que alguien haga a continuación con este archivo, pueden eliminarlo, cambiarle el nombre, cambiar el propietario o quitar los permisos de lectura y escritura, todavía tendré acceso a él, porque en el momento en que se abra el archivo, Tenía derecho a leerlo y / o escribirlo, y logré comenzar a trabajar con él, lo que significa que debo seguir haciéndolo.
En Linux, la biblioteca libc se abre para cada archivo descriptor de la aplicación (proceso) 3 en ejecución, con números 0,1,2. Puede encontrar más información en los enlaces
man stdio y
man stdout- El descriptor de archivo 0 se llama STDIN y está asociado con la entrada de datos desde la aplicación
- El descriptor de archivo 1 se llama STDOUT y las aplicaciones lo utilizan para generar datos, por ejemplo, comandos de impresión
- El descriptor de archivo 2 se llama STDERR y las aplicaciones lo utilizan para generar datos de informes de errores.
Si en su programa abre un archivo para leer o escribir, lo más probable es que obtenga la primera identificación gratuita y esta será la número 3.
Se puede ver una lista de archivos descriptores desde cualquier proceso si conoce su PID.
Por ejemplo, abra una consola con bash y vea el PID de nuestro proceso
[user@localhost ]$ echo $$ 15771
En la segunda consola, ejecute
[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 de manera segura el número de descriptor de archivo 255 en el marco de este artículo; bash lo abrió para sus necesidades y no una biblioteca vinculada.
Ahora los 3 archivos descriptores están asociados con el dispositivo pseudo-terminal
/ dev / pts , pero aún podemos manipularlos, por ejemplo, ejecutarlos en la segunda consola
[user@localhost ]$ echo "hello world" > /proc/15771/fd/0
Y en la primera consola veremos
[user@localhost ]$ hello world
Redirigir y canalizar
Puede anular fácilmente estos 3 archivos descriptores en cualquier proceso, incluso en 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
strace -f y ver qué sucede dentro, pero le contaré brevemente.
Nuestro proceso principal bash con PID 15771 analiza nuestro comando y comprende exactamente cuántos comandos queremos ejecutar, en nuestro caso hay dos de ellos: cat y sleep. Bash sabe que necesita crear dos procesos secundarios y combinarlos con una tubería. Total bash necesitará 2 procesos secundarios y una tubería.
Antes de crear procesos secundarios, bash inicia la llamada al sistema de
tuberías y recibe nuevos descriptores de archivo para el búfer de tubería temporal, pero este búfer aún no une nuestros dos procesos secundarios.
Para el proceso padre, parece que la tubería ya existe, y todavía no hay procesos hijos:
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 una llamada al sistema,
clone bash crea dos procesos secundarios, y nuestros tres procesos se verán así:
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 olvide que clonar clona el proceso junto con todos los descriptores de archivo, por lo que serán los mismos en el proceso primario y en los secundarios. La tarea del proceso principal con PID 15771 es monitorear los procesos secundarios, por lo que solo espera una respuesta de los procesos secundarios.
Por lo tanto, no necesita tubería, y cierra los descriptores de archivo con los números 3 y 4.
En el primer proceso secundario de bash con PID 9004, la
llamada del sistema
dup2 cambia nuestro descriptor de archivo STDOUT con el número 1 a un descriptor de archivo que apunta a pipe, en nuestro caso es el número 3. Por lo tanto, todo lo que el primer proceso secundario con PID 9004 escribirá en STDOUT caerá automáticamente en el buffer de la tubería.
En el segundo proceso secundario con PID 9005, bash cambia el descriptor de archivo STDIN con el número 0 usando dup2. Ahora todo lo que leerá nuestro segundo bash con PID 9005 se leerá desde la tubería.
Después de eso, los descriptores con los números 3 y 4 también se cierran en los procesos secundarios, ya que ya no se usan.
Ignoro deliberadamente el descriptor de archivo 255, usa bash para necesidades internas y también se cerrará en procesos secundarios.
Luego, en el primer proceso secundario con PID 9004, bash inicia el archivo ejecutable que especificamos en la línea de comando con el
exec de la llamada al sistema, en nuestro caso es / usr / bin / cat.
En el segundo proceso secundario con PID 9005, bash inicia el segundo archivo ejecutable que especificamos, en nuestro caso es / usr / bin / sleep.
La llamada al sistema ejecutivo no cierra los descriptores de archivo si no se abrieron con el indicador O_CLOEXEC durante la llamada abierta. En nuestro caso, después de ejecutar los archivos ejecutables, se guardarán todos los descriptores de archivos actuales.
Compruebe 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 puede ver, el número único de nuestra tubería es el mismo en ambos procesos. Entonces tenemos una conexión entre dos procesos diferentes con un 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 dentro, por ejemplo, así:
strace -s 1024 -f bash -c "ls | grep hello"
Volvamos a nuestro problema de quedarse sin espacio en disco e intentar guardar datos sin reiniciar el proceso. Escribamos un pequeño programa que escribirá en el disco aproximadamente 1 megabyte por segundo. Además, si por alguna razón no pudiéramos escribir datos en el disco, simplemente lo ignoraremos e intentaremos escribir datos nuevamente después de un segundo. En el ejemplo que uso 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
Ejecute el programa y mire los descriptores de archivo
[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 archivo estándar y otro que abrimos. Verifique el tamaño del archivo:
[user@localhost ]$ ls -lah 123.txt -rw-rw-r-- 1 user user 117M Oct 7 16:30 123.txt
se están escribiendo datos, intente cambiar los permisos del 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 derecho a 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 se escriben los datos? ¿Y están escritos en absoluto? Comprobamos:
[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 archivo descriptor todavía existe, y podemos trabajar con este archivo descriptor como con nuestro archivo anterior, podemos leerlo, limpiarlo y copiarlo.
Nos fijamos en el tamaño del archivo:
[user@localhost ]$ lsof | grep 123.txt python 31083 user 3w REG 8,5 19923457 2621522 /home/user/123.txt
Tamaño de archivo 19923457. Intentando 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 está aumentando y nuestro trankate no funcionó. Consulte la documentación de la llamada al sistema
abierto . Si usamos el indicador O_APPEND al abrir un archivo, cada vez que el sistema operativo verifica el tamaño del archivo y escribe datos hasta el final del archivo, lo hace atómicamente. Esto permite que múltiples hilos o procesos escriban en el mismo archivo. Pero en nuestro código no usamos esta bandera. Podemos ver un tamaño de archivo diferente en lsof después de la conversión solo si abrimos el archivo para una grabación adicional, lo que significa que en lugar de en nuestro código
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 ya en ejecución
A menudo, los programadores usan depuradores (por ejemplo, GDB) o varios niveles de inicio de sesión en la aplicación al crear y probar programas. Linux proporciona la capacidad de escribir y cambiar un programa que ya se está ejecutando, por ejemplo, cambiar los valores de las variables, establecer puntos de interrupción, etc., etc.
Volviendo a la pregunta original sin suficiente espacio en disco para escribir el archivo, intentaremos emular el problema.
Cree 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 ~]$
Crea 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 ~]$
Montar 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
Crea un directorio con nuestro propietario:
[user@localhost ~]$ sudo mkdir /mnt/logs [user@localhost ~]$ sudo chown user: /mnt/logs
Abrimos el archivo para escribir solo en nuestro programa:
with open("/mnt/logs/123.txt", "w") as f:
Lanzamos
[user@localhost ]$ python openforwrite.py
Esperando unos segundos
[user@localhost ~]$ df -h | grep mnt /dev/loop0 8.7M 8.0M 0 100% /mnt
Entonces, tenemos el problema descrito al comienzo de este artículo. Espacio libre 0, ocupado 100%.
Recordamos que de acuerdo con las condiciones de la tarea, estamos tratando de registrar datos muy importantes que no se pueden perder. Y al mismo tiempo, necesitamos reparar el servicio sin reiniciar el proceso.
Supongamos 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.
Observamos el PID de nuestro proceso, que se comió 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
Nos conectamos al proceso a través de gdb
[user@localhost ~]$ gdb -p 10078 ... (gdb)
Nos fijamos en 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
Observamos la información sobre el descriptor de archivo con el número 3, que nos interesa
(gdb) shell cat /proc/10078/fdinfo/3 pos: 8189952 flags: 0100001 mnt_id: 482
Recordando qué tipo de llamada al sistema hace Python (ver arriba, dónde ejecutamos strace y encontramos la llamada abierta), procesando nuestro código para abrir el archivo, hacemos lo mismo en nuestro propio nombre, pero necesitamos los bits O_WRONLY | O_CREAT | O_TRUNC reemplazar con un valor numérico. Para hacer esto, abra las fuentes del núcleo, por ejemplo,
aquí y vea qué indicadores son responsables de qué
#define O_WRONLY 00000001
#define O_CREAT 00000100
#define O_TRUNC 00001000
Combinamos todos los valores en uno, obtenemos 00001101
Ejecute nuestra llamada desde gdb
(gdb) call open("/home/user/123.txt", 00001101,0666) $1 = 4
Entonces obtuvimos un nuevo archivo descriptor con el número 4 y un nuevo archivo abierto en otra sección, verifique:
(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 de la tubería: cómo bash cambia los descriptores de archivo 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
Verifique 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 nuevo archivo, verificamos 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
Los datos no se pierden, la aplicación funciona, los registros se escriben en un nuevo lugar.
Vamos a complicar un poco la tarea
Imagine que los datos son importantes para nosotros, pero no tenemos espacio en disco en ninguna de las secciones y no podemos conectar el disco.
Lo que podemos hacer es redirigir nuestros datos en algún lugar, por ejemplo, a la tubería, y los datos de la tubería, a su vez, redirigen a la red a través de algún programa, como netcat.
Podemos crear una tubería con nombre con el comando mkfifo. Creará un pseudo archivo en el sistema de archivos, incluso si no hay espacio libre en él.
Reiniciamos la aplicación y verificamos:
[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 tubería 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 envolver de alguna manera todos los datos que ingresan en esta tubería a otro servidor a través de la red, para esto hará lo mismo netcat.
En el servidor remote-server.example.com, ejecute
[user@localhost ~]$ nc -l 7777 > 123.txt
En nuestro servidor problemático, ejecute en una terminal separada
[user@localhost ~]$ nc remote-server.example.com 7777 < /mnt/logs/megapipe
Ahora todos los datos que ingresan a la tubería irán automáticamente a stdin en 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 tubería con nombre.
Ya tenemos una aplicación en ejecución:
[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
Comprobación del servidor remoto remote-server.example.com
[user@localhost ~]$ ls -lah 123.txt -rw-rw-r-- 1 user user 38M Oct 8 14:21 123.txt
Los datos van, verificamos un 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
Datos guardados, problema resuelto.
Aprovecho esta oportunidad para transmitir mis saludos a mis colegas de Degiro.
Escuche los podcasts de Radio T.
Bueno para todos
Como tarea, me propongo pensar en lo que habrá en los descriptores de archivo del proceso gato y sueño si ejecuta este comando:
[user@localhost ~]$ cat /dev/zero 2>/dev/null| sleep 10000