Fildescriptor i Linux med eksempler

Engang, under et interview, spurgte de mig, hvad jeg ville gøre, hvis jeg opdagede en ikke-fungerende tjeneste, fordi der ikke var plads på disken?

Jeg svarede selvfølgelig, at jeg ville se, hvad der optager pladsen, og hvis det var muligt, ville jeg rydde op.
Så spurgte intervieweren, hvad hvis der ikke er ledig plads på partitionen, men du heller ikke kan se filer, der optager al pladsen?

Til det sagde jeg, at man altid kan se åbne fil-deskriptorer, for eksempel med kommandoen lsof, og forstå hvilket program der har taget al den tilgængelige plads, og derefter kan man handle afhængigt af situationen, afhængigt af om dataene er vigtige.

Intervieweren afbrød mig på det sidste ord og tilføjede sit spørgsmål: «Antag, at vi ikke har brug for dataene, det er bare en debug-log, men applikationen fungerer ikke, fordi den ikke kan skrive debug?»

«Okay,» svarede jeg, «vi kan deaktivere debug i applikationens konfiguration og genstarte den.»
Intervieweren indvendte: «Nej, vi kan ikke genstarte applikationen, da vi stadig har vigtige data gemt i hukommelsen, og de vigtige kunder er tilsluttet selve tjenesten, som vi ikke kan tvinge til at genoprette forbindelsen.»

«Nå godt,» sagde jeg, «hvis vi ikke kan genstarte applikationen og dataene ikke er vigtige, kan vi blot rydde denne åbne fil via fil-deskriptoren, selvom vi ikke ser den med kommandoen ls i filsystemet.»

Intervieweren var tilfreds, men jeg var ikke.

Så tænkte jeg, hvorfor personen, der tester mine færdigheder, ikke graver dybere? Hvad hvis dataene alligevel er vigtige? Hvad hvis vi ikke kan genstarte processen, og samtidig skriver denne proces til filsystemet på en partition, hvor der ikke er ledig plads? Hvad hvis vi ikke kan miste ikke kun de allerede gemte data, men også de data, som denne proces skriver eller forsøger at skrive?

Tuzik

I begyndelsen af min karriere forsøgte jeg at oprette en lille applikation, hvor jeg skulle gemme oplysninger om brugere. På det tidspunkt tænkte jeg over, hvordan jeg skulle knytte brugeren til hans data. For eksempel har jeg Ivanov Ivan Ivanovich, og han har nogle data, men hvordan skal jeg få dem til at passe sammen? Jeg kan angive direkte, at hunden ved navn "Tuzik" tilhører denne Ivan. Men hvad nu, hvis han skifter navn og i stedet for Ivan bliver til Olya? Så vil vores Olya Ivanovna Ivanova ikke længere have en hund, men vores Tuzik vil stadig tilhøre en ikke-eksisterende Ivan. Dette problem blev løst ved hjælp af en database, der gav hver bruger en unik identifikator (ID), og min Tuzik blev knyttet til dette ID, som i bund og grund bare var et ordensnummer. Således havde ejerens Tuzik ID nummer 2, og på et tidspunkt var det Ivan, og lidt senere blev det Olya. Problemet med menneskehed og dyrehold var næsten løst.

Fil-deskriptor

Problemet med filen og programmet, der arbejder med denne fil, er omtrent det samme som vores hund og mennesket. Antag, at jeg åbner en fil ved navn ivan.txt og begynder at skrive ordet tuzik, men kun når at skrive det første bogstav "t" i filen, og denne fil bliver omdøbt af nogen, for eksempel til olya.txt. Men filen er den samme, og jeg vil stadig gerne skrive min Tuzik i den. Hver gang jeg åbner filen med et systemopkald open i et hvilket som helst programmeringssprog får jeg en unik ID, der peger på filen, dette ID er fil-deskriptoren. Og det er slet ikke vigtigt, hvad og hvem der gør med filen derefter; den kan blive slettet, omdøbt, ændret ejer, eller rettigheder til læsning og skrivning kan blive ændret; jeg vil stadig have adgang til den, fordi jeg på tidspunktet for åbningen af filen havde rettigheder til dens læsning og/eller skrivning, og jeg nåede at begynde at arbejde med den, så jeg skal fortsætte med det.

I Linux åbner libc-biblioteket 3 fil-deskriptorer for hver kørende applikation (proces) med numrene 0, 1, 2. Du kan finde flere oplysninger via links man stdio og man stdout

  • Fil-deskriptor 0 kaldes STDIN og associeres med inputdata i applikationen
  • Filbeskrivelsen 1 hedder STDOUT og bruges af applikationer til at udskrive data, for eksempel print-kommandoerne.
  • Filbeskrivelsen 2 hedder STDERR og bruges af applikationer til at udskrive data, der rapporterer fejl.

Hvis du åbner en fil til læsning eller skrivning i dit program, vil du sandsynligvis få den første ledige ID, og det vil være nummer 3.

Du kan se listen over filbeskrivelser for enhver proces, hvis du kender dens PID.

For eksempel åbner vi en konsol med bash og ser PID for vores proces.

[user@localhost ]$ echo $$
15771

I den anden konsol vil vi starte

[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

Filbeskrivelsen med nummer 255 kan du trygt ignorere i denne artikel; den blev åbnet til egne behov af selve bash og ikke af den tilknyttede bibliotek.

I øjeblikket er alle 3 filbeskrivelser knyttet til en pseudoterminal. /dev/pts, men vi kan stadig manipulere med dem, for eksempel vil vi starte i den anden konsol.

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

Og i den første konsol vil vi se

[user@localhost ]$ hello world

Redirect og Pipe

Du kan let overskrive disse 3 filbeskrivelser i enhver proces, herunder i bash, for eksempel gennem en pipe, der forbinder to processer. Lad os se.

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

Du kan selv køre denne kommando med strace -f og se, hvad der sker indeni, men jeg vil kort fortælle.

Vores forældreproces bash med PID 15771 parser os kommandoen og forstår, hvor mange kommandoer vi ønsker at køre; i vores tilfælde er der to: cat og sleep. Bash ved, at den skal oprette to underprocesser og forbinde dem med en pipe. I alt skal bash bruge 2 underprocesser og en pipe.

Før oprettelsen af underprocesser foretager bash et systemopkald pipe og får nye filbeskrivelser til den midlertidige bufferpipe, men denne buffer forbinder ikke endnu vores to underprocesser.

For forældreprocessen ser det ud til, at pipen allerede eksisterer, mens underprocesserne endnu ikke er der:

PID    kommando
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

Derefter bruger systemopkaldet clone bash til at oprette to datterprocesser, og vores tre processer vil se sådan ud:

PID    kommando
15771  bash
lrwx------ 1 bruger bruger 64 Okt  7 15:42 0 -> /dev/pts/21
lrwx------ 1 bruger bruger 64 Okt  7 15:42 1 -> /dev/pts/21
lrwx------ 1 bruger bruger 64 Okt  7 15:42 2 -> /dev/pts/21
lrwx------ 1 bruger bruger 64 Okt  7 15:42 3 -> pipe:[253543032]
lrwx------ 1 bruger bruger 64 Okt  7 15:42 4 -> pipe:[253543032]
lrwx------ 1 bruger bruger 64 Okt  7 15:42 255 -> /dev/pts/21
PID    kommando
9004  bash
lrwx------ 1 bruger bruger 64 Okt  7 15:57 0 -> /dev/pts/21
lrwx------ 1 bruger bruger 64 Okt  7 15:57 1 -> /dev/pts/21
lrwx------ 1 bruger bruger 64 Okt  7 15:57 2 -> /dev/pts/21
lrwx------ 1 bruger bruger 64 Okt  7 15:57 3 -> pipe:[253543032]
lrwx------ 1 bruger bruger 64 Okt  7 15:57 4 -> pipe:[253543032]
lrwx------ 1 bruger bruger 64 Okt  7 15:57 255 -> /dev/pts/21
PID    kommando
9005  bash
lrwx------ 1 bruger bruger 64 Okt  7 15:57 0 -> /dev/pts/21
lrwx------ 1 bruger bruger 64 Okt  7 15:57 1 -> /dev/pts/21
lrwx------ 1 bruger bruger 64 Okt  7 15:57 2 -> /dev/pts/21
lrwx------ 1 bruger bruger 64 Okt  7 15:57 3 -> pipe:[253543032]
lrwx------ 1 bruger bruger 64 Okt  7 15:57 4 -> pipe:[253543032]
lrwx------ 1 bruger bruger 64 Okt  7 15:57 255 -> /dev/pts/21

Husk at clone kloner processen sammen med alle filbeskrivelser, så de vil være ens i både forælderprocessen og datterprocesserne. Opgaven for forælderprocessen med PID 15771 er at overvåge datterprocesserne, så den venter simpelthen på svar fra dem.

Dermed har den ikke brug for pipe, og den lukker filbeskrivelserne med numrene 3 og 4.

I den første datterproces bash med PID 9004, via systemopkaldet dup2, ændrer den vores STDOUT filbeskrivelse fra nummer 1 til filbeskriverens, der peger på pipe, i dette tilfælde nummer 3. På denne måde vil alt, hvad den første datterproces med PID 9004 skriver til STDOUT, automatisk havne i pipe-bufferen.

I den anden datterproces med PID 9005 ændrer bash via dup2 filbeskrivelsen STDIN fra nummer 0. Nu vil alt, hvad vores anden bash med PID 9005 læser, blive læst fra pipe.

Herefter lukkes filbeskrivelserne med numrene 3 og 4 også i datterprocesserne, da de ikke længere bruges.

Filbeskriveren 255 ignorerer jeg bevidst, den bruges til bash' interne behov og vil også blive lukket i datterprocesserne.

Dernæst i den første datterproces med PID 9004 lancerer bash via systemopkaldet exec den eksekverbare fil, vi angav i kommandolinjen, i vores tilfælde er det /usr/bin/cat.

I den anden datterproces med PID 9005 lancerer bash den anden eksekverbare fil, vi angav, i vores tilfælde er det /usr/bin/sleep.

Systemopkaldet exec lukker ikke filbeskrivelser, hvis de ikke blev åbnet med flaget O_CLOEXEC under kaldet til open. I vores tilfælde vil alle nuværende filbeskrivelser forblive bevaret efter at have kørt de eksekverbare filer.

Lad os tjekke i konsollen:

[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

Som du kan se, har vores pipe et unikt nummer, der er det samme i begge processer. Dermed har vi en forbindelse mellem to forskellige processer med én overordnet.

For dem, der ikke er bekendt med systemopkald, som bash bruger, vil jeg stærkt anbefale at køre kommandoerne gennem strace for at se, hvad der sker indeni, for eksempel sådan her:

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

Lad os vende tilbage til vores problem med pladsmangel på disken og forsøge at gemme data uden at genstarte processen. Vi vil skrive et lille program, der vil skrive til disken omkring 1 megabyte i sekundet. Hvis vi af en eller anden grund ikke kan skrive data til disken, vil vi blot ignorere dette og forsøge at skrive data igen om et sekund. I eksemplet bruger jeg Python, men du kan bruge et andet programmeringssprog.

[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

Lad os køre programmet og se på filbeskrivelserne

[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

Som vi kan se, har vi vores 3 standard fil-håndtag og én ekstra, som vi har åbnet. Lad os tjekke filens størrelse:

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

Dataene skrives, lad os forsøge at ændre rettighederne til filen:

[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

Vi kan se, at dataene stadig skrives, selvom vores bruger ikke har rettigheder til at skrive i filen. Lad os prøve at slette den:

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

Hvor skrives dataene? Og skrives de overhovedet? Lad os tjekke:

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

Ja, vores fil-håndtag eksisterer stadig, og vi kan arbejde med dette håndtag som med vores gamle fil. Vi kan læse, rydde og kopiere den.

Lad os se på filens størrelse:

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

Filens størrelse er 19923457. Lad os prøve at rydde filen:

[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

Som vi kan se, vokser filens størrelse kun, og vores truncate fungerer ikke. Lad os konsultere dokumentationen for systemkaldet. openNår vi åbner en fil med flaget O_APPEND, tjekker operativsystemet filens størrelse ved hver skrivning og skriver dataene til slutningen af filen atomisk. Dette muliggør for flere tråde eller processer at skrive til den samme fil. Men i vores kode bruger vi ikke dette flag. Vi kan se en anden filstørrelse i lsof efter truncate, kun hvis vi åbner filen til vedhæftning. Derfor bør vi i vores kode ændre

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

til

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

Lad os tjekke med "w" flaget.

[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

og med flagget «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

Programmering af en allerede kørende proces

Ofte bruger programmører, når de opretter og tester programmer, debuggere (f.eks. GDB) eller forskellige logningsniveauer i applikationen. Linux giver mulighed for faktisk at skrive til og ændre et allerede kørende program, f.eks. ændre variabelværdier, sætte breakpoint osv.

Når vi vender tilbage til det oprindelige spørgsmål om mangel på diskplads til at skrive en fil, lad os forsøge at simulere problemet.

Lad os oprette en fil til vores partition, som vi vil montere som en separat disk:

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

Lad os oprette et filsystem:

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

Monter filsystemet:

[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

Opretter et bibliotek med vores ejer:

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

Åbner filen som kun kan skrives til i vores program:

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

Starting

[user@localhost ]$ python openforwrite.py 

Vent et par sekunder

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

Så vi har fået det problem, der blev beskrevet i begyndelsen af denne artikel. 0 ledig plads, 100% optaget.

Vi husker, at ifølge opgaven forsøger vi at skrive meget vigtige data, som ikke kan gå tabt. Og samtidig skal vi reparere tjenesten uden at genstarte processen.

Antag, at vi stadig har plads på disken, men i en anden partition, f.eks. /home.

Lad os prøve at "omprogrammere" vores kode on-the-fly.

Lad os se PID'en af vores proces, der har optaget al diskplads:

[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

Tilslut til processen via gdb

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

Lad os se på de åbne filbeskrivelser:

(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

Lad os se oplysningerne om filbeskrivelsen med nummer 3, som vi er interesserede i

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

Når vi husker, hvilken systemopkald Python foretager (se ovenfor, hvor vi kørte strace og fandt opkaldet open), gør vi det samme selv på vegne af vores proces for at åbne en fil, men vi skal erstatte bits O_WRONLY|O_CREAT|O_TRUNC med en numerisk værdi. For at gøre dette åbner vi kildekoden til kernen, for eksempel her og ser, hvilke flag der svarer til hvad

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

Vi kombinerer alle værdier til en, så får vi 00001101

Vi kører vores opkald fra gdb

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

Så vi har fået en ny fildescriptor med nummer 4 og en ny åben fil på en anden partition, lad os tjekke:

(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

Vi husker eksemplet med pipe – hvordan bash ændrer fildescriptors, og vi har allerede lært systemopkaldet dup2.

Vi prøver at erstatte en fildescriptor med en anden

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

Vi tjekker:

(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

Vi lukker fildescriptor 4, da vi ikke har brug for den:

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

Og vi går ud af 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

Vi tjekker den nye fil:

[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

Som vi kan se, bliver data gemt i den nye fil, lad os tjekke den gamle:

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

Data er ikke gået tabt, applikationen fungerer, logfilerne skrives til det nye sted.

Lad os gøre opgaven lidt mere kompleks

Lad os forestille os, at data er vigtige for os, men der er ikke plads på disken i nogen partitioner, og vi kan ikke tilslutte en disk.

Hvad vi kan gøre, er at omdirigere vores data et eller andet sted, for eksempel til en pipe, og dataene fra pipen kan vi dernæst omdirigere til netværket gennem et program, f.eks. netcat.
Vi kan oprette en navngivet pipe med kommandoen mkfifo. Den vil oprette en pseudofil i filsystemet, selvom der ikke er ledig plads.

Vi genstarter applikationen og tjekker:

[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

Der er ikke plads på disken, men vi opretter med succes en navngivet pipe der:

[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

Nu skal vi på en eller anden måde pakke alle data, der går ind i denne pipe til en anden server over nettet, det vil samme netcat klare.

På serveren remote-server.example.com starter vi

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

På vores problemserver starter vi i et andet terminalvindue

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

Nu vil alle data, der kommer ind i pipen, automatisk gå til stdin i netcat, som vil sende dem over netværket til port 7777.

Alt hvad vi mangler at gøre, er at begynde at skrive vores data ind i denne navngivne pipe.

Vi har allerede en kørende applikation:

[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

Af alle flagene har vi kun brug for O_WRONLY, da filen allerede eksisterer, og vi behøver ikke at rense den.

[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

Vi tjekker den fjernserver remote-server.example.com

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

Dataene strømmer, vi tjekker den problematiske server

[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

Dataene er gemt, problemet er løst.

Jeg sender hilsner til kollegerne fra Degiro.
Lyt til podcasts fra Radio-T.

Ønsker alle godt.

Som hjemmearbejde foreslår jeg, at I overvejer, hvad der vil være i procesfilbeskrivelserne for cat og sleep, hvis vi kører denne kommando:

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

Kilde: habr.com

Køb pålidelig webhosting med DDoS-beskyttelse, VPS VDS-servere 🔥 Køb pålidelig webhosting med DDoS-beskyttelse, VPS VDS-servere | ProHoster