Descriptor de fitxer a Linux amb exemples

Una vegada, en una entrevista, em van preguntar què faries si trobes un servei trencat a causa del fet que el disc s'ha quedat sense espai?

Això sí, vaig respondre que miraria què feia aquest lloc i, si era possible, netejaria el lloc.
Llavors l'entrevistador va preguntar: què passa si no hi ha espai lliure a la partició, però tampoc veus els fitxers que ocuparien tot l'espai?

A això, vaig dir que sempre podeu mirar els descriptors de fitxers oberts, per exemple, amb l'ordre lsof i entendre quina aplicació ha ocupat tot l'espai disponible, i després podeu actuar segons les circumstàncies, depenent de si les dades són necessàries. .

L'entrevistador em va interrompre a l'última paraula, i va afegir a la seva pregunta: "Suposem que no necessitem les dades, només és un registre de depuració, però l'aplicació està inactiva perquè no pot escriure la depuració"?

"D'acord", vaig respondre, "podem desactivar la depuració a la configuració de l'aplicació i reiniciar-la".
L'entrevistador va objectar: ​​"No, no podem reiniciar l'aplicació, encara tenim dades importants a la memòria i clients importants estan connectats al propi servei, que no podem obligar a tornar a connectar".

"D'acord", vaig dir, "si no podem reiniciar l'aplicació i no ens importen les dades, llavors només podem esborrar aquest fitxer obert mitjançant el descriptor de fitxer, fins i tot si no el veiem a ls comanda al sistema de fitxers".

L'entrevistador estava satisfet, però jo no.

Llavors vaig pensar, per què la persona que prova els meus coneixements no aprofundeix més? Però, i si les dades són importants després de tot? Què passa si no podem reiniciar el procés i, al mateix temps, aquest procés escriu al sistema de fitxers en una partició que no té espai lliure? Què passa si no podem perdre no només les dades ja escrites, sinó també les dades que aquest procés està escrivint o intentant escriure?

Tuzik

Al començament de la meva carrera, estava intentant crear una petita aplicació que necessitava emmagatzemar informació sobre els usuaris. I llavors vaig pensar, com puc relacionar l'usuari amb les seves dades. Per exemple, tinc Ivanov Ivan Ivanovich, i té algunes dades, però com fer-los amics? Puc assenyalar directament que el gos anomenat "Tuzik" pertany a aquest mateix Ivan. Però, què passa si canvia de nom i en comptes d'Ivan es converteix, per exemple, en Olya? Aleshores resultarà que la nostra Olya Ivanovna Ivanova ja no tindrà un gos i el nostre Tuzik continuarà pertanyent a l'inexistent Ivan. La base de dades va ajudar a resoldre aquest problema, que donava a cada usuari un identificador únic (ID), i el meu Tuzik estava lligat a aquest identificador, que, de fet, era només un número de sèrie. Així, el propietari del tuzik tenia el número d'identificació 2, i en algun moment Ivan estava sota aquest identificador, i després Olya es va convertir en el mateix identificador. El problema de la humanitat i la ramaderia estava pràcticament resolt.

Descriptor de fitxer

El problema d'un fitxer i d'un programa que funciona amb aquest fitxer és gairebé el mateix que el nostre gos i humà. Suposem que vaig obrir un fitxer anomenat ivan.txt i vaig començar a escriure-hi la paraula tuzik, però vaig aconseguir escriure només la primera lletra "t" al fitxer i algú va canviar el nom d'aquest fitxer, per exemple, a olya.txt. Però el fitxer és el mateix i encara vull escriure-hi el meu as. Cada vegada que obriu un fitxer amb una trucada al sistema obrir en qualsevol llenguatge de programació, tinc un identificador únic que em dirigeix ​​a un fitxer, aquest identificador és el descriptor del fitxer. I no importa gens què i qui faci a continuació amb aquest fitxer, es pot esborrar, es pot canviar el nom, pot canviar el seu propietari o treure'n els drets de lectura i escriptura, encara hi tindré accés, perquè en el moment d'obrir l'arxiu tenia els drets per llegir-lo i/o escriure'l i vaig aconseguir començar a treballar-hi, la qual cosa vol dir que he de continuar fent-ho.

A Linux, la biblioteca libc obre 3 fitxers descriptors per a cada aplicació (procés) en execució, amb els números 0,1,2. Més informació podeu trobar als enllaços home stdio и home stdout

  • El descriptor de fitxer 0 s'anomena STDIN i està associat a l'entrada de l'aplicació.
  • El descriptor de fitxer 1 s'anomena STDOUT i és utilitzat per aplicacions de sortida com ara ordres d'impressió.
  • El descriptor de fitxer 2 s'anomena STDERR i les aplicacions l'utilitzen per generar missatges d'error.

Si al vostre programa obriu qualsevol fitxer per llegir o escriure, és molt probable que obtingueu el primer identificador gratuït i serà el número 3.

Podeu veure la llista de descriptors de fitxers per a qualsevol procés si coneixeu el seu PID.

Per exemple, obrim una consola amb bash i veiem el PID del nostre procés

[user@localhost ]$ echo $$
15771

A la segona consola, executeu

[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

Podeu ignorar amb seguretat el descriptor del fitxer amb el número 255 en el marc d'aquest article, ja que s'ha obert per a les vostres necessitats pel mateix bash i no per la biblioteca enllaçada.

Ara els 3 fitxers descriptors estan associats amb el dispositiu pseudoterminal /dev/pts, però encara podem manipular-los, per exemple, executar-los a la segona consola

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

I a la primera consola ho veurem

[user@localhost ]$ hello world

Redirecció i canalització

Podeu substituir fàcilment aquests 3 fitxers descriptors en qualsevol procés, inclòs a bash, per exemple, a través d'una canonada (pipe) que connecta dos processos, vegeu

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

Podeu executar aquesta comanda vosaltres mateixos strace -f i a veure què passa dins, però ho faré curt.

El nostre procés bash principal amb PID 15771 analitza la nostra ordre i entén exactament quantes ordres volem executar, en el nostre cas n'hi ha dues: cat i sleep. Bash sap que ha de crear dos processos fills i fusionar-los en un sol tub. En total, bash necessitarà 2 processos secundaris i una canonada.

Abans de crear processos fills, bash executa una trucada al sistema tub i rep nous descriptors de fitxers en una memòria intermèdia temporal, però aquesta memòria intermèdia encara no connecta els nostres dos processos fills de cap manera.

Per al procés principal, sembla que la canonada ja hi és, però encara no hi ha processos secundaris:

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ó, utilitzeu la trucada del sistema clonar bash crea dos processos fills i els nostres tres processos seran així:

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 oblideu que el clon clona el procés juntament amb tots els descriptors de fitxers, de manera que seran els mateixos en el procés pare i en els fills. La tasca del procés principal amb PID 15771 és supervisar els processos secundaris, de manera que només espera una resposta dels fills.

Per tant, no necessita cap canonada i tanca els descriptors de fitxers amb els números 3 i 4.

En el primer procés fill bash amb PID 9004, la trucada del sistema dup2, canvia el nostre descriptor de fitxer STDOUT número 1 a un descriptor de fitxer que apunta a una canalització, en el nostre cas és el número 3. Així, tot el que el primer procés fill amb PID 9004 escriu a STDOUT caurà automàticament a la memòria intermèdia de la canalització.

En el segon procés fill amb PID 9005, bash dup2 el fitxer al descriptor STDIN número 0. Ara tot el que llegirà el nostre segon bash amb PID 9005 es llegirà de la canonada.

Després d'això, els descriptors de fitxers amb els números 3 i 4 també es tanquen als processos fills, ja que ja no s'utilitzen.

Ignoro deliberadament el descriptor de fitxer 255, el mateix bash l'utilitza internament i també es tancarà als processos fills.

A continuació, en el primer procés fill amb PID 9004, bash comença amb una trucada al sistema exec el fitxer executable que hem especificat a la línia d'ordres, en el nostre cas és /usr/bin/cat.

En el segon procés fill amb PID 9005, bash executa el segon executable que hem especificat, en el nostre cas /usr/bin/sleep.

La crida al sistema exec no tanca els descriptors de fitxers tret que s'hagin obert amb el senyalador O_CLOEXEC en el moment en què es va executar la crida oberta. En el nostre cas, després d'executar els fitxers executables, es desaran tots els descriptors de fitxers actuals.

Comprovació a 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

Com podeu veure, el número únic de la nostra canonada és el mateix en tots dos processos. Així, tenim una connexió entre dos processos diferents amb el mateix pare.

Per a aquells que no estiguin familiaritzats amb les trucades del sistema que fa servir bash, recomano encaridament executar ordres mitjançant Strace i veure què passa dins, per exemple, com aquest:

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

Tornem al nostre problema amb quedar-nos sense espai al disc i intentar desar dades sense reiniciar el procés. Escrivim un petit programa que escrigui al disc aproximadament 1 megabyte per segon. A més, si per algun motiu no podíem escriure dades al disc, simplement ho ignorarem i intentarem escriure les dades de nou en un segon. A l'exemple que estic fent servir Python, podeu utilitzar qualsevol altre llenguatge de programació.

[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

Executeu el programa i mireu els descriptors de fitxers

[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

Com podeu veure, tenim els nostres 3 descriptors de fitxer estàndard i un altre que hem obert. Comprovem la mida del fitxer:

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

les dades estan escrites, intentem canviar els drets del fitxer:

[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

Veiem que les dades encara s'estan escrivint, tot i que el nostre usuari no té dret a escriure al fitxer. Intentem eliminar-lo:

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

On s'escriuen les dades? I estan escrites en absolut? Comprovem:

[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í, el nostre descriptor de fitxer encara existeix, i podem treballar amb aquest descriptor de fitxer com el nostre fitxer antic, podem llegir-lo, netejar-lo i copiar-lo.

Mireu la mida del fitxer:

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

La mida del fitxer és 19923457. S'està intentant esborrar el fitxer:

[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

Com podeu veure, la mida del fitxer només augmenta i el nostre tronc no va funcionar. Anem a la documentació de la trucada del sistema obrir. Si utilitzem el senyalador O_APPEND quan obrim un fitxer, aleshores amb cada escriptura, el sistema operatiu comprova la mida del fitxer i escriu dades fins al final del fitxer i ho fa de forma atòmica. Això permet que diversos fils o processos escriguin al mateix fitxer. Però al nostre codi no fem servir aquesta bandera. Podem veure una mida de fitxer diferent a lsof després del tronc només si obrim el fitxer per escriure, el que significa que al nostre codi, en lloc de

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

hem de posar

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

Comprovació amb 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

i amb 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

Programació d'un procés ja en execució

Sovint, en crear i provar un programa, els programadors utilitzen depuradors (per exemple, GDB) o diversos nivells de registre a l'aplicació. Linux ofereix la possibilitat d'escriure i canviar realment un programa que ja s'executa, com ara canviar els valors de les variables, establir un punt d'interrupció, etc.

Tornant a la pregunta original sobre la manca d'espai al disc per escriure un fitxer, intentem simular el problema.

Creem un fitxer per a la nostra partició, que muntarem com a unitat independent:

[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 ~]$

Creem un sistema de fitxers:

[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 ~]$

Muntem el sistema de fitxers:

[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

Creeu un directori amb el nostre propietari:

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

Obrim el fitxer per escriure només al nostre programa:

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

Llançament

[user@localhost ]$ python openforwrite.py 

Esperant uns segons

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

Per tant, tenim el problema descrit al principi d'aquest article. Espai lliure 0, ocupat al 100%.

Recordem que segons les condicions del problema, estem intentant registrar dades molt importants que no es poden perdre. I en fer-ho, hem de solucionar el servei sense reiniciar el procés.

Suposem que encara tenim espai al disc, però en una partició diferent, per exemple, a / home.

Intentem "reprogramar sobre la marxa" el nostre codi.

Mirem el PID del nostre procés, que va consumir tot l'espai del disc:

[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

Connexió a un procés amb gdb

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

Mirem els descriptors de fitxers oberts:

(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

Observem informació sobre el descriptor de fitxer amb el número 3, que ens interessa

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

Tenint en compte quina trucada de sistema fa Python (vegeu més amunt on hem executat Strace i hem trobat una trucada oberta), mentre processem el nostre codi per obrir un fitxer, fem el mateix nosaltres mateixos en nom del nostre procés, però necessitem O_WRONLY|O_CREAT| Els bits O_TRUNC se substitueixen per un valor numèric. Per fer-ho, obriu les fonts del nucli, per exemple aquí i veure quines banderes són responsables de què

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

Combinem tots els valors en un, obtenim 00001101

Executant la nostra trucada des de gdb

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

Així que tenim un nou descriptor de fitxer amb el número 4 i un nou fitxer obert en una altra partició, comproveu:

(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

Recordem l'exemple amb pipe: com bash canvia els descriptors de fitxers i ja hem après la trucada al sistema dup2.

S'està intentant substituir un descriptor de fitxer per un altre

(gdb) call dup2(4,3)
$2 = 3

Comprovem:

(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

Tanqueu el descriptor de fitxer 4, ja que no el necessitem:

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

I surt 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

Comprovant el nou fitxer:

[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

Com podeu veure, les dades s'escriuen en un fitxer nou, comprovem l'antic:

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

Les dades no es perden, l'aplicació funciona, els registres s'escriuen en una nova ubicació.

Posem les coses una mica més difícils

Imagineu que les dades són importants per a nosaltres, però no tenim espai en disc a cap de les particions i no podem connectar el disc.

El que podem fer és redirigir les nostres dades a algun lloc, per exemple, a una canonada, i redirigir les dades de la canonada a la xarxa mitjançant algun programa, com ara netcat.
Podem crear una canonada amb nom amb l'ordre mkfifo. Crearà un pseudofitxer al sistema de fitxers, encara que no hi hagi espai lliure.

Reinicieu l'aplicació i comproveu:

[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 hi ha espai al disc, però creem correctament una canonada amb nom 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

Ara hem d'embolicar d'alguna manera totes les dades que entren en aquesta canonada a un altre servidor a través de la xarxa, el mateix netcat és adequat per a això.

Al servidor remote-server.example.com, executeu

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

Al nostre servidor de problemes, executeu-lo en un terminal independent

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

Ara totes les dades que entren a la canonada aniran automàticament a stdin a netcat, que les enviarà a la xarxa al port 7777.

Tot el que hem de fer és començar a escriure les nostres dades en aquesta canonada amb nom.

Ja tenim una aplicació en funcionament:

[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 totes les banderes, només necessitem O_WRONLY ja que el fitxer ja existeix i no cal esborrar-lo

[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

Comprovació del servidor remot remote-server.example.com

[user@localhost ~]$ ls -lah 123.txt 
-rw-rw-r-- 1 user user 38M Oct  8 14:21 123.txt

Arriben les dades, comprovem el servidor del 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

Es guarden les dades, es resol el problema.

Aprofito per saludar els meus companys de Degiro.
Escolta els podcasts de Radio-T.

Tot bé.

Com a deures, proposo pensar què hi haurà als descriptors de fitxers del procés cat i sleep si executeu l'ordre següent:

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

Font: www.habr.com

Afegeix comentari