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