Descriptor de ficheiros en Linux con exemplos

Unha vez, durante unha entrevista, preguntáronme, que fará se atopa un servizo que non funciona debido ao feito de que o disco quedou sen espazo?

Por suposto, contesteille que vería o que ocupaba este lugar e, se é posible, limparía o local.
Entón o entrevistador preguntou: e se non hai espazo libre na partición, pero tampouco ves ningún ficheiro que ocupe todo o espazo?

A isto dixen que sempre podes mirar os descritores de ficheiros abertos, por exemplo co comando lsof, e entender que aplicación ocupou todo o espazo dispoñible, e despois podes actuar segundo as circunstancias, dependendo de se os datos son necesarios. .

O entrevistador interrompeume na última palabra, engadindo á súa pregunta: "Supoñamos que non necesitamos os datos, é só un rexistro de depuración, pero a aplicación non funciona porque non pode escribir unha depuración"?

"Está ben", respondín, "podemos desactivar a depuración na configuración da aplicación e reiniciala".
O entrevistador objetou: "Non, non podemos reiniciar a aplicación, aínda temos datos importantes almacenados na memoria e os clientes importantes están conectados ao propio servizo, que non podemos obrigar a conectar de novo".

"Está ben", dixen, "se non podemos reiniciar a aplicación e os datos non son importantes para nós, poderemos simplemente borrar este ficheiro aberto a través do descritor de ficheiros, aínda que non o vexamos no comando ls. no sistema de ficheiros".

O entrevistador estaba satisfeito, pero eu non.

Entón pensei, por que a persoa que proba os meus coñecementos non afonda máis? Pero e se os datos son importantes despois de todo? E se non podemos reiniciar un proceso e o proceso escribe no sistema de ficheiros nunha partición que non ten espazo libre? E se non podemos perder non só os datos que xa foron escritos, senón tamén os datos que este proceso escribe ou tenta escribir?

Tuzik

A principios da miña carreira, tentei crear unha pequena aplicación que precisase almacenar información do usuario. E entón pensei, como podo relacionar o usuario cos seus datos. Por exemplo, teño a Ivanov Ivan Ivanovich, e ten información, pero como podo facer amigos con eles? Podo sinalar directamente que o can chamado "Tuzik" pertence a este mesmo Iván. Pero que pasa se cambia de nome e no canto de Iván convértese, por exemplo, en Olya? Entón, resultará que a nosa Olya Ivanovna Ivanova xa non terá un can e o noso Tuzik seguirá pertencendo ao inexistente Ivan. Unha base de datos que proporcionaba a cada usuario un identificador único (ID) axudou a resolver este problema, e o meu Tuzik estaba ligado a este ID, que, de feito, era só un número de serie. Así, o propietario do as tiña o número de identificación 2 e, nalgún momento, Ivan estaba baixo esta identificación, e entón Olya pasou a ter o mesmo ID. O problema da humanidade e da gandería estaba practicamente resolto.

Descriptor de ficheiro

O problema do ficheiro e do programa que traballa con este ficheiro é aproximadamente o mesmo que o do noso can e home. Supoña que abrín un ficheiro chamado ivan.txt e comecei a escribir a palabra tuzik nel, pero só conseguín escribir a primeira letra "t" no ficheiro e alguén renomeou este ficheiro, por exemplo, a olya.txt. Pero o ficheiro segue sendo o mesmo, e aínda quero gravar nel ao meu fillo. Cada vez que se abre un ficheiro mediante unha chamada do sistema abrir en calquera linguaxe de programación recibo un ID único que me apunta a un ficheiro, este ID é o descritor do ficheiro. E non importa o que e quen faga a continuación con este ficheiro, pódese borrar, renomear, cambiar o propietario ou quitarlle os dereitos de lectura e escritura, aínda terei acceso a el, porque no momento de abrir o ficheiro tiña dereitos para lelo e/ou escribilo e conseguín comezar a traballar con el, o que significa que teño que seguir facéndoo.

En Linux, a biblioteca libc abre 3 ficheiros descritores para cada aplicación (proceso) en execución, numerados 0,1,2. Pódese atopar máis información nas ligazóns home stdio и home stdout

  • O descritor de ficheiro 0 chámase STDIN e está asociado coa entrada da aplicación
  • O descritor de ficheiros 1 chámase STDOUT e é usado polas aplicacións para producir datos, como comandos de impresión
  • O descritor de ficheiros 2 chámase STDERR e é usado polas aplicacións para emitir mensaxes de erro.

Se no teu programa abres algún ficheiro para ler ou escribir, o máis probable é que obteñas o primeiro ID gratuíto e será o número 3.

A lista de descritores de ficheiros pódese ver para calquera proceso se coñece o seu PID.

Por exemplo, abramos a consola bash e vexamos o PID do noso proceso

[user@localhost ]$ echo $$
15771

Na segunda consola imos correr

[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

Pode ignorar con seguridade o descritor de ficheiro número 255 para os efectos deste artigo; foi aberto para as súas necesidades polo propio bash e non pola biblioteca ligada.

Agora os 3 ficheiros descritores están asociados co dispositivo pseudoterminal /dev/pts, pero aínda podemos manipulalos, por exemplo, executalos nunha segunda consola

[user@localhost ]$ echo "hello world" > /proc/15771/fd/0

E na primeira consola xa veremos

[user@localhost ]$ hello world

Redirección e canalización

Podes anular facilmente estes 3 ficheiros descritores en calquera proceso, incluso en bash, por exemplo a través dunha canalización que conecta dous procesos, consulte

[user@localhost ]$ cat /dev/zero | sleep 10000

Podes executar este comando ti mesmo strace -f e a ver que pasa por dentro, pero vouche contar brevemente.

O noso proceso bash pai con PID 15771 analiza o noso comando e comprende exactamente cantos comandos queremos executar, no noso caso hai dous deles: cat e sleep. Bash sabe que ten que crear dous procesos fillos e combinalos nun tubo. En total, bash necesitará 2 procesos fillos e un tubo.

Bash executa unha chamada ao sistema antes de crear procesos fillos tubo e recibe novos descritores de ficheiros no búfer temporal, pero este búfer aínda non conecta os nosos dous procesos fillos.

Para o proceso principal, parece que xa hai unha canalización, pero aínda non hai procesos fillos:

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

A continuación, usando a chamada do sistema clonar bash crea dous procesos fillos, e os nosos tres procesos terán este aspecto:

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

Non esquezas que o clon clona o proceso xunto con todos os descritores de ficheiros, polo que serán iguais no proceso pai e nos fillos. O traballo do proceso principal co PID 15771 é supervisar os procesos fillos, polo que simplemente espera unha resposta dos fillos.

Polo tanto, non precisa pipe e pecha os descritores de ficheiros numerados 3 e 4.

No primeiro proceso de bash fillo con PID 9004, a chamada do sistema dup2, cambia o noso descritor de ficheiro STDOUT número 1 por un descritor de ficheiro que apunta a canalización, no noso caso é o número 3. Así, todo o que o primeiro proceso fillo con PID 9004 escribe en STDOUT acabará automaticamente no búfer da canalización.

No segundo proceso fillo con PID 9005, bash usa dup2 para cambiar o descritor do ficheiro STDIN número 0. Agora todo o que lerá o noso segundo bash con PID 9005 lerase dende a canalización.

Despois disto, os descritores de ficheiros numerados 3 e 4 tamén se pechan nos procesos fillos, xa que xa non se utilizan.

Ignoro deliberadamente o descritor de ficheiro 255; úsase para fins internos polo propio bash e tamén se pechará nos procesos fillos.

A continuación, no primeiro proceso fillo con PID 9004, bash comeza a usar unha chamada ao sistema exec o ficheiro executable que especificamos na liña de comandos, no noso caso é /usr/bin/cat.

No segundo proceso fillo con PID 9005, bash executa o segundo executable que especificamos, no noso caso /usr/bin/sleep.

A chamada ao sistema exec non pecha os identificadores de ficheiros a non ser que se abrisen coa marca O_CLOEXEC no momento en que se fixo a chamada aberta. No noso caso, despois de lanzar os ficheiros executables, gardaranse todos os descritores de ficheiros actuais.

Consulta na 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 podes ver, o número único do noso tubo é o mesmo en ambos os procesos. Así, temos unha conexión entre dous procesos diferentes co mesmo pai.

Para aqueles que non estean familiarizados coas chamadas de sistema que usa bash, recoméndolles executar os comandos a través de strace e ver o que está a suceder internamente, por exemplo, así:

strace -s 1024 -f bash -c "ls | grep hello"

Volvamos ao noso problema con pouco espazo no disco e tentando gardar datos sen reiniciar o proceso. Escribamos un pequeno programa que escribirá aproximadamente 1 megabyte por segundo no disco. Ademais, se por algún motivo non puidemos escribir datos no disco, simplemente ignoraremos isto e tentaremos escribir os datos de novo nun segundo. No exemplo que estou usando Python, pode usar calquera outra linguaxe 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

Imos executar o programa e ver os descritores dos ficheiros

[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 podes ver, temos os nosos 3 descritores de ficheiros estándar e un máis que abrimos. Comprobamos o tamaño do ficheiro:

[user@localhost ]$ ls -lah 123.txt 
-rw-rw-r-- 1 user user 117M Oct  7 16:30 123.txt

Os datos están a escribirse, intentamos cambiar os permisos do ficheiro:

[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 os datos aínda se están a escribir, aínda que o noso usuario non ten permiso para escribir no ficheiro. Imos tentar eliminalo:

[user@localhost ]$ sudo rm 123.txt 
[user@localhost ]$ ls 123.txt
ls: cannot access 123.txt: No such file or directory

Onde están escritos os datos? E 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)

Si, o noso descritor de ficheiros aínda existe e podemos tratar este descritor de ficheiros como o noso ficheiro antigo, podemos lelo, borralo e copialo.

Vexamos o tamaño do ficheiro:

[user@localhost ]$ lsof | grep 123.txt
python    31083             user    3w      REG                8,5   19923457   2621522 /home/user/123.txt

O tamaño do ficheiro é 19923457. Tentemos borrar o ficheiro:

[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 podes ver, o tamaño do ficheiro só está aumentando e o noso tronco non funcionaba. Vexamos a documentación da chamada ao sistema abrir. Se usamos a marca O_APPEND ao abrir un ficheiro, entón con cada escritura, o sistema operativo comproba o tamaño do ficheiro e escribe os datos ata o final do ficheiro, e faino atomicamente. Isto permite que varios fíos ou procesos escriban no mesmo ficheiro. Pero no noso código non usamos esta bandeira. Podemos ver un tamaño de ficheiro diferente en lsof despois do tronco só se abrimos o ficheiro para a escritura adicional, o que significa que no noso código no seu lugar

with open("123.txt", "w") as f:

temos que poñer

with open("123.txt", "a") as f:

Comprobando coa bandeira "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

e coa bandeira "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

Programando un proceso xa en execución

Moitas veces os programadores, ao crear e probar programas, usan depuradores (por exemplo GDB) ou varios niveis de rexistro na aplicación. Linux ofrece a posibilidade de escribir e cambiar un programa que xa está en execución, por exemplo, cambiar os valores de variables, establecer un punto de interrupción, etc., etc.

Volvendo á pregunta orixinal sobre a falta de espazo no disco para escribir un ficheiro, imos tentar simular o problema.

Imos crear un ficheiro para a nosa 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 ~]$

Imos crear un sistema de ficheiros:

[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 o sistema de ficheiros:

[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 co noso propietario:

[user@localhost ~]$ sudo mkdir /mnt/logs
[user@localhost ~]$ sudo chown user: /mnt/logs

Abramos o ficheiro para escribir só no noso programa:

with open("/mnt/logs/123.txt", "w") as f:

Lanzamento

[user@localhost ]$ python openforwrite.py 

Agardamos uns segundos

[user@localhost ~]$ df -h | grep mnt
/dev/loop0      8.7M  8.0M     0 100% /mnt

Entón, temos o problema descrito ao comezo deste artigo. Espazo libre 0, 100% ocupado.

Lembramos que segundo as condicións da tarefa, estamos tentando rexistrar datos moi importantes que non se poden perder. E ao mesmo tempo, necesitamos arranxar o servizo sen reiniciar o proceso.

Digamos que aínda temos espazo no disco, pero nunha partición diferente, por exemplo en /home.

Tentemos "reprogramar sobre a marcha" o noso código.

Vexamos o PID do noso proceso, que ocupou todo o espazo do 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 ao proceso mediante gdb

[user@localhost ~]$ gdb -p 10078
...
(gdb) 

Vexamos os descritores de ficheiros abertos:

(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 a información sobre o descritor do ficheiro número 3, que nos interesa

(gdb) shell cat /proc/10078/fdinfo/3
pos:    8189952
flags:  0100001
mnt_id: 482

Tendo en conta a chamada ao sistema que fai Python (consulta arriba onde executamos strace e atopamos a chamada aberta), ao procesar o noso código para abrir un ficheiro, facemos o mesmo nós mesmos en nome do noso proceso, pero necesitamos o O_WRONLY|O_CREAT| Os bits O_TRUNC substitúense por un valor numérico. Para iso, abra as fontes do núcleo, por exemplo aquí e mira que bandeiras son responsables de que

#define O_WRONLY 00000001
#define O_CREAT 00000100
#definir O_TRUNC 00001000

Combinamos todos os valores nun só, obtemos 00001101

Facemos a nosa chamada desde gdb

(gdb) call open("/home/user/123.txt", 00001101,0666)
$1 = 4

Entón, temos un novo descritor de ficheiro co número 4 e un novo ficheiro aberto noutra partición, 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 -> /mnt/logs/123.txt
l-wx------ 1 user user 64 Oct  8 11:15 4 -> /home/user/123.txt

Lembramos o exemplo con pipe: como bash cambia os descritores dos ficheiros, e xa aprendemos a chamada ao sistema dup2.

Tentamos substituír un descritor de ficheiros por outro

(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

Pechamos o descritor de ficheiro 4, xa que non o necesitamos:

(gdb) call close (4)
$1 = 0

E sae 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 o novo ficheiro:

[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 podes ver, os datos escriben nun ficheiro novo, comprobemos o antigo:

[user@localhost ~]$ ls -lah /mnt/logs/123.txt 
-rw-rw-r-- 1 user user 7.9M Oct  8 11:08 /mnt/logs/123.txt

Non se perden datos, a aplicación funciona, os rexistros escríbense nunha nova localización.

Imos complicar un pouco a tarefa

Imaxinemos que os datos son importantes para nós, pero non temos espazo en disco en ningunha das particións e non podemos conectar o disco.

O que podemos facer é redirixir os nosos datos nalgún lugar, por exemplo a tubería, e á súa vez redirixir os datos dende a canalización á rede a través dalgún programa, por exemplo netcat.
Podemos crear unha canalización con nome co comando mkfifo. Creará un pseudo ficheiro no sistema de ficheiros aínda que non haxa espazo libre nel.

Reinicie a aplicación e comprobe:

[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

Non hai espazo no disco, pero creamos con éxito unha canalización con nome alí:

[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

Agora necesitamos envolver dalgún xeito todos os datos que entran nesta canalización a outro servidor a través da rede; o mesmo netcat é adecuado para iso.

No servidor remote-server.example.com executamos

[user@localhost ~]$ nc -l 7777 > 123.txt 

No noso servidor problemático lanzámonos nun terminal separado

[user@localhost ~]$ nc remote-server.example.com 7777 < /mnt/logs/megapipe 

Agora todos os datos que acaban na canalización irán automaticamente a stdin en netcat, que os enviará á rede no porto 7777.

Todo o que temos que facer é comezar a escribir os nosos datos nesta canalización denominada.

Xa temos a aplicación en execució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 as bandeiras, só necesitamos O_WRONLY xa que o ficheiro xa existe e non necesitamos borralo

[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 o 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

Os datos están chegando, comprobamos o servidor do problema

[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

Os datos gárdanse, o problema está resolto.

Aproveito para saudar aos meus compañeiros de Degiro.
Escoita os podcasts de Radio-T.

Todo o mellor.

Como deberes, suxiro que penses no que haberá nos descritores do ficheiro de proceso cat e sleep se executas o seguinte comando:

[user@localhost ~]$ cat /dev/zero 2>/dev/null| sleep 10000

Fonte: www.habr.com

Engadir un comentario