En gÄng, under en intervju, blev jag frÄgad vad jag skulle göra om jag upptÀckte en icke-fungerande tjÀnst pÄ grund av att det inte fanns tillrÀckligt med utrymme pÄ disken.
SjÀlvklart svarade jag att jag skulle kontrollera vad som tar upp detta utrymme och om det Àr möjligt, stÀda upp utrymmet.
DÄ frÄgade intervjuaren, vad hÀnder om det inte finns nÄgot ledigt utrymme pÄ partitionen, men du heller inte ser nÄgra filer som tar upp allt utrymme?
Till det svarade jag att man alltid kan titta pÄ de öppna filbeskrivarna, till exempel med kommandot lsof, för att förstÄ vilket program som har tagit allt tillgÀngligt utrymme, och sedan kan man agera utifrÄn omstÀndigheterna, beroende pÄ om datan Àr viktig eller inte.
Intervjuaren avbröt mig pÄ det sista ordet och kompletterade sin frÄga: "Anta att datan inte Àr viktig för oss, det Àr bara en debug-logg, men applikationen fungerar inte eftersom den inte kan skriva debug-loggen"?
"Okej", svarade jag, "vi kan stÀnga av debug i applikationens konfiguration och starta om den".
Intervjuaren invÀnde: "Nej, vi kan inte starta om applikationen, vi har fortfarande viktiga data lagrade i minnet och viktiga kunder Àr kopplade till tjÀnsten, som vi inte kan tvinga att koppla om."
"NÄvÀl", sa jag, "om vi inte kan starta om applikationen och datan inte Àr viktig, sÄ kan vi helt enkelt rensa den hÀr öppna filen genom filbeskrivaren, Àven om vi inte ser den med ls-kommandot i filsystemet."
Intervjuaren verkade nöjd, men jag gjorde inte det.
DÄ tÀnkte jag, varför grÀver inte personen som bedömer mina kunskaper djupare? Vad hÀnder om datan faktiskt Àr viktig? Vad hÀnder om vi inte kan starta om processen, och under tiden skriver denna process till filsystemet pÄ en partition dÀr det inte finns nÄgot ledigt utrymme? Vad hÀnder om vi inte kan förlora inte bara redan inspelade data, utan Àven de data som denna process skriver eller försöker skriva?
Tuzik
I början av min karriÀr försökte jag skapa en liten applikation dÀr jag behövde lagra information om anvÀndare. Jag tÀnkte dÄ, hur ska jag koppla en anvÀndare till deras data? Till exempel, jag har Ivanov Ivan Ivanovich, och han har vissa uppgifter, men hur kopplar jag dem? Jag kan direkt ange att hunden vid namn "Tuzik" tillhör just den Ivan. Men vad hÀnder om han byter namn och plötsligt blir Olya? DÄ skulle vÄr Olya Ivanovna Ivanova inte lÀngre ha en hund, medan vÄr Tuzik fortfarande skulle tillhöra den icke-existerande Ivan. För att lösa detta problem hjÀlpte en databas, som gav varje anvÀndare en unik identifierare (ID), och min Tuzik kopplades till detta ID, som i grund och botten bara var ett ordningsnummer. SÄledes hade Tuziks Àgare ID 2, och vid en viss tidpunkt hade detta ID Ivan, och senare blev det Olya. MÀnniskans och djurhÄllningens problem var nÀstan löst.
Filbeskrivare
Problemet med filen och programmet som arbetar med denna fil Àr ungefÀr detsamma som för vÄr hund och mÀnniskan. Anta att jag öppnar en fil med namnet ivan.txt och börjar skriva ordet tuzik, men jag hinner bara skriva den första bokstaven "t" i filen, och den hÀr filen blir omdöpt av nÄgon, till exempel till olya.txt. Men filen förblir densamma, och jag vill fortfarande skriva min Tuzik i den. Varje gÄng jag öppnar filen med ett systemsamtal i vilket programmeringssprÄk som helst fÄr jag en unik ID som pekar pÄ filen, denna ID Àr filbeskrivaren. Och det spelar ingen roll vad som hÀnder med filen sedan, den kan raderas, den kan omdöpas, Àgaren kan bytas ut eller lÀsrÀttigheter kan tas bort, jag kommer fortfarande ha tillgÄng till den, eftersom jag vid tidpunkten för att öppna filen hade rÀtt att lÀsa och/eller skriva och jag hann börja arbeta med den, vilket betyder att jag bör fortsÀtta göra det.
I Linux öppnar libc-biblioteket tre filbeskrivare för varje körd applikation (process), med numren 0, 1, 2. Mer information kan du hitta via lÀnkarna och
- Filbeskrivare 0 kallas STDIN och Àr kopplad till dataingÄngar i applikationen
- Filbeskrivaren 1 kallas STDOUT och anvÀnds av applikationer för att skriva ut data, till exempel kommandon som print.
- Filbeskrivaren 2 kallas STDERR och anvÀnds av applikationer för att skriva ut data som rapporterar om fel.
Om du öppnar en fil för lÀsning eller skrivning i ditt program fÄr du troligtvis det första lediga ID:t, och det kommer att vara nummer 3.
Listan över filbeskrivare kan ses för vilken process som helst om du vet dess PID.
Till exempel, lÄt oss öppna en konsol med bash och kolla PID för vÄr process.
[user@localhost ]$ echo $$
15771
I den andra konsolen kör vi.
[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
Filbeskrivaren med nummer 255 kan du ignorera i denna artikel, den har redan öppnats av bash för sina egna behov, inte av det lÀnkade biblioteket.
Nu Àr alla 3 filbeskrivare kopplade till en pseudoterminal. , men vi kan fortfarande manipulera dem, till exempel genom att köra i den andra konsolen.
[user@localhost ]$ echo "hello world" > /proc/15771/fd/0
Och i den första konsolen ser vi.
[user@localhost ]$ hello world
Redirect och Pipe
Du kan enkelt överskrida dessa 3 filbeskrivare i vilken process som helst, inklusive bash, till exempel via en pipe som kopplar ihop tvÄ processer, lÄt oss se.
[user@localhost ]$ cat /dev/zero | sleep 10000
Du kan sjÀlv köra denna kommando med strace -f och se vad som hÀnder inuti, men jag förklarar kort.
VÄr förÀldraprocess bash med PID 15771 parser vÄr kommandon och förstÄr hur mÄnga kommandon vi vill köra, i vÄrt fall Àr det tvÄ: cat och sleep. Bash vet att den behöver skapa tvÄ barnprocesser och koppla ihop dem med en pipe. Det totala antalet barnprocesser och en pipe kan vara 2.
Innan bash skapar barnprocesserna gör den ett systemanrop. och fÄr nya filbeskrivare till en temporÀr buffer pipe, men denna buffer kopplar Ànnu inte ihop vÄra tvÄ barnprocesser.
För förÀldraprocessen ser det ut som om pipen redan finns, medan barnprocesserna Ànnu inte finns:
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
DÀrefter anvÀnder systemanropet bash skapar tvÄ barnprocesser, och vÄra tre processer kommer att se ut sÄ hÀr:
PID kommando
15771 bash
lrwx------ 1 anvÀndare anvÀndare 64 7 okt 15:42 0 -> /dev/pts/21
lrwx------ 1 anvÀndare anvÀndare 64 7 okt 15:42 1 -> /dev/pts/21
lrwx------ 1 anvÀndare anvÀndare 64 7 okt 15:42 2 -> /dev/pts/21
lrwx------ 1 anvÀndare anvÀndare 64 7 okt 15:42 3 -> pipe:[253543032]
lrwx------ 1 anvÀndare anvÀndare 64 7 okt 15:42 4 -> pipe:[253543032]
lrwx------ 1 anvÀndare anvÀndare 64 7 okt 15:42 255 -> /dev/pts/21
PID kommando
9004 bash
lrwx------ 1 anvÀndare anvÀndare 64 7 okt 15:57 0 -> /dev/pts/21
lrwx------ 1 anvÀndare anvÀndare 64 7 okt 15:57 1 -> /dev/pts/21
lrwx------ 1 anvÀndare anvÀndare 64 7 okt 15:57 2 -> /dev/pts/21
lrwx------ 1 anvÀndare anvÀndare 64 7 okt 15:57 3 -> pipe:[253543032]
lrwx------ 1 anvÀndare anvÀndare 64 7 okt 15:57 4 -> pipe:[253543032]
lrwx------ 1 anvÀndare anvÀndare 64 7 okt 15:57 255 -> /dev/pts/21
PID kommando
9005 bash
lrwx------ 1 anvÀndare anvÀndare 64 7 okt 15:57 0 -> /dev/pts/21
lrwx------ 1 anvÀndare anvÀndare 64 7 okt 15:57 1 -> /dev/pts/21
lrwx------ 1 anvÀndare anvÀndare 64 7 okt 15:57 2 -> /dev/pts/21
lrwx------ 1 anvÀndare anvÀndare 64 7 okt 15:57 3 -> pipe:[253543032]
lrwx------ 1 anvÀndare anvÀndare 64 7 okt 15:57 4 -> pipe:[253543032]
lrwx------ 1 anvÀndare anvÀndare 64 7 okt 15:57 255 -> /dev/pts/21
Vi ska inte glömma att clone klonar processen tillsammans med alla filbeskrivare, sÄ i förÀldraprocessen och de underordnade kommer de att vara samma. Uppgiften för förÀldraprocessen med PID 15771 Àr att övervaka de underordnade processerna, sÄ den vÀntar bara pÄ svar frÄn dem.
DÀrför behöver den inte pipe, och den stÀnger filbeskrivarna med nummer 3 och 4.
I den första underordnade processen bash med PID 9004, med systemanropet , Àndrar vÄr STDOUT filbeskrivare med nummer 1 till filbeskrivaren som pekar pÄ pipe, i vÄrt fall nummer 3. SÄ, allt som den första underordnade processen med PID 9004 skriver till STDOUT kommer automatiskt att hamna i bufferpippen.
I den andra underordnade processen med PID 9005 Àndrar bash med hjÀlp av dup2 filbeskrivaren STDIN med nummer 0. Nu kommer allt som vÄr andra bash med PID 9005 lÀser att lÀsas frÄn pipen.
DÀrefter stÀngs Àven filbeskrivarna med nummer 3 och 4 i de underordnade processerna, eftersom de inte lÀngre anvÀnds.
Filbeskrivaren 255 ignorerar jag avsiktligt, den anvÀnds för bash egna interna behov och kommer Àven att stÀngas i de underordnade processerna.
DÀrefter i den första underordnade processen med PID 9004 startar bash med hjÀlp av systemanropet den exekverbara filen som vi angav i kommandoraden, i vÄrt fall Àr det /usr/bin/cat.
I den andra underordnade processen med PID 9005 startar bash den andra exekverbara filen som vi angav, i vÄrt fall Àr det /usr/bin/sleep.
Systemanropet exec stÀnger inte filhandtag om de inte har öppnats med flaggan O_CLOEXEC under anropet open. I vÄrt fall kommer alla aktuella filhandtag att bevaras efter att de körbara filerna har startats.
Vi kontrollerar 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 ni kan se sÄ Àr det unika numret pÄ vÄr pipe samma i bÄda processerna. DÀrmed har vi en koppling mellan tvÄ olika processer med samma förÀlder.
För dem som inte Àr bekanta med systemanropen som bash anvÀnder, rekommenderar jag starkt att köra kommandon genom strace för att se vad som hÀnder inuti, till exempel sÄ hÀr:
strace -s 1024 -f bash -c "ls | grep hello"
LÄt oss ÄtergÄ till vÄrt problem med brist pÄ diskutrymme och försök att spara data utan att starta om processen. Vi kommer att skriva ett litet program som skriver till disk ungefÀr 1 megabyte per sekund. Om vi av nÄgon anledning inte kan skriva data till disken kommer vi helt enkelt att ignorera det och försöka skriva data igen om en sekund. I exemplet anvÀnder jag Python, men du kan 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
Vi kör programmet och ser pÄ filhandtagen.
[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 ser har vi vÄra 3 standard filbeskrivare och ytterligare en som vi har ö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
Data skrivs, vi försöker Àndra rÀttigheterna 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 rÀtt att skriva till filen. LÄt oss försöka radera den:
[user@localhost ]$ sudo rm 123.txt
[user@localhost ]$ ls 123.txt
ls: cannot access 123.txt: No such file or directory
Vart skrivs datan? Och skrivs det alls? LÄt oss kontrollera:
[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 filbeskrivare existerar fortfarande, och vi kan arbeta med denna filbeskrivare som med vÄr gamla fil, vi kan lÀsa den, rensa den 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. Vi försöker 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 vi ser ökar filstorleken bara och vÄr truncate fungerade inte. LÄt oss titta i dokumentationen för systemanropet. Om vi anvÀnder flaggan O_APPEND nÀr vi öppnar filen, kommer operativsystemet att kontrollera filstorleken vid varje skrivning och skriva data i slutet av filen, och detta görs 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 truncate endast om vi öppnar filen för tillÀgg, vilket betyder att vi i vÄr kod istÀllet för
with open("123.txt", "w") as f:
bör vi ha
with open("123.txt", "a") as f:
LÄt oss kontrollera 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 flaggan "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
Programmerar redan körda processer
Ofta anvÀnder programmerare debugger (t.ex. GDB) eller olika nivÄer av loggning i applikationen vid skapande och testning av program. Linux ger möjligheten att faktiskt skriva och Àndra redan körda program, till exempel Àndra vÀrden pÄ variabler, sÀtta breakpoints etc.
à tergÄr till den ursprungliga frÄgan om bristen pÄ utrymme pÄ disken för att skriva filen, 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 ~]$
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
Skapar en katalog med vÄr Àgare:
[user@localhost ~]$ sudo mkdir /mnt/logs
[user@localhost ~]$ sudo chown user: /mnt/logs
Ăppnar filen endast för skrivning i vĂ„rt program:
with open("/mnt/logs/123.txt", "w") as f:
Starta
[user@localhost ]$ python openforwrite.py
VÀnta nÄgra sekunder
[user@localhost ~]$ df -h | grep mnt
/dev/loop0 8.7M 8.0M 0 100% /mnt
SÄ vi fick problemet som beskrevs i början av den hÀr artikeln. Fritt utrymme 0, upptaget 100%.
Vi kommer ihÄg att vi enligt problemet försöker skriva mycket viktigt data som inte fÄr gÄ förlorad. Och samtidigt behöver vi reparera tjÀnsten utan att starta om processen.
Anta att vi faktiskt har utrymme pÄ disken, men pÄ en annan partition, till exempel i /home.
LÄt oss försöka "omprogrammera programmet pÄ sprÄng".
Kollar PID för vÄr process som tagit all plats pÄ disken:
[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)
Kollar öppna filbeskrivare:
(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
Kollar information om filbeskrivaren med nummer 3, som intresserar oss
(gdb) shell cat /proc/10078/fdinfo/3
pos: 8189952
flags: 0100001
mnt_id: 482
Med tanke pÄ vilken syscall Python gör (se ovan dÀr vi körde strace och hittar open-anropet), nÀr vi hanterar vÄr kod för att öppna en fil gör vi detsamma sjÀlva Ä vÄr process vÀgnar, men vi behöver ersÀtta O_WRONLY|O_CREAT|O_TRUNC-bitarna med ett numeriskt vÀrde. För detta öppnar vi kÀrnans kÀllkod, till exempel och ser vilka flaggor som ansvarar för vad
#define O_WRONLY 00000001
#define O_CREAT 00000100
#define O_TRUNC 00001000
Vi sammanfogar alla vÀrden till ett, vi fÄr 00001101
Vi kör vÄr anrop frÄn gdb
(gdb) call open("/home/user/123.txt", 00001101,0666)
$1 = 4
SÄ vi fick en ny filbeskrivare 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 filbeskrivare, och vi har redan lĂ€rt oss systemanropet dup2.
Vi försöker byta ut en filbeskrivare mot en annan
(gdb) call dup2(4,3)
$2 = 3
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 filbeskrivaren 4 eftersom vi inte behöver den:
(gdb) call close (4)
$1 = 0
Och vi lÀmnar 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 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 vi ser skriver data till den nya filen, vi kontrollerar 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 har gÄtt förlorad, applikationen fungerar, loggar skrivs till den nya platsen.
LÄt oss komplicera uppgiften nÄgot
TÀnk dig att data Àr viktiga för oss, men vi har inget utrymme pÄ disken i nÄgon av partitionerna och vi kan inte koppla in en disk.
Vad vi kan göra Àr att omdirigera vÄra data nÄgonstans, till exempel till en pipe, och data frÄn pipen kan vi omdirigera till nÀtverket via nÄgot program, som netcat.
Vi kan skapa en namngiven pipe med kommandot mkfifo. Det skapar en pseudofil i filsystemet, Àven om det inte finns nÄgot ledigt utrymme.
Vi Äterstartar applikationen och kontrollerar:
[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 ingen ledig diskutrymme, men vi lyckas skapa 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 behöver vi pÄ nÄgot sÀtt omdirigera all data som gÄr in i denna pipe till en annan server över nÀtverket, för detta passar netcat bra.
PĂ„ servern remote-server.example.com startar vi
[user@localhost ~]$ nc -l 7777 > 123.txt
PÄ vÄr problemserver startar vi i en separat terminal
[user@localhost ~]$ nc remote-server.example.com 7777 < /mnt/logs/megapipe
Nu kommer all data som gÄr in i pipen automatiskt att skickas till stdin i netcat, som skickar dem till nÀtverket pÄ port 7777.
Det enda vi har kvar att göra Àr att börja skriva vÄr data till denna namngivna pipe.
Vi har redan en igÄngsatt 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
Av alla flaggor behöver vi endast 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
Vi kontrollerar den fjÀrrstyrda servern remote-server.example.com
[user@localhost ~]$ ls -lah 123.txt
-rw-rw-r-- 1 user user 38M Oct 8 14:21 123.txt
Data överförs, vi kontrollerar den problematiska servern
[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 har sparats, problemet Àr löst.
Jag passar pÄ att skicka en hÀlsning till kollegorna pÄ Degiro.
Lyssna pÄ Radio-T:s podcasts.
Ănskar alla gott.
Som en hemlÀxa föreslÄr jag att vi funderar pÄ vad som kommer att stÄ i filbeskrivarna för processerna cat och sleep om vi kör följande kommando:
[user@localhost ~]$ cat /dev/zero 2>/dev/null| sleep 10000
KĂ€lla: habr.com
