Descriptor de archivo en Linux con ejemplos

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 habiertos en cualquier lenguaje de programación recibo una identificación única que me apunta a un archivo, esta identificación es el descriptor del archivo. Y no importa en absoluto qué y quién haga con este archivo a continuación, se puede eliminar, se le puede cambiar el nombre, se puede cambiar el propietario o se le pueden quitar los derechos de lectura y escritura, todavía tendré acceso. a ello, porque al momento de abrir el archivo, tenía derechos de lectura y/o escritura y logré empezar a trabajar con él, por lo que debo continuar haciéndolo.

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. hombre stdio и hombre salido del estándar

  • 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 / dev / pts, pero aún podemos manipularlos, por ejemplo, ejecutarlos en una segunda consola

[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 tubo y recibe nuevos descriptores de archivos en el búfer de canalización temporal, pero este búfer aún no conecta nuestros dos 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 clonar 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 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 dup2, cambia nuestro descriptor de archivo STDOUT número 1 a un descriptor de archivo que apunta a la tubería, en nuestro caso es el número 3. Por lo tanto, todo lo que el primer proceso hijo con PID 9004 escriba en STDOUT terminará automáticamente en el búfer de tubería.

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. ejecutivo el archivo ejecutable que especificamos en la línea de comando, en nuestro caso es /usr/bin/cat.

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. habiertos. Si usamos el indicador O_APPEND al abrir un archivo, con cada escritura, el sistema operativo verifica el tamaño del archivo y escribe datos hasta el final del archivo, y lo hace de forma atómica. Esto permite que varios subprocesos 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 del tronco solo si abrimos el archivo para escritura adicional, lo que significa que 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 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 aquí y mira qué banderas son responsables de qué

#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

Añadir un comentario