Filbeskrivning i Linux med exempel

En gång, under en intervju, fick jag frågan, vad kommer du att göra om du upptäcker att en tjänst inte fungerar på grund av att disken har slut på utrymme?

Självklart svarade jag att jag skulle se vad som var upptaget av den här platsen och om möjligt skulle jag städa platsen.
Sedan frågade intervjuaren, vad händer om det inte finns något ledigt utrymme på partitionen, men du inte heller ser några filer som skulle ta upp allt utrymme?

Till detta sa jag att man alltid kan titta på öppna fildeskriptorer, till exempel med kommandot lsof, och förstå vilken applikation som har tagit upp allt tillgängligt utrymme, och sedan kan man agera efter omständigheterna, beroende på om data behövs .

Intervjuaren avbröt mig på sista ordet och lade till sin fråga: "Anta att vi inte behöver data, det är bara en felsökningslogg, men applikationen fungerar inte eftersom den inte kan skriva en felsökning"?

"Okej", svarade jag, "vi kan stänga av felsökning i applikationskonfigurationen och starta om den."
Intervjuaren invände: "Nej, vi kan inte starta om applikationen, vi har fortfarande viktig data lagrad i minnet och viktiga klienter är anslutna till själva tjänsten, som vi inte kan tvinga att återansluta igen."

"okej," sa jag, "om vi inte kan starta om programmet och data inte är viktiga för oss, då kan vi helt enkelt rensa den här öppna filen genom filbeskrivningen, även om vi inte ser den i ls-kommandot på filsystemet."

Intervjuaren var nöjd, men det var jag inte.

Sedan tänkte jag, varför gräver inte personen som testar mina kunskaper djupare? Men vad händer om uppgifterna trots allt är viktiga? Vad händer om vi inte kan starta om en process och processen skriver till filsystemet på en partition som inte har något ledigt utrymme? Vad händer om vi inte bara kan förlora den data som redan har skrivits, utan också den data som denna process skriver eller försöker skriva?

jolle

Tidigt i min karriär försökte jag skapa en liten applikation som behövde lagra användarinformation. Och då tänkte jag, hur kan jag matcha användaren med hans data. Till exempel har jag Ivanov Ivan Ivanovich, och han har lite information, men hur kan jag bli vän med dem? Jag kan direkt påpeka att hunden som heter "Tuzik" tillhör just denne Ivan. Men tänk om han byter namn och istället för Ivan blir till exempel Olya? Då kommer det att visa sig att vår Olya Ivanovna Ivanova inte längre kommer att ha en hund, och vår Tuzik kommer fortfarande att tillhöra den icke-existerande Ivan. En databas som gav varje användare en unik identifierare (ID) hjälpte till att lösa detta problem, och min Tuzik var knuten till detta ID, som i själva verket bara var ett serienummer. Ässägaren hade alltså ID-nummer 2, och vid någon tidpunkt var Ivan under detta ID, och sedan blev Olya under samma ID. Problemet med mänskligheten och djurhållningen var praktiskt taget löst.

Filbeskrivning

Problemet med filen och programmet som fungerar med denna fil är ungefär detsamma som för vår hund och människa. Anta att jag öppnade en fil som heter ivan.txt och började skriva ordet tuzik i den, men bara lyckades skriva den första bokstaven "t" i filen, och den här filen döptes om av någon, till exempel till olya.txt. Men filen förblir densamma, och jag vill fortfarande spela in mitt ess i den. Varje gång en fil öppnas av ett systemanrop öppet i alla programmeringsspråk får jag ett unikt ID som pekar mig till en fil, detta ID är filbeskrivningen. Och det spelar ingen som helst roll vad och vem som gör med den här filen härnäst, den kan raderas, den kan bytas om, ägaren kan ändras eller rättigheterna att läsa och skriva kan tas bort, jag kommer fortfarande att ha åtkomst till den, för när jag öppnade filen hade jag rättigheterna att läsa och/eller skriva den och jag lyckades börja arbeta med den, vilket betyder att jag måste fortsätta att göra det.

I Linux öppnar libc-biblioteket 3 deskriptorfiler för varje körande applikation (process), numrerade 0,1,2. Mer information finns på länkarna man stdio и man standout

  • Filbeskrivning 0 kallas STDIN och är associerad med applikationsinmatning
  • Filbeskrivning 1 kallas STDOUT och används av applikationer för att mata ut data, såsom utskriftskommandon
  • Filbeskrivning 2 kallas STDERR och används av applikationer för att mata ut felmeddelanden.

Om du i ditt program öppnar en fil för läsning eller skrivning, kommer du troligen att få det första gratis ID:t och det blir nummer 3.

Listan över filbeskrivningar kan ses för alla processer om du känner till dess PID.

Låt oss till exempel öppna bash-konsolen och titta på PID för vår process

[user@localhost ]$ echo $$
15771

Låt oss springa i den andra konsolen

[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

Du kan säkert ignorera fildeskriptor nummer 255 för denna artikels syften; den öppnades för sina behov av bash själv och inte av det länkade biblioteket.

Nu är alla tre deskriptorfilerna associerade med pseudoterminalenheten /dev/pts, men vi kan fortfarande manipulera dem, till exempel köra dem i en andra konsol

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

Och i den första konsolen kommer vi att se

[user@localhost ]$ hello world

Redirect och Pipe

Du kan enkelt åsidosätta dessa 3 deskriptorfiler i vilken process som helst, inklusive i bash, till exempel genom ett rör som förbinder två processer, se

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

Du kan köra detta kommando själv med strace -f och se vad som händer inuti, men jag ska berätta kort.

Vår föräldrabash-process med PID 15771 analyserar vårt kommando och förstår exakt hur många kommandon vi vill köra, i vårt fall finns det två av dem: katt och sömn. Bash vet att det måste skapa två underordnade processer och slå samman dem till ett rör. Totalt kommer bash att behöva 2 underordnade processer och ett rör.

Bash kör ett systemanrop innan underordnade processer skapas Röret och tar emot nya filbeskrivningar på den temporära pipe-bufferten, men denna buffert kopplar ännu inte ihop våra två underordnade processer.

För föräldraprocessen ser det ut som att det redan finns ett rör, men det finns inga underordnade processer ännu:

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

Använd sedan systemanropet klona bash skapar två underordnade processer, och våra tre processer kommer att se ut så här:

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

Glöm inte att klon klonar processen tillsammans med alla filbeskrivningar, så de kommer att vara desamma i föräldraprocessen och i de underordnade. Föräldraprocessens jobb med PID 15771 är att övervaka de underordnade processerna, så den väntar helt enkelt på ett svar från barnen.

Därför behöver den inte pipe, och den stänger filbeskrivningar numrerade 3 och 4.

I den första underordnade bash-processen med PID 9004 anropar systemet dup2, ändrar vår STDOUT-fildeskriptor nummer 1 till en fildeskriptor som pekar mot pipe, i vårt fall är det nummer 3. Allt som den första underordnade processen med PID 9004 skriver till STDOUT kommer alltså automatiskt hamna i pipebufferten.

I den andra underordnade processen med PID 9005 använder bash dup2 för att ändra filbeskrivningen STDIN nummer 0. Nu kommer allt som vår andra bash med PID 9005 kommer att läsa att läsas från pipen.

Efter detta stängs även fildeskriptorer med nummer 3 och 4 i underordnade processer, eftersom de inte längre används.

Jag ignorerar medvetet fildeskriptor 255; den används för interna ändamål av bash själv och kommer också att stängas i underordnade processer.

Därefter, i den första underordnade processen med PID 9004, börjar bash använda ett systemanrop exec den körbara filen som vi angav på kommandoraden, i vårt fall är det /usr/bin/cat.

I den andra underordnade processen med PID 9005 kör bash den andra körbara filen vi angav, i vårt fall /usr/bin/sleep.

Exec-systemanropet stänger inte filhandtag om de inte öppnades med O_CLOEXEC-flaggan vid den tidpunkt då det öppna anropet gjordes. I vårt fall, efter att ha startat de körbara filerna, kommer alla aktuella filbeskrivningar att sparas.

Kolla i konsolen:

[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 är det unika numret på vårt rör detsamma i båda processerna. Vi har alltså ett samband mellan två olika processer med samma förälder.

För de som inte är bekanta med systemanropen som bash använder rekommenderar jag starkt att köra kommandona genom strace och se vad som händer internt, till exempel så här:

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

Låt oss återgå till vårt problem med lågt diskutrymme och försöka spara data utan att starta om processen. Låt oss skriva ett litet program som kommer att skriva ungefär 1 megabyte per sekund till disken. Dessutom, om vi av någon anledning inte kunde skriva data till disken, kommer vi helt enkelt att ignorera detta och försöka skriva data igen om en sekund. I exemplet jag använder Python kan du använda vilket annat programmeringsspråk som helst.

[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

Låt oss köra programmet och titta på filbeskrivningarna

[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 du kan se har vi våra 3 standardfilbeskrivningar och en till som vi öppnat. Låt oss kontrollera filstorleken:

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

Datan skrivs, vi försöker ändra behörigheterna på 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 ser att data fortfarande skrivs, även om vår användare inte har behörighet att skriva till filen. Låt oss försöka ta bort det:

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

Var är data skriven? Och är de skrivna överhuvudtaget? Vi kontrollerar:

[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, vår filbeskrivning finns fortfarande och vi kan behandla denna filbeskrivning som vår gamla fil, vi kan läsa, rensa och kopiera den.

Låt oss titta på filstorleken:

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

Filstorleken är 19923457. Låt oss försöka rensa 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 du kan se ökar filstorleken bara och vår trunk fungerade inte. Låt oss titta på systemanropsdokumentationen öppet. Om vi ​​använder flaggan O_APPEND när vi öppnar en fil, kontrollerar operativsystemet filstorleken vid varje skrivning och skriver data till slutet av filen och gör detta atomärt. Detta gör att flera trådar eller processer kan skriva till samma fil. Men i vår kod använder vi inte denna flagga. Vi kan se en annan filstorlek i lsof efter trunk endast om vi öppnar filen för ytterligare skrivning, vilket betyder i vår kod istället

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

vi måste sätta

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

Kollar med "w"-flaggan

[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

och med "a"-flaggan

[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

Programmera en redan pågående process

Ofta använder programmerare, när de skapar och testar program, debuggers (till exempel GDB) eller olika nivåer av inloggning i applikationen. Linux ger möjligheten att faktiskt skriva och ändra ett redan kört program, till exempel ändra värden på variabler, ställa in en brytpunkt, etc., etc.

För att återgå till den ursprungliga frågan om inte tillräckligt med diskutrymme för att skriva en fil, låt oss försöka simulera problemet.

Låt oss skapa en fil för vår partition, som vi kommer att montera 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 ~]$

Låt oss skapa ett 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 ~]$

Montera 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

Vi skapar en katalog med vår ägare:

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

Låt oss endast öppna filen för att skriva i vårt program:

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

Vi lanserar

[user@localhost ]$ python openforwrite.py 

Vi väntar några sekunder

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

Så vi har problemet som beskrivs i början av den här artikeln. Ledigt utrymme 0, 100 % upptaget.

Vi kommer ihåg att vi i enlighet med villkoren för uppgiften försöker spela in mycket viktig data som inte kan gå förlorad. Och samtidigt måste vi fixa tjänsten utan att starta om processen.

Låt oss säga att vi fortfarande har diskutrymme, men i en annan partition, till exempel i /home.

Låt oss försöka "programmera om i farten" vår kod.

Låt oss titta på PID för vår process, som har ätit upp allt diskutrymme:

[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

Anslut till processen via gdb

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

Låt oss titta på de öppna filbeskrivningarna:

(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

Vi tittar på informationen om fildeskriptor nummer 3, som intresserar oss

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

Med tanke på vilket systemanrop Python gör (se ovan var vi körde strace och hittade det öppna anropet), när vi bearbetar vår kod för att öppna en fil, gör vi samma sak själva på uppdrag av vår process, men vi behöver O_WRONLY|O_CREAT| O_TRUNC-bitar ersätts med ett numeriskt värde. För att göra detta, öppna till exempel kärnkällorna här och titta på vilka flaggor som ansvarar för vad

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

Vi kombinerar alla värden till ett, vi får 00001101

Vi kör vårt samtal från gdb

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

Så vi fick en ny filbeskrivning med nummer 4 och en ny öppen fil på en annan partition, vi kontrollerar:

(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 minns exemplet med pipe - hur bash ändrar filbeskrivningar, och vi har redan lärt oss dup2-systemanropet.

Vi försöker ersätta en filbeskrivning med en annan

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

Vi kontrollerar:

(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 stänger filbeskrivning 4, eftersom vi inte behöver den:

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

Och avsluta 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

Kontrollerar den nya filen:

[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 du kan se skrivs data till en ny fil, låt oss kontrollera den gamla:

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

Ingen data går förlorad, applikationen fungerar, loggar skrivs till en ny plats.

Låt oss komplicera uppgiften lite

Låt oss föreställa oss att data är viktiga för oss, men vi har inte diskutrymme i någon av partitionerna och vi kan inte ansluta disken.

Det vi kan göra är att omdirigera vår data någonstans, till exempel till pipe, och i sin tur omdirigera data från pipe till nätverket genom något program, till exempel netcat.
Vi kan skapa en namngiven pipe med kommandot mkfifo. Det kommer att skapa en pseudofil på filsystemet även om det inte finns något ledigt utrymme på den.

Starta om programmet och kontrollera:

[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

Det finns inget diskutrymme, men vi har skapat en namngiven pipe där:

[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 måste vi på något sätt slå in all data som går in i detta rör till en annan server via nätverket; samma netcat är lämplig för detta.

På servern remote-server.example.com kör vi

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

På vår problematiska server startar vi i en separat terminal

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

Nu kommer all data som hamnar i röret automatiskt att gå till stdin i netcat, som skickar den till nätverket på port 7777.

Allt vi behöver göra är att börja skriva in vår data i detta namngivna rör.

Vi har redan applikationen igång:

[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

Av alla flaggor behöver vi bara O_WRONLY eftersom filen redan finns och vi behöver inte rensa 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

Kontrollerar fjärrservern remote-server.example.com

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

Data kommer, vi kontrollerar problemservern

[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

Data sparas, problemet är löst.

Jag tar tillfället i akt att säga hej till mina kollegor från Degiro.
Lyssna på Radio-T-poddar.

Allt väl.

Som läxa föreslår jag att du tänker på vad som kommer att finnas i processens filbeskrivningar cat and sleep om du kör följande kommando:

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

Källa: will.com

Lägg en kommentar